The cleanest way to do the dirty job
Detect Unsaved Changes in Angular Forms
- ✅ Dirty Check Forms!
- ✅ Support In-App Navigation
- ✅ Support Form Departure
npm install @ngneat/dirty-check-forms
Call the dirtyCheck
function, which accepts three arguments:
- AbstractControl (
FormControl
,FormGroup
,FormArray
) - A stream with the original value to compare
- Config:
debounce
- debounce time ofvalueChanges
. Defaults to 300withDisabled
- whether to include disable fields (by using control'sgetRawValue
) or not. Defaults totrue
.useBeforeunloadEvent
- enable or disableonbeforeunload
event handling. Defaults totrue
.
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() {
// Update the form with the current store value
this.storeSub = this.store.selectSettings()
.subscribe(state => this.settings.patchValue(state, { emitEvent: false }));
// Initialize dirtyCheck
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>
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 {
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.
Thanks goes to these wonderful people (emoji key):
Dan Roujinsky 💻 📖 💡 🤔 📆 |
Netanel Basal 📝 💻 🖋 🎨 📖 💡 🚇 🚧 📆 |
This project follows the all-contributors specification. Contributions of any kind welcome!