Skip to content

Commit 6cdbfcb

Browse files
committed
feat: 55-back-button-navigation
1 parent 8f40f41 commit 6cdbfcb

10 files changed

Lines changed: 98 additions & 12 deletions

File tree

apps/angular/55-back-button-navigation/project.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
"browser": "apps/angular/55-back-button-navigation/src/main.ts",
1616
"polyfills": ["zone.js"],
1717
"tsConfig": "apps/angular/55-back-button-navigation/tsconfig.app.json",
18-
"inlineStyleLanguage": "css",
18+
"inlineStyleLanguage": "scss",
1919
"assets": [
2020
{
2121
"glob": "**/*",
2222
"input": "apps/angular/55-back-button-navigation/public"
2323
}
2424
],
2525
"styles": [
26-
"apps/angular/55-back-button-navigation/src/styles.css",
26+
"apps/angular/55-back-button-navigation/src/styles.scss",
2727
"node_modules/@angular/material/prebuilt-themes/indigo-pink.css"
2828
],
2929
"scripts": []

apps/angular/55-back-button-navigation/src/app/app.component.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import { Component } from '@angular/core';
2-
import { RouterLink, RouterOutlet } from '@angular/router';
2+
import { RouterOutlet } from '@angular/router';
33

44
@Component({
5-
imports: [RouterOutlet, RouterLink],
5+
imports: [RouterOutlet],
66
selector: 'app-root',
77
templateUrl: './app.component.html',
88
})

apps/angular/55-back-button-navigation/src/app/app.routes.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Routes } from '@angular/router';
2+
import { guardNavigationGuard } from './core/guard-navigation.guard';
23
import { HomeComponent } from './home/home.component';
34
import { SensitiveActionComponent } from './sensitive-action/sensitive-action.component';
45
import { SimpleActionComponent } from './simple-action/simple-action.component';
@@ -16,9 +17,11 @@ export const APP_ROUTES: Routes = [
1617
{
1718
path: 'simple-action',
1819
component: SimpleActionComponent,
20+
canDeactivate: [guardNavigationGuard],
1921
},
2022
{
2123
path: 'sensitive-action',
2224
component: SensitiveActionComponent,
25+
canDeactivate: [guardNavigationGuard],
2326
},
2427
];
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export interface BackButtonNavigationHandler {
2+
canDeactivate(): boolean;
3+
handleBackNavigation(): void;
4+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { CanDeactivateFn } from '@angular/router';
2+
import { BackButtonNavigationHandler } from '../back-button-navigation-handler';
3+
4+
export const guardNavigationGuard: CanDeactivateFn<
5+
BackButtonNavigationHandler
6+
> = (component) => {
7+
if (component.canDeactivate()) {
8+
return true;
9+
}
10+
11+
component.handleBackNavigation();
12+
return false;
13+
};
Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
<h2 mat-dialog-title>Delete file</h2>
2-
<mat-dialog-content>Would you like to delete cat.jpeg?</mat-dialog-content>
1+
<h2 mat-dialog-title>{{ data?.title ?? 'Delete file' }}</h2>
2+
<mat-dialog-content>
3+
{{ data?.content ?? 'Would you like to delete cat.jpeg?' }}
4+
</mat-dialog-content>
35
<mat-dialog-actions>
4-
<button mat-button mat-dialog-close>No</button>
5-
<button mat-button mat-dialog-close cdkFocusInitial>Ok</button>
6+
<button mat-button mat-dialog-close>
7+
{{ data?.cancelLabel ?? 'No' }}
8+
</button>
9+
<button mat-button mat-dialog-close cdkFocusInitial>
10+
{{ data?.confirmLabel ?? 'Ok' }}
11+
</button>
612
</mat-dialog-actions>

apps/angular/55-back-button-navigation/src/app/dialog/dialog.component.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
22
import { MatButtonModule } from '@angular/material/button';
33
import {
4+
MAT_DIALOG_DATA,
45
MatDialogActions,
56
MatDialogClose,
67
MatDialogContent,
@@ -21,5 +22,15 @@ import {
2122
changeDetection: ChangeDetectionStrategy.OnPush,
2223
})
2324
export class DialogComponent {
25+
readonly data = inject<DialogData>(MAT_DIALOG_DATA, {
26+
optional: true,
27+
});
2428
readonly dialogRef = inject(MatDialogRef<DialogComponent>);
2529
}
30+
31+
export interface DialogData {
32+
title: string;
33+
content: string;
34+
cancelLabel?: string;
35+
confirmLabel?: string;
36+
}
Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,58 @@
11
import { Component, inject } from '@angular/core';
22
import { MatButtonModule } from '@angular/material/button';
3-
import { MatDialog } from '@angular/material/dialog';
3+
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
4+
import { BackButtonNavigationHandler } from '../back-button-navigation-handler';
45
import { DialogComponent } from '../dialog/dialog.component';
56

67
@Component({
78
imports: [MatButtonModule],
89
selector: 'app-sensitive-action',
910
templateUrl: './sensitive-action.component.html',
1011
})
11-
export class SensitiveActionComponent {
12+
export class SensitiveActionComponent implements BackButtonNavigationHandler {
1213
readonly #dialog = inject(MatDialog);
14+
#initialDialogRef: MatDialogRef<DialogComponent> | null = null;
15+
#confirmationDialogRef: MatDialogRef<DialogComponent> | null = null;
1316

1417
openDialog(): void {
15-
this.#dialog.open(DialogComponent, {
18+
this.#initialDialogRef = this.#dialog.open(DialogComponent, {
1619
width: '250px',
20+
closeOnNavigation: false,
1721
});
22+
23+
this.#initialDialogRef.afterClosed().subscribe(() => {
24+
this.#initialDialogRef = null;
25+
this.#confirmationDialogRef?.close();
26+
this.#confirmationDialogRef = null;
27+
});
28+
}
29+
30+
canDeactivate(): boolean {
31+
return !this.#initialDialogRef && !this.#confirmationDialogRef;
32+
}
33+
34+
handleBackNavigation(): void {
35+
if (this.#confirmationDialogRef) {
36+
this.#confirmationDialogRef.close();
37+
return;
38+
}
39+
40+
if (this.#initialDialogRef) {
41+
this.#confirmationDialogRef = this.#dialog.open(DialogComponent, {
42+
width: '250px',
43+
closeOnNavigation: false,
44+
data: {
45+
title: 'Confirm navigation',
46+
content:
47+
'A sensitive dialog is still open. Press Back again to close this confirmation and stay here.',
48+
cancelLabel: 'Stay',
49+
confirmLabel: 'Ok',
50+
},
51+
});
52+
53+
this.#confirmationDialogRef.afterClosed().subscribe(() => {
54+
this.#confirmationDialogRef = null;
55+
});
56+
}
1857
}
1958
}
Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,29 @@
11
import { Component, inject } from '@angular/core';
22
import { MatButtonModule } from '@angular/material/button';
33
import { MatDialog } from '@angular/material/dialog';
4+
import { BackButtonNavigationHandler } from '../back-button-navigation-handler';
45
import { DialogComponent } from '../dialog/dialog.component';
56

67
@Component({
78
imports: [MatButtonModule],
89
selector: 'app-simple-action',
910
templateUrl: './simple-action.component.html',
1011
})
11-
export class SimpleActionComponent {
12+
export class SimpleActionComponent implements BackButtonNavigationHandler {
1213
readonly #dialog = inject(MatDialog);
1314

1415
openDialog(): void {
1516
this.#dialog.open(DialogComponent, {
1617
width: '250px',
18+
closeOnNavigation: false,
1719
});
1820
}
21+
22+
canDeactivate(): boolean {
23+
return this.#dialog.openDialogs.length === 0;
24+
}
25+
26+
handleBackNavigation(): void {
27+
this.#dialog.closeAll();
28+
}
1929
}

apps/angular/55-back-button-navigation/src/styles.css renamed to apps/angular/55-back-button-navigation/src/styles.scss

File renamed without changes.

0 commit comments

Comments
 (0)