[Angular] 저장되지 않은 파일이 있는데 뒤로 가기를 누른 경우 경고 알럿 띄우기

안녕하세요. 명동섞어찌개 입니다.

오늘은 Angular 페이지에서 문서 (form) 작성 중 또는 무언가를 등록하다가, 저장하지 않고 브라우저 뒤로가기 버튼을 눌렀을 때 '저장하지 않은 파일이 있습니다. 페이지를 나가시겠습니까?'라고 묻는 알럿 띄우는 기능을 만들려고 합니다.



일반적으로 이런 기능은 javascript 에서는 popstate 이벤트 리스너를 사용해서 구현합니다. (모바일에서도 사용한 기억이 있습니다.)

1
2
3
window.addEventListener('popstate', (event) => {
  console.log("location: " + document.location + ", state: " + JSON.stringify(event.state));
});

하지만 Angular 에서 window:popstate 로 아무리 구현해 봐도 원하는 기능이 작동하지를 않더군요.

아마도 popstate 는 페이지가 이동할 때 호출되는 이벤트인데, Angular 는 SPA(Single Page Application) 다 보니 하나의 페이지 안에서 component 끼리 routing 될 뿐이라서 저 이벤트가 호출되지 않는 듯 했습니다. (자세히 아시는 분은 댓글로 제보 부탁드립니다!!)

검색해보니 Angular 자체적으로 페이지를 나갈 때 처리해줄 수 있는 솔루션이 있더라구요.
router 의 canDeactivate !!!

canActivate 는 로그인하지 않은 사용자가 해당 페이지에 접근하지 못하게 막는 용도로 사용되는 경우를 많이 봤는데요, 페이지를 나갈 때 체크하는 용도로 canDeactivate 를 사용해 본 건 처음이었습니다.


시나리오를 말씀드리자면

1. CdnRegisterComponent 라는 페이지 컴포넌트에서 파일을 등록하다가 저장 버튼을 안 누르고
2. 브라우저 뒤로가기 버튼을 눌렀다!

이 경우에 알럿이 뜨게 하는 처리입니다.


1. CdnRegisterComponent 페이지에 저장될 파일이 있는지 없는지 판단할 flag 를 설정합니다. 

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
@Component({
    selector: 'app-cdn-register',
    templateUrl: './cdn-register.component.html',
    styleUrls: ['./cdn-register.component.scss']
})
export class CdnRegisterComponent implements OnInit {

.
.

public hasUnsavedData = false;

.
.

fileHandling( files ) {
        //파일이 들어온 경우 hasUnsavedData 가 있다고 표시해준다
        this.hasUnsavedData = true;
}

이 flag 는 public 이어야 합니다. 다른 service 에서 접근이 가능해야 하거든요!


2. 페이지를 떠날 때 감시하는(?) canDeactivate Service 를 만들어줍니다.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
import {Injectable} from '@angular/core';
import {ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot, UrlTree} from '@angular/router';
import {Observable} from 'rxjs';
import {CdnRegisterComponent} from '../../pages/cdn/cdn-register/cdn-register.component';

@Injectable()
export class HasUnsavedDataGuard implements CanDeactivate<CdnRegisterComponent> {

    constructor() {}

    canDeactivate(
        component: CdnRegisterComponent,
        currentRoute: ActivatedRouteSnapshot,
        currentState: RouterStateSnapshot,
        nextState: RouterStateSnapshot
    ): Observable<boolean|UrlTree>|Promise<boolean|UrlTree>|boolean|UrlTree {
        //cdnRegisterComponent 페이지의 hasUnsavedData 가 true 즉 저장 안된 파일이 있다면
if(component.hasUnsavedData) { let confirmMsg = confirm('저장되지 않은 데이터가 있습니다. 계속하시겠습니까?'); if(confirmMsg) { //확인을 눌렀을 때 할 기능 구현부분 } return confirmMsg; } return true; } }

참조: https://angular.io/api/router/CanDeactivate



3. 해당 페이지가 세팅되어 있는 있는 router 에 canDeactivate 를 세팅해줍니다.

먼저 providers 에 해당 HasUnsavedDataGuard  Service 를 세팅해주고,

1
2
3
4
5
6
7
@NgModule({
    imports: [RouterModule.forChild(routes)],
    exports: [RouterModule],
    providers: [HasUnsavedDataGuard]
})
export class PagesRouterModule {
}


해당 페이지 router 에 canDeactivate 를 세팅해줍니다.

1
 {path: 'cdn/register', component: CdnRegisterComponent, canDeactivate: [HasUnsavedDataGuard] },



이렇게 해서 기능이 완성되었습니다!!
이외에 canDeactivate 를 사용하는 다른 용도가 있다면 댓글로 공유 부탁드립니다~^^





댓글

주간 인기글

[정보] 인스타그램은 당신의 소리를 '듣고' 있을 수도 있습니다

[Angular] 모델, 값이 바뀌었는데 화면 template 이 업데이트 되지 않을 때 조치 팁

[AWS] Lambda + API GateWay를 이용해 간단한 RESTful API 만들기 #1

[AWS] Lambda + API GateWay를 이용해 간단한 RESTful API 만들기 #2

안드로이드에서 당겨서 새로고침(SwipeRefreshLayout) 쉽게 구현하기