পৃষ্ঠাটি ছাড়ার আগে ব্যবহারকারীদের সংরক্ষণে না যাওয়া পরিবর্তনের বিষয়ে সতর্ক করুন


112

আমি ব্যবহারকারীদের আমার কৌণিক 2 অ্যাপ্লিকেশনের একটি নির্দিষ্ট পৃষ্ঠা ছাড়ার আগে সংরক্ষিত পরিবর্তনগুলি সম্পর্কে সতর্ক করতে চাই। সাধারণত আমি ব্যবহার করব window.onbeforeunload, তবে এটি একক পৃষ্ঠাগুলির জন্য কাজ করে না।

আমি দেখতে পেয়েছি যে কৌণিক 1 এ, আপনি ব্যবহারকারীর জন্য $locationChangeStartএকটি confirmবাক্স নিক্ষেপ করার জন্য ইভেন্টটিতে ঝুঁকতে পারেন , তবে আমি এমন কোনও কিছুই দেখিনি যা এই কৌনিক 2 এর জন্য কীভাবে কাজ করবে তা দেখায়, বা যদি ইভেন্টটি এখনও উপস্থিত থাকে তবে । আমিও দেখেছি প্লাগিন ag1 যে জন্য কার্যকারিতা প্রদান জন্য onbeforeunload, কিন্তু আবার, আমি কোন ভাবেই AG2 জন্য এটি ব্যবহার করতে দেখা যায় না।

আমি আশা করছি যে অন্য কেউ এই সমস্যার সমাধান পেয়েছেন; উভয় পদ্ধতিই আমার উদ্দেশ্যগুলির জন্য সূক্ষ্মভাবে কাজ করবে।


2
আপনি পৃষ্ঠা / ট্যাবটি বন্ধ করার চেষ্টা করার সময় এটি একক পৃষ্ঠার অ্যাপ্লিকেশনগুলির জন্য কাজ করে। সুতরাং প্রশ্নের কোনও উত্তর কেবলমাত্র একটি আংশিক সমাধান হবে যদি তারা সেই সত্যটিকে উপেক্ষা করে।
ইএসডিএক্স 9 আরভিজে 0

উত্তর:


74

রাউটার একটি লাইফসাইকেল কলব্যাক ক্যানডিয়েটিভেট সরবরাহ করে

আরও তথ্যের জন্য গার্ড টিউটোরিয়াল দেখুন

class UserToken {}
class Permissions {
  canActivate(user: UserToken, id: string): boolean {
    return true;
  }
}
@Injectable()
class CanActivateTeam implements CanActivate {
  constructor(private permissions: Permissions, private currentUser: UserToken) {}
  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean>|Promise<boolean>|boolean {
    return this.permissions.canActivate(this.currentUser, route.params.id);
  }
}
@NgModule({
  imports: [
    RouterModule.forRoot([
      {
        path: 'team/:id',
        component: TeamCmp,
        canActivate: [CanActivateTeam]
      }
    ])
  ],
  providers: [CanActivateTeam, UserToken, Permissions]
})
class AppModule {}

আসল (আরসি.এক্স রাউটার)

class CanActivateTeam implements CanActivate {
  constructor(private permissions: Permissions, private currentUser: UserToken) {}
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<boolean> {
    return this.permissions.canActivate(this.currentUser, this.route.params.id);
  }
}
bootstrap(AppComponent, [
  CanActivateTeam,
  provideRouter([{
    path: 'team/:id',
    component: Team,
    canActivate: [CanActivateTeam]
  }])
);

28
ওপি যা যা চেয়েছিল তার বিপরীতে, ক্যানডিএক্টিভেট হ'ল- ক্রমবর্ধমান-অনবার্যবর্ধমান ইভেন্টের প্রতি আকস্মিকভাবে নয় (দুর্ভাগ্যক্রমে)। এর অর্থ হ'ল ব্যবহারকারী যদি কোনও বাহ্যিক ইউআরএল নেভিগেট করার চেষ্টা করে, উইন্ডোটি বন্ধ করুন ইত্যাদি ক্যানডিএক্টিভেট ট্রিগার করা হবে না। ব্যবহারকারী কেবল অ্যাপের মধ্যেই থাকবেন বলে মনে হয় এটি কাজ করে।
ক্রিস্টোফ ভিদাল

1
@ ক্রিসটফেভিডাল সঠিক। দয়া করে একটি সমাধান এছাড়াও একটি বহিস্থিত URL- এ নেভিগেট করতে, উইন্ডো বন্ধ জুড়ে পৃষ্ঠা ইত্যাদি পুনরায় লোড জন্য আমার উত্তর দেখার
stewdebaker

রুট পরিবর্তন করার সময় এটি কাজ করে। যদি এটি এসপিএ হয়? এটি অর্জনের অন্য কোনও উপায়?
সুজয় কোডমালা

stackoverflow.com/questions/36763141/… আপনার রুটগুলির পাশাপাশি এটিও দরকার। যদি উইন্ডোটি বন্ধ থাকে বা বর্তমান সাইট থেকে দূরে নেভিগেট করা canDeactivateকাজ করবে না।
গন্টার জ্যাচবাউয়ার

214

ব্রাউজার রিফ্রেশ, উইন্ডো বন্ধ ইত্যাদি ইত্যাদির বিরুদ্ধেও প্রহরীদের coverাকতে (ইস্যুটির বিশদগুলির জন্য @ ক্রিস্টোফেভিডালের মন্তব্য গন্টারের উত্তরে দেখুন), ইভেন্টটি শোনার জন্য @HostListenerআপনার শ্রেণীর canDeactivateপ্রয়োগের সাথে ডেকরেটার যুক্ত করা আমার পক্ষে সহায়ক হয়েছে beforeunload window। সঠিকভাবে কনফিগার করা হলে, এটি একই সময়ে অ্যাপ্লিকেশন এবং বাহ্যিক নেভিগেশন উভয়ের বিরুদ্ধে রক্ষা করবে।

উদাহরণ স্বরূপ:

উপাদান:

import { ComponentCanDeactivate } from './pending-changes.guard';
import { HostListener } from '@angular/core';
import { Observable } from 'rxjs/Observable';

export class MyComponent implements ComponentCanDeactivate {
  // @HostListener allows us to also guard against browser refresh, close, etc.
  @HostListener('window:beforeunload')
  canDeactivate(): Observable<boolean> | boolean {
    // insert logic to check if there are pending changes here;
    // returning true will navigate without confirmation
    // returning false will show a confirm dialog before navigating away
  }
}

গার্ড:

import { CanDeactivate } from '@angular/router';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';

export interface ComponentCanDeactivate {
  canDeactivate: () => boolean | Observable<boolean>;
}

@Injectable()
export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {
  canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> {
    // if there are no pending changes, just allow deactivation; else confirm first
    return component.canDeactivate() ?
      true :
      // NOTE: this warning message will only be shown when navigating elsewhere within your angular app;
      // when navigating away from your angular app, the browser will show a generic warning message
      // see http://stackoverflow.com/a/42207299/7307355
      confirm('WARNING: You have unsaved changes. Press Cancel to go back and save these changes, or OK to lose these changes.');
  }
}

রুট:

import { PendingChangesGuard } from './pending-changes.guard';
import { MyComponent } from './my.component';
import { Routes } from '@angular/router';

export const MY_ROUTES: Routes = [
  { path: '', component: MyComponent, canDeactivate: [PendingChangesGuard] },
];

মডিউল:

import { PendingChangesGuard } from './pending-changes.guard';
import { NgModule } from '@angular/core';

@NgModule({
  // ...
  providers: [PendingChangesGuard],
  // ...
})
export class AppModule {}

দ্রষ্টব্য : @ য্যাস্পার রিসিউ যেমন উল্লেখ করেছেন, আই আই এবং এজ beforeunloadইভেন্টটি অন্য ব্রাউজারগুলির থেকে পৃথকভাবে পরিচালনা করে এবং ইভেন্টটি সক্রিয় falseহওয়ার সময় নিশ্চিত ডায়ালগটিতে শব্দটি অন্তর্ভুক্ত করে beforeunload(যেমন ব্রাউজার রিফ্রেশ, উইন্ডোটি বন্ধ করা ইত্যাদি)। কৌণিক অ্যাপের মধ্যে নেভিগেট করা প্রভাবিত নয় এবং সঠিকভাবে আপনার মনোনীত নিশ্চিতকরণ সতর্কতা বার্তাটি প্রদর্শন করবে। যাঁরা আইই / এজকে সমর্থন করতে চান falseএবং beforeunloadইভেন্টটি সক্রিয় হওয়ার সময় নিশ্চিত ডায়ালগে আরও বিস্তারিত বার্তা প্রদর্শন করতে / দেখতে চান না তারা যখন @ জ্যাসপারআরসিউইউ'র উত্তরটিও কার্যবিধির জন্য দেখতে চাইতে পারেন।


2
এটি সত্যিই ভাল কাজ করে! আমার এই সমাধানটিতে একটি সংযোজন রয়েছে, আমার উত্তর নীচে দেখুন।
জ্যাস্পার রিসিউউ

1
'rxjs / পর্যবেক্ষণযোগ্য' থেকে আমদানি করুন serv পর্যবেক্ষণযোগ্য; কম্পোনেন্টক্যানডিয়েটিভেট থেকে নিখোঁজ রয়েছে
মাহমুদ আলী কাসেম

1
আমাকে @Injectable()পেন্ডিং চ্যাঞ্জসগার্ড ক্লাসে যুক্ত করতে হয়েছিল। এছাড়াও, আমি আমার প্রদানকারীর PendingChangesGuard যোগ করার জন্য ছিল@NgModule
spottedmahn

আমাকে যোগ করতে হয়েছিলimport { HostListener } from '@angular/core';
13

4
এটি লক্ষ্য করার মতো বিষয় যে আপনি যদি নেভিগেট হয়ে যান তবে আপনাকে অবশ্যই একটি বুলিয়ান প্রদান করতে হবে beforeunload। আপনি যদি কোনও পর্যবেক্ষণযোগ্য ফিরিয়ে দেন তবে এটি কার্যকর হবে না। আপনি ভালো কিছু জন্য আপনার ইন্টারফেস পরিবর্তন করতে পারেন canDeactivate: (internalNavigation: true | undefined)এই মত এবং আপনার উপাদান আহবান return component.canDeactivate(true)। এইভাবে, আপনি falseপর্যবেক্ষণের পরিবর্তে ফিরে যাওয়ার জন্য অভ্যন্তরীণভাবে নেভিগেট করছেন না কিনা তা আপনি পরীক্ষা করতে পারেন ।
jsgoupil

58

স্টিওডেবেকারের @ হোস্টলিস্টারের উদাহরণটি সত্যিই ভালভাবে কাজ করে তবে আমি এতে আরও একটি পরিবর্তন করেছি কারণ আইই এবং এজটি "মিথ্যা" প্রদর্শন করে যা মাইকম্পোন্টেন্ট শ্রেণিতে ক্যানডিয়েটিভেট () পদ্ধতি দ্বারা শেষ ব্যবহারকারীর কাছে ফিরে আসে।

উপাদান:

import {ComponentCanDeactivate} from "./pending-changes.guard";
import { Observable } from 'rxjs'; // add this line

export class MyComponent implements ComponentCanDeactivate {

  canDeactivate(): Observable<boolean> | boolean {
    // insert logic to check if there are pending changes here;
    // returning true will navigate without confirmation
    // returning false will show a confirm alert before navigating away
  }

  // @HostListener allows us to also guard against browser refresh, close, etc.
  @HostListener('window:beforeunload', ['$event'])
  unloadNotification($event: any) {
    if (!this.canDeactivate()) {
        $event.returnValue = "This message is displayed to the user in IE and Edge when they navigate without using Angular routing (type another URL/close the browser/etc)";
    }
  }
}

2
ভালো লেগেছে @ জ্যাস্পার রিসিজিউ! আমি বুঝতে পারি নি যে আইই / এজ এটিকে অন্যভাবে পরিচালনা করেছে। এটি যাদের IE / এজ সমর্থন করা প্রয়োজন এবং falseকনফার্ম ডায়ালগটিতে দেখাতে চান না তাদের জন্য এটি একটি খুব কার্যকর সমাধান । আমি তোমার উত্তরের একটি ছোট সম্পাদনা অন্তর্ভুক্ত করা '$event'মধ্যে @HostListener, টীকা থেকে, যাতে প্রয়োজন বোধ করা হয় মধ্যে এটি অ্যাক্সেস পাবে unloadNotificationফাংশন।
স্টিওডেবেকার

1
ধন্যবাদ, আমি নিজের কোড থেকে ", ['$ ইভেন্ট']" অনুলিপি করতে ভুলে গিয়েছিলাম আপনার কাছ থেকেও এত ভাল ক্যাচ!
জ্যাস্পার রিসিউউ

কাজগুলির একমাত্র সমাধানটি ছিল এটি (এজ ব্যবহার করে)। অন্যরা সমস্ত কাজ করে তবে কেবলমাত্র আমার পাঠ্য নয়, কেবল ডিফল্ট ডায়লগ বার্তা (ক্রোম / ফায়ারফক্স) দেখায় ... আমি কী ঘটছে তা বুঝতে একটি প্রশ্ন জিজ্ঞাসাও করেছি
এলমার ড্যান্টাস

@ এলমারড্যান্টাস ক্রোম / ফায়ারফক্সে কেন ডিফল্ট ডায়লগ বার্তাটি দেখায় তার ব্যাখ্যার জন্য আপনার প্রশ্নের উত্তরটি দেখুন ।
স্টুডেবেকার

2
আসলে, এটি কাজ করে, দুঃখিত! আমাকে মডিউল সরবরাহকারীদের মধ্যে প্রহরীটি উল্লেখ করতে হয়েছিল।
tudor.illecu

6

আমি @ স্টেভেডবেকারের কাছ থেকে সমাধানটি কার্যকর করেছি যা সত্যই ভাল কাজ করে, তবে আমি ক্লানকি স্ট্যান্ডার্ড জাভাস্ক্রিপ্ট কনফার্মির পরিবর্তে একটি দুর্দান্ত বুটস্ট্র্যাপ পপআপ চাইছিলাম। ধরে নিই যে আপনি ইতিমধ্যে এনজিএক্স-বুটস্ট্র্যাপ ব্যবহার করছেন, আপনি @ স্টেভেডবেকারের সলিউশনটি ব্যবহার করতে পারেন, তবে আমি এখানে যা দেখছি তার জন্য 'গার্ড' অদলবদল করুন। আপনাকে পরিচয় করিয়ে দিতে হবে ngx-bootstrap/modalএবং একটি নতুন যুক্ত করতে হবে ConfirmationComponent:

পাহারা

(একটি ফাংশন যা 'বুটস্ট্র্যাপ মডেল খুলবে - একটি নতুন, কাস্টম প্রদর্শন করে) এর সাথে' নিশ্চিত করুন 'প্রতিস্থাপন করুন ConfirmationComponent:

import { Component, OnInit } from '@angular/core';
import { ConfirmationComponent } from './confirmation.component';

import { CanDeactivate } from '@angular/router';
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs/Observable';
import { BsModalService } from 'ngx-bootstrap/modal';
import { BsModalRef } from 'ngx-bootstrap/modal';

export interface ComponentCanDeactivate {
  canDeactivate: () => boolean | Observable<boolean>;
}

@Injectable()
export class PendingChangesGuard implements CanDeactivate<ComponentCanDeactivate> {

  modalRef: BsModalRef;

  constructor(private modalService: BsModalService) {};

  canDeactivate(component: ComponentCanDeactivate): boolean | Observable<boolean> {
    // if there are no pending changes, just allow deactivation; else confirm first
    return component.canDeactivate() ?
      true :
      // NOTE: this warning message will only be shown when navigating elsewhere within your angular app;
      // when navigating away from your angular app, the browser will show a generic warning message
      // see http://stackoverflow.com/a/42207299/7307355
      this.openConfirmDialog();
  }

  openConfirmDialog() {
    this.modalRef = this.modalService.show(ConfirmationComponent);
    return this.modalRef.content.onClose.map(result => {
        return result;
    })
  }
}

confirmation.component.html

<div class="alert-box">
    <div class="modal-header">
        <h4 class="modal-title">Unsaved changes</h4>
    </div>
    <div class="modal-body">
        Navigate away and lose them?
    </div>
    <div class="modal-footer">
        <button type="button" class="btn btn-secondary" (click)="onConfirm()">Yes</button>
        <button type="button" class="btn btn-secondary" (click)="onCancel()">No</button>        
    </div>
</div>

confirmation.component.ts

import { Component } from '@angular/core';
import { Subject } from 'rxjs/Subject';
import { BsModalRef } from 'ngx-bootstrap/modal';

@Component({
    templateUrl: './confirmation.component.html'
})
export class ConfirmationComponent {

    public onClose: Subject<boolean>;

    constructor(private _bsModalRef: BsModalRef) {

    }

    public ngOnInit(): void {
        this.onClose = new Subject();
    }

    public onConfirm(): void {
        this.onClose.next(true);
        this._bsModalRef.hide();
    }

    public onCancel(): void {
        this.onClose.next(false);
        this._bsModalRef.hide();
    }
}

এবং যেহেতু নতুনটি কোনও এইচটিএমএল টেম্পলেটটিতে ConfirmationComponentব্যবহার না করে প্রদর্শিত হবে selector, তাই এটি entryComponentsআপনার মূলের মধ্যে ঘোষণা করা দরকার app.module.ts(বা আপনি নিজের রুট মডিউলটির নাম দিন)। নিম্নলিখিত পরিবর্তনগুলি করুন app.module.ts:

app.module.ts

import { ModalModule } from 'ngx-bootstrap/modal';
import { ConfirmationComponent } from './confirmation.component';

@NgModule({
  declarations: [
     ...
     ConfirmationComponent
  ],
  imports: [
     ...
     ModalModule.forRoot()
  ],
  entryComponents: [ConfirmationComponent]

1
ব্রাউজার রিফ্রেশ জন্য কাস্টম মডেল দেখানোর কোন সুযোগ আছে?
k11k2

অবশ্যই একটি উপায় থাকতে হবে, যদিও এই সমাধানটি আমার প্রয়োজনের জন্য ঠিক ছিল। আমি সময় পেলে আমি আরও কিছু বিকাশ করব, যদিও আমি এই উত্তরটি কিছুক্ষণের জন্য আপডেট করতে সক্ষম হব না!
ক্রিস হালাক্র

1

সমাধানটি প্রত্যাশার চেয়ে সহজ ছিল, ব্যবহার করবেন না hrefকারণ এটি routerLinkপরিবর্তে কৌণিক রাউটিং ব্যবহারের নির্দেশিকা দ্বারা পরিচালিত হয় না।


1

জুন 2020 উত্তর:

মনে রাখবেন যে এই সমাধান পর্যন্ত প্রস্তাবিত সমস্ত সমাধান অ্যাঙ্গুলার canDeactivateগার্ডদের সাথে একটি গুরুত্বপূর্ণ জ্ঞাত ত্রুটি মোকাবেলা করে না :

  1. ব্যবহারকারী ব্রাউজারে 'পিছনে' বোতামটি ক্লিক করে, ডায়ালগ প্রদর্শন এবং ব্যবহারকারী ক্লিকগুলি ক্যানসেল করে
  2. ব্যবহারকারী আবার 'পিছনে' বোতামটি ক্লিক করে, সংলাপ প্রদর্শন করে এবং ব্যবহারকারীরা ক্লিক করে কনফার্ম
  3. দ্রষ্টব্য: ব্যবহারকারী 2 বার ফিরে নেভিগেট করা হয়েছে, এটি এমনকি তাদের পুরোপুরি অ্যাপ থেকে বের করে নিতে পারে :(

এটি এখানে , এখানে এবং দৈর্ঘ্যে এখানে আলোচনা করা হয়েছে


দয়া করে এখানে প্রদর্শিত সমস্যার আমার সমাধান দেখুন যা নিরাপদে এই সমস্যাটিকে ঘিরে কাজ করে *। এটি ক্রোম, ফায়ারফক্স এবং এজতে পরীক্ষা করা হয়েছে।


* গুরুত্বপূর্ণ কভিয়েট : এই পর্যায়ে, উপরের অংশগুলি পূর্বের বোতামটি ক্লিক করার পরে ফরোয়ার্ডের ইতিহাস সাফ করবে, তবে পিছনের ইতিহাসটি সংরক্ষণ করবে। আপনার ফরোয়ার্ড ইতিহাস সংরক্ষণ করা অত্যাবশ্যক হলে এই সমাধানটি উপযুক্ত হবে না। আমার ক্ষেত্রে, আমি সাধারণত একটি মাস্টার-ডিটেইল রাউটিং কৌশল ব্যবহার করি যখন এটি ফর্মগুলি আসে, সুতরাং এগিয়ে থাকা ইতিহাস বজায় রাখা গুরুত্বপূর্ণ নয়।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.