
The cleanest way to do the dirty job
Detect Unsaved Changes in Angular Forms
Features
- β
Dirty Check Forms!
- β
Support In-App Navigation
- β
Support Form Departure
Table of Contents
Installation
npm install @ngneat/dirty-check-forms
Usage
Call the dirtyCheck
function, which accepts two arguments:
- AbstractControl (
FormControl
, FormGroup
, FormArray
)
- A stream with the original value to compare
- Config
3.1 debounce
- debounce time of valueChanges
. Defaults to 300
3.2 withDisabled
- whether to include disable fields (by using control's getRawValue
) or not. Defaults to true
.
The function returns an Observable<boolean>
, which notifies whether the form is dirty. Furthermore, it also hooks on the browser's beforeunload
event to confirm upon refreshing/closing the tab when needed.
For example:
import { dirtyCheck } from '@ngneat/dirty-check-forms';
@Component({ ... })
export class SettingsComponent {
storeSub: Subscription;
settings = new FormGroup({
firstName: new FormControl(''),
lastName: new FormControl('')
});
isDirty$: Observable<boolean>;
constructor(private store: Store) {}
ngOnInit() {
this.storeSub = this.store.selectSettings()
.subscribe(state => this.settings.patchValue(state, { emitEvent: false }));
this.isDirty$ = dirtyCheck(this.settings, this.store.selectSettings());
}
ngOnDestroy() {
this.storeSub && this.storeSub.unsubscribe();
}
}
<form [formGroup]="settings">
<input type="text" formControlName="firstName" placeholder="First name" />
<input type="text" formControlName="lastName" placeholder="Last name" />
<button (click)="submit()" [disabled]="isDirty$ | async">Submit</button>
</form>
In-App Navigation:
Create a guard that extends DirtyCheckGuard
, and provide the confirmChanges
method:
import { Injectable } from "@angular/core";
import { DirtyCheckGuard } from "@ngneat/dirty-check-forms";
import { Observable } from "rxjs";
@Injectable()
export class DirtyGuard extends DirtyCheckGuard<DirtyComponent> {
constructor() {
super();
}
confirmChanges(): Observable<boolean> | boolean {
return confirm('Are you sure you want to discard changes?');
}
}
Note that when using a guard, your component must implement the DirtyComponent
interface:
import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms';
@Component({ ... })
export class SettingsComponent implements DirtyComponent { ... }
The last step is to activate the DirtyCheckGuard
:
const routes: Routes = [
{
path: 'settings',
component: SettingsComponent,
canDeactivate: [DirtyCheckGuard]
}
];
You can find a complete example here.
Contributors β¨
Thanks goes to these wonderful people (emoji key):
This project follows the all-contributors specification. Contributions of any kind welcome!