Skip to content

Commit

Permalink
feat(Answer:48): avoid losing form data solution
Browse files Browse the repository at this point in the history
  • Loading branch information
svenson95 committed Apr 17, 2024
1 parent a691a7e commit 954bc62
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 10 deletions.
5 changes: 4 additions & 1 deletion apps/forms/form-dialog-alert/project.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@
"apps/forms/form-dialog-alert/src/favicon.ico",
"apps/forms/form-dialog-alert/src/assets"
],
"styles": ["apps/forms/form-dialog-alert/src/styles.scss"],
"styles": [
"apps/forms/form-dialog-alert/src/styles.scss",
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css"
],
"scripts": []
},
"configurations": {
Expand Down
6 changes: 5 additions & 1 deletion apps/forms/form-dialog-alert/src/app/app.config.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import { ApplicationConfig } from '@angular/core';
import { provideRouter, withComponentInputBinding } from '@angular/router';
import { appRoutes } from './app.routes';
import { PendingChangesGuard } from './guards/pending-changes.guard';

export const appConfig: ApplicationConfig = {
providers: [provideRouter(appRoutes, withComponentInputBinding())],
providers: [
provideRouter(appRoutes, withComponentInputBinding()),
PendingChangesGuard,
],
};
2 changes: 2 additions & 0 deletions apps/forms/form-dialog-alert/src/app/app.routes.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Route } from '@angular/router';
import { PendingChangesGuard } from './guards/pending-changes.guard';
import { JoinComponent } from './pages/join.component';
import { PageComponent } from './pages/page.component';

Expand All @@ -11,6 +12,7 @@ export const appRoutes: Route[] = [
{
path: 'form',
loadComponent: () => JoinComponent,
canDeactivate: [PendingChangesGuard],
},
{
path: 'page-1',
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { Injectable } from '@angular/core';
import { Observable } from 'rxjs';

export interface ComponentCanDeactivate {
canDeactivate: () => boolean | Observable<boolean>;
}

@Injectable()
export class PendingChangesGuard {
canDeactivate(
component: ComponentCanDeactivate,
): boolean | Observable<boolean> {
return component.canDeactivate();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Directive, HostListener, inject } from '@angular/core';
import { Observable } from 'rxjs';
import { FormComponent } from '../ui/form.component';

@Directive({ selector: 'app-form[appUnloadGuard]', standalone: true })
export class UnloadGuardDirective {
readonly component = inject(FormComponent);

@HostListener('window:beforeunload')
canDeactivate(): Observable<boolean> | boolean {
return !this.component.form!.dirty;
}
}
32 changes: 28 additions & 4 deletions apps/forms/form-dialog-alert/src/app/pages/join.component.ts
Original file line number Diff line number Diff line change
@@ -1,16 +1,40 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import {
ChangeDetectionStrategy,
Component,
inject,
viewChild,
} from '@angular/core';
import { MatDialog } from '@angular/material/dialog';
import { Observable, of } from 'rxjs';
import { PendingChangesGuard } from '../guards/pending-changes.guard';
import { UnloadGuardDirective } from '../guards/unload-window.directive';
import { AlertDialogComponent } from '../ui/dialog.component';
import { FormComponent } from '../ui/form.component';

@Component({
standalone: true,
imports: [FormComponent],
imports: [FormComponent, UnloadGuardDirective],
template: `
<section class="mx-auto max-w-screen-sm">
<div class="rounded-lg bg-white p-8 shadow-lg lg:p-12">
<app-form />
<app-form appUnloadGuard />
</div>
</section>
`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class JoinComponent {}
export class JoinComponent implements PendingChangesGuard {
private dialog = inject(MatDialog);
formComponent = viewChild(FormComponent);

canDeactivate(): boolean | Observable<boolean> {
if (this.formComponent()!.form.dirty) {
const dialogRef = this.dialog.open(AlertDialogComponent, {
disableClose: true,
});
return dialogRef.afterClosed();
} else {
return of(true);
}
}
}
18 changes: 14 additions & 4 deletions apps/forms/form-dialog-alert/src/app/ui/dialog.component.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,34 @@
import { ChangeDetectionStrategy, Component } from '@angular/core';
import { MatDialogClose } from '@angular/material/dialog';

// NOTE : this is just the dialog content, you need to implement dialog logic

@Component({
standalone: true,
imports: [MatDialogClose],
template: `
<div role="alert" class="rounded-xl border border-gray-100 bg-white p-5">
<h3 class="block text-xl font-medium text-red-600">
You have unsaved information!
<div
role="alertdialog"
aria-labelledby="alert-title"
aria-describedby="alert-description"
class="rounded-xl border border-gray-100 bg-white p-5">
<h3 id="alert-title" class="block text-xl font-medium text-red-600">
Confirmation
</h3>
<p class="mt-1 text-gray-700">Do you want to continue and lose them?</p>
<p id="alert-description" class="mt-1 text-gray-700">
You have unsaved information! Do you want to continue and lose them?
</p>
<div class="mt-4 flex gap-2">
<button
[matDialogClose]="true"
class="inline-flex items-center gap-2 rounded-lg bg-red-600 px-4 py-2 text-white hover:bg-red-700">
Yes continue
</button>
<button
[matDialogClose]="false"
class="block rounded-lg px-4 py-2 text-gray-700 transition hover:bg-gray-50">
Stay on page
</button>
Expand Down

0 comments on commit 954bc62

Please sign in to comment.