我想在用户离开我的angular 2应用程序的特定页面之前警告用户未保存的更改。通常我会使用window.onbeforeunload
,但这对单页应用程序不起作用。
我发现在角度1中,您可以加入$locationChangeStart
事件,为用户抛出一个confirm
框,但我还没有看到任何显示的内容如何使角度2工作,或者如果该事件仍然存在。我也看到plugins为ag1提供onbeforeunload
的功能,但同样,我还没有看到任何方法将它用于ag2。
我希望其他人找到解决这个问题的方法;任何一种方法都可以用于我的目的。
答案 0 :(得分:162)
为了防止浏览器刷新,关闭窗口等(请参阅@ChristopheVidal对Günter答案的评论以获取有关此问题的详细信息),我发现将@HostListener
装饰器添加到您的班级{ {1}}实施以监听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] },
];
注意:正如@JasperRisseeuw所指出的那样,IE和Edge处理import { PendingChangesGuard } from './pending-changes.guard';
import { NgModule } from '@angular/core';
@NgModule({
// ...
providers: [PendingChangesGuard],
// ...
})
export class AppModule {}
事件的方式与其他浏览器不同,并且在{时{1}}中包含单词beforeunload
{1}}事件激活(例如,浏览器刷新,关闭窗口等)。在Angular应用程序中导航不受影响,并将正确显示您指定的确认警告消息。那些需要支持IE / Edge且不希望false
在beforeunload
事件激活时在确认对话框中显示/想要更详细消息的人也可能希望看到@JasperRisseeuw的解决方法
答案 1 :(得分:61)
路由器提供生命周期回调CanDeactivate
有关详细信息,请参阅guards tutorial
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.x路由器)
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] }]) );
答案 2 :(得分:48)
来自stewdebaker的@Hostlistener的示例工作得很好,但我对它做了一个更改,因为IE和Edge显示了MyComponent类上的canDeactivate()方法返回给最终用户的“false”。
<强>组件:强>
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)";
}
}
}
答案 3 :(得分:8)
2020年6月的答案:
请注意,到目前为止,所有提出的解决方案都无法解决Angular的canDeactivate
卫队的重大已知缺陷:
请查看我对问题demonstrated here的解决方案,该解决方案可以安全解决此问题*。已在Chrome,Firefox和Edge上进行了测试。
* 重要注意事项:在此阶段,当单击后退按钮时,以上内容将清除前进的历史记录,但保留前进的历史记录。如果保留您的转发历史至关重要,则此解决方案将不合适。就我而言,通常在表单上使用master-detail路由策略,因此保持转发历史记录并不重要。
答案 4 :(得分:2)
我已经实现了@stewdebaker的解决方案,该解决方案效果非常好,但是我想要一个不错的bootstrap弹出窗口而不是笨重的标准JavaScript确认。假设你已经在使用ngx-bootstrap,你可以使用@ stwedebaker的解决方案,但是交换“守卫”#39;对于我在这里展示的人。您还需要介绍ngx-bootstrap/modal
,然后添加新的ConfirmationComponent
:
(替换&#39;确认&#39;使用将打开引导模式的功能 - 显示新的自定义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;
})
}
}
<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
将在html模板中不使用selector
而显示,因此需要在根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]
答案 5 :(得分:0)
解决方案比预期的要容易,不要使用href
,因为Angular Routing使用routerLink
指令不会处理此问题。