사용자가 내 Angular 2 앱의 특정 페이지를 떠나기 전에 저장되지 않은 변경 사항에 대해 경고하고 싶습니다. 일반적으로을 사용 window.onbeforeunload
하지만 단일 페이지 응용 프로그램에서는 작동하지 않습니다.
앵귤러 1에서 $locationChangeStart
이벤트에 연결 confirm
하여 사용자를 위해 상자 를 던질 수 있지만 앵귤러 2에서이 작업을 수행하는 방법 또는 해당 이벤트가 여전히 존재하는지 보여주는 내용을 본 적이 없습니다. . 에 대한 기능을 제공하는 ag1 용 플러그인 도 보았지만 onbeforeunload
다시 한 번, ag2에 대해 사용하는 방법을 보지 못했습니다.
다른 사람이이 문제에 대한 해결책을 찾았기를 바랍니다. 두 가지 방법 모두 내 목적에 적합합니다.
답변
라우터는 수명주기 콜백 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.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] }]) );
답변
또한 브라우저 새로 고침, 창 닫기 등을 방지하기 위해 (문제에 대한 자세한 내용은 Günter의 답변에 대한 @ChristopheVidal의 의견 참조) 이벤트 를 수신하기 위해 @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 {}
참고 : @JasperRisseeuw가 지적했듯이 IE와 Edge beforeunload
는 다른 브라우저와 다르게 이벤트를 처리하며 이벤트가 활성화 false
될 때 확인 대화 상자에 단어 를 포함합니다 beforeunload
(예 : 브라우저 새로 고침, 창 닫기 등). Angular 앱 내에서 탐색하는 것은 영향을받지 않으며 지정된 확인 경고 메시지가 올바르게 표시됩니다. IE / Edge를 지원해야 false
하고 beforeunload
이벤트가 활성화 될 때 확인 대화 상자에 더 자세한 메시지를 표시하거나 원하지 않는 사람들 은 해결 방법에 대한 @JasperRisseeuw의 답변을 볼 수도 있습니다.
답변
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)";
}
}
}
답변
정말 잘 작동하는 @stewdebaker의 솔루션을 구현했지만, 어설픈 표준 JavaScript 확인 대신 멋진 부트 스트랩 팝업을 원했습니다. 이미 ngx-bootstrap을 사용하고 있다고 가정하면 @stwedebaker의 솔루션을 사용할 수 있지만 여기에 표시되는 ‘가드’를 교체하십시오. 또한을 소개 ngx-bootstrap/modal
하고 다음을 새로 추가 해야합니다 ConfirmationComponent
.
가드
( ‘confirm’을 부트 스트랩 모달을 여는 함수로 바꾸십시오-새로운 custom 표시 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;
})
}
}
confirm.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>
confirm.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();
}
}
그리고 새로운 것은 html 템플릿에서 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]
답변
솔루션은 예상보다 쉬웠으며 대신 href
Angular Routing 사용 routerLink
지시문에 의해 처리 되지 않으므로 사용하지 마십시오 .
답변
2020 년 6 월 답변 :
이 시점까지 제안 된 모든 솔루션은 Angular의 canDeactivate
가드에 대해 알려진 중대한 결함을 처리하지 않습니다 .
- 사용자가 브라우저에서 ‘뒤로’버튼을 클릭하면 대화 상자가 표시되고 사용자가 취소를 클릭 합니다.
- 사용자가 ‘뒤로’버튼을 다시 클릭하고 대화 상자가 표시되고 사용자가 CONFIRM을 클릭 합니다.
- 참고 : 사용자가 2 번 뒤로 이동하면 앱에서 모두 제거 할 수도 있습니다.
이것은 여기 , 여기 , 그리고 여기 에서 길게 논의되었습니다.
이 문제를 안전하게 해결 하는 여기에 설명 된 문제에 대한 내 솔루션을 참조하십시오 *. 이것은 Chrome, Firefox 및 Edge에서 테스트되었습니다.
* 중요주의 사항 :이 단계에서 위의 내용은 뒤로 버튼을 클릭하면 앞으로 이력을 지우지 만 뒤로 이력은 보존합니다. 이 솔루션은 앞으로 이력을 보존해야하는 경우 적절하지 않습니다. 필자의 경우 양식과 관련하여 일반적으로 마스터-디테일 라우팅 전략을 사용 하므로 전달 기록을 유지하는 것은 중요하지 않습니다.