@ngneat/dirty-check-forms
Version:
Detect Unsaved Changes in Angular Forms
155 lines (111 loc) • 6.06 kB
Markdown
<p align="center">
<img width="20%" height="20%" src="./logo.svg">
</p>
<br />
[]()
[]()
[]()
[](https://github.com/prettier/prettier)
[](#contributors-)
[](https://github.com/ngneat/)
[]()
> The cleanest way to do the dirty job
Detect Unsaved Changes in Angular Forms
<img src="https://miro.medium.com/max/1400/1*OEA-Gdmy4GFmkNPCtwHXKg.gif">
## Features
- ✅ Dirty Check Forms!
- ✅ Support In-App Navigation
- ✅ Support Form Departure
## Table of Contents
- [Installation](#installation)
- [Usage](#usage)
- [In-App Navigation](#In-App-Navigation)
## Installation
`npm install @ngneat/dirty-check-forms`
## Usage
Call the `dirtyCheck` function, which accepts two arguments:
1. AbstractControl (`FormControl`, `FormGroup`, `FormArray`)
2. A stream with the original value to compare
3. 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:
```ts
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();
}
}
```
```html
<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:
```ts
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:
```ts
import { dirtyCheck, DirtyComponent } from '@ngneat/dirty-check-forms';
@Component({ ... })
export class SettingsComponent implements DirtyComponent { ... }
```
The last step is to activate the `DirtyCheckGuard`:
```ts
const routes: Routes = [
{
path: 'settings',
component: SettingsComponent,
canDeactivate: [DirtyCheckGuard]
}
];
```
You can find a complete example [here](https://github.com/ngneat/dirty-check-forms/tree/master/apps/playground).
## Contributors ✨
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
<!-- prettier-ignore-start -->
<!-- markdownlint-disable -->
<table>
<tr>
<td align="center"><a href="https://github.com/danzrou"><img src="https://avatars3.githubusercontent.com/u/6433766?v=4" width="100px;" alt=""/><br /><sub><b>Dan Roujinsky</b></sub></a><br /><a href="https://github.com/@ngneat/dirty-check-forms/commits?author=danzrou" title="Code">💻</a> <a href="https://github.com/@ngneat/dirty-check-forms/commits?author=danzrou" title="Documentation">📖</a> <a href="#example-danzrou" title="Examples">💡</a> <a href="#ideas-danzrou" title="Ideas, Planning, & Feedback">🤔</a> <a href="#projectManagement-danzrou" title="Project Management">📆</a></td>
<td align="center"><a href="https://www.netbasal.com"><img src="https://avatars1.githubusercontent.com/u/6745730?v=4" width="100px;" alt=""/><br /><sub><b>Netanel Basal</b></sub></a><br /><a href="#blog-NetanelBasal" title="Blogposts">📝</a> <a href="https://github.com/@ngneat/dirty-check-forms/commits?author=NetanelBasal" title="Code">💻</a> <a href="#content-NetanelBasal" title="Content">🖋</a> <a href="#design-NetanelBasal" title="Design">🎨</a> <a href="https://github.com/@ngneat/dirty-check-forms/commits?author=NetanelBasal" title="Documentation">📖</a> <a href="#example-NetanelBasal" title="Examples">💡</a> <a href="#infra-NetanelBasal" title="Infrastructure (Hosting, Build-Tools, etc)">🚇</a> <a href="#maintenance-NetanelBasal" title="Maintenance">🚧</a> <a href="#projectManagement-NetanelBasal" title="Project Management">📆</a> <a href="https://github.com/@ngneat/dirty-check-forms/commits?author=NetanelBasal" title="Tests">⚠️</a></td>
</tr>
</table>
<!-- markdownlint-enable -->
<!-- prettier-ignore-end -->
<!-- ALL-CONTRIBUTORS-LIST:END -->
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!