UNPKG

@ngneat/dirty-check-forms

Version:
155 lines (111 loc) 6.06 kB
<p align="center"> <img width="20%" height="20%" src="./logo.svg"> </p> <br /> [![MIT](https://img.shields.io/packagist/l/doctrine/orm.svg?style=flat-square)]() [![commitizen](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg?style=flat-square)]() [![PRs](https://img.shields.io/badge/PRs-welcome-brightgreen.svg?style=flat-square)]() [![styled with prettier](https://img.shields.io/badge/styled_with-prettier-ff69b4.svg?style=flat-square)](https://github.com/prettier/prettier) [![All Contributors](https://img.shields.io/badge/all_contributors-2-orange.svg?style=flat-square)](#contributors-) [![ngneat](https://img.shields.io/badge/@-ngneat-383636?style=flat-square&labelColor=8f68d4)](https://github.com/ngneat/) [![spectator](https://img.shields.io/badge/tested%20with-spectator-2196F3.svg?style=flat-square)]() > 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!