diff --git a/apps/forms/48-avoid-losing-form-data/src/app/app.routes.ts b/apps/forms/48-avoid-losing-form-data/src/app/app.routes.ts index 84be34b9a..64794cc4b 100644 --- a/apps/forms/48-avoid-losing-form-data/src/app/app.routes.ts +++ b/apps/forms/48-avoid-losing-form-data/src/app/app.routes.ts @@ -1,4 +1,5 @@ import { Route } from '@angular/router'; +import { unsavedChangesGuard } from './guards/unsaved-changes.guard'; import { JoinComponent } from './pages/join.component'; import { PageComponent } from './pages/page.component'; @@ -11,6 +12,7 @@ export const appRoutes: Route[] = [ { path: 'form', loadComponent: () => JoinComponent, + canDeactivate: [unsavedChangesGuard], }, { path: 'page-1', diff --git a/apps/forms/48-avoid-losing-form-data/src/app/guards/unsaved-changes.guard.ts b/apps/forms/48-avoid-losing-form-data/src/app/guards/unsaved-changes.guard.ts new file mode 100644 index 000000000..04fbce494 --- /dev/null +++ b/apps/forms/48-avoid-losing-form-data/src/app/guards/unsaved-changes.guard.ts @@ -0,0 +1,19 @@ +import { CanDeactivateFn } from '@angular/router'; +import { Observable } from 'rxjs'; + +export interface CanDeactivateComponent { + canDeactivate: () => boolean | Observable; +} + +export const unsavedChangesGuard: CanDeactivateFn = ( + component, +) => { + if ( + component.canDeactivate && + typeof component.canDeactivate === 'function' + ) { + return component.canDeactivate(); + } + + return true; +}; diff --git a/apps/forms/48-avoid-losing-form-data/src/app/pages/join.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/pages/join.component.ts index 51449a7fb..3554129a0 100644 --- a/apps/forms/48-avoid-losing-form-data/src/app/pages/join.component.ts +++ b/apps/forms/48-avoid-losing-form-data/src/app/pages/join.component.ts @@ -1,15 +1,56 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { Dialog } from '@angular/cdk/dialog'; +import { + ChangeDetectionStrategy, + Component, + HostListener, + inject, + viewChild, +} from '@angular/core'; +import { map, Observable } from 'rxjs'; +import { CanDeactivateComponent } from '../guards/unsaved-changes.guard'; +import { AlertDialogComponent } from '../ui/dialog.component'; import { FormComponent } from '../ui/form.component'; @Component({ imports: [FormComponent], template: ` -
+
- +
`, changeDetection: ChangeDetectionStrategy.OnPush, }) -export class JoinComponent {} +export class JoinComponent implements CanDeactivateComponent { + dialog = inject(Dialog); + formComponentRef = viewChild(FormComponent); + + @HostListener('window:beforeunload', ['$event']) + beforeUnloadHandler(event: BeforeUnloadEvent) { + const form = this.formComponentRef()?.form; + if (form && form.dirty) { + event.preventDefault(); + } + } + + canDeactivate(): Observable | boolean { + const form = this.formComponentRef()?.form; + if (!form || !form.dirty) { + return true; + } + + const dialogRef = this.dialog.open(AlertDialogComponent, { + role: 'alertdialog', + ariaDescribedBy: 'dialog-description', + ariaLabelledBy: 'dialog-title', + ariaModal: true, + }); + + return dialogRef.closed.pipe( + map((canLeave) => { + return canLeave === true; + }), + ); + } +} diff --git a/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts b/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts index 21db4d56f..3f5203f1c 100644 --- a/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts +++ b/apps/forms/48-avoid-losing-form-data/src/app/ui/dialog.component.ts @@ -1,4 +1,5 @@ -import { ChangeDetectionStrategy, Component } from '@angular/core'; +import { DialogRef } from '@angular/cdk/dialog'; +import { ChangeDetectionStrategy, Component, inject } from '@angular/core'; // NOTE : this is just the dialog content, you need to implement dialog logic @@ -13,12 +14,14 @@ import { ChangeDetectionStrategy, Component } from '@angular/core';
@@ -26,4 +29,6 @@ import { ChangeDetectionStrategy, Component } from '@angular/core'; `, changeDetection: ChangeDetectionStrategy.OnPush, }) -export class AlertDialogComponent {} +export class AlertDialogComponent { + dialogRef = inject(DialogRef); +}