Предупреждать пользователя о несохраненных изменениях перед уходом со страницы



Я хотел бы предупредить пользователей о несохраненных изменениях, прежде чем они покинут определенную страницу моего приложения angular 2. Обычно я бы использовал window.onbeforeunload, но это не работает для одностраничных приложений.



я обнаружил, что в угловой 1, Вы можете подключить в $locationChangeStart событие, чтобы бросить вверх confirm box для пользователя, но я не видел ничего, что показывает, как заставить это работать для angular 2, или если это событие все еще присутствует. Я тоже видел Плагины для ag1 это обеспечить функциональность для onbeforeunload, но опять же, я не видел никакого способа использовать его для ag2.



Я надеюсь, что кто-то нашел решение этой проблемы; либо метод будет работать для моих целей.

697   5  

5 ответов:

маршрутизатор обеспечивает обратный вызов жизненного цикла CanDeactivate

для более подробной информации см. охранники уроке

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 {}

оригинал (RC.х маршрутизатор)

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]
  }])
);

чтобы также покрыть защиту от обновления браузера, закрытия окна и т. д. (см. комментарий @ChristopheVidal к ответу Гюнтера для получения подробной информации по этому вопросу), я нашел полезным добавить @HostListener декоратор для вашего класса canDeactivate реализация для прослушивания beforeunloadwindow событие. При правильной настройке это будет защищать как от встроенной, так и от внешней навигации одновременно.

для пример:

компоненты:

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 {}

Примечание: как указал @JasperRisseeuw, IE и Edge обрабатывают beforeunload событие отличается от других браузеров и будет включать в себя слово false в диалоговом окне подтверждения, когда beforeunload активация события (например, обновление браузера, закрыв окно и т. д.). Перемещение в пределах углового приложения не влияет и будет правильно отображать назначенное сообщение с предупреждением о подтверждении. Те, кому нужно поддерживать IE / Edge и не хотят false чтобы показать / хотите более подробное сообщение в диалоговом окне подтверждения, когда beforeunload активация события также может потребоваться увидеть ответ @JasperRisseeuw для обхода.

пример с @Hostlistener из stewdebaker работает очень хорошо, но я сделал еще одно изменение, потому что IE и Edge отображают "false", который возвращается методом canDeactivate() в классе MyComponent конечному пользователю.

компоненты:

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)";
    }
  }
}

я реализовал решение от @stewdebaker, которое работает очень хорошо, однако я хотел хорошее всплывающее окно начальной загрузки вместо неуклюжего стандартного подтверждения JavaScript. Предполагая, что вы уже используете NGX-bootstrap, вы можете использовать решение @stwedebaker, но замените "Guard" на тот, который я показываю здесь. Вам также нужно ввести ngx-bootstrap/modal и добавить новый ConfirmationComponent:

гвардии

(замените 'confirm' функцией, которая откроет модальный загрузчик-отображение a новые, изготовленные на заказ 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;
    })
  }
}

подтверждение.деталь.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>

подтверждение.деталь.ТС

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 в шаблоне html, он должен быть объявлен в entryComponents в корневом 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]

решение было проще, чем ожидалось, не используйте href потому что это не обрабатывается с помощью угловой маршрутизации routerLink вместо директивы.

Comments

    Ничего не найдено.