Zero-dependency Angular form draft auto-save and restore. Works with Angular 14-20.
- Auto-saves form values to localStorage
- Restores drafts on page reload
- Works with Reactive and Template-driven forms
- Handles nested FormGroups and FormArrays
- Visual banner with discard option
- 7-day draft expiration
- Zero external dependencies
- No Angular animations required (pure CSS)
- Angular 14-20
- TypeScript 4.7+
Note: Angular 21+ support is not yet tested. The package uses stable Angular APIs that should work, but may require updates for newer versions. Please report any issues.
npm install ngx-form-draftLive demo: https://neokyuubi.github.io/ngx-form-draft/
import { NgxFormDraftModule } from 'ngx-form-draft';
@NgModule({
imports: [NgxFormDraftModule]
})
export class AppModule {}<form [formGroup]="myForm" ngxFormDraft="uniqueFormId">
<input formControlName="name">
<input formControlName="email">
</form><form #myForm="ngForm" ngxFormDraft="uniqueFormId">
<input name="name" [(ngModel)]="model.name">
<input name="email" [(ngModel)]="model.email">
</form><!-- Reactive -->
<form [formGroup]="editForm" [ngxFormDraft]="'edit_' + userId">
<!-- Template-driven -->
<form #editForm="ngForm" [ngxFormDraft]="'edit_' + userId"><form
[formGroup]="myForm"
[ngxFormDraft]="'myForm_' + entityId"
[draftDebounce]="1000"
[draftExcludeFields]="['password', 'confirmPassword']"
[draftShowOnChange]="true"
[draftRestoredText]="'Draft restored'"
[draftSavedText]="'Draft saved'"
[draftSavedLabel]="'saved'"
[draftDiscardText]="'Discard'">
</form><form
#myForm="ngForm"
[ngxFormDraft]="'myForm_' + entityId"
[draftDebounce]="1000"
[draftExcludeFields]="['password', 'confirmPassword']"
[draftShowOnChange]="true"
[draftRestoredText]="'Draft restored'"
[draftSavedText]="'Draft saved'"
[draftSavedLabel]="'saved'"
[draftDiscardText]="'Discard'">
</form>ngxFormDraft(string): Unique form identifierdraftDebounce(number): Debounce time in ms (default: 800)draftExcludeFields(string[]): Fields to exclude from draftdraftShowOnChange(boolean): Show banner immediately on change (default: false)draftRestoredText(string): Banner text for restored draftdraftSavedText(string): Banner text for saved draftdraftSavedLabel(string): Label before timestampdraftDiscardText(string): Discard button text
The draft is not cleared automatically on form submit. To clear it (e.g. after a successful submit), use FormDraftService — the same for both reactive and template-driven forms.
Inject the service and call clear(formId) with the same formId you use on the form:
import { FormDraftService } from 'ngx-form-draft';
export class MyComponent {
constructor(private draftService: FormDraftService) {}
onSubmit() {
this.myForm.markAllAsTouched();
if (this.myForm.invalid) return;
// ... submit to API ...
this.draftService.clear('uniqueFormId'); // same id as ngxFormDraft
}
}Users can also clear the draft at any time via the banner’s Discard button.
The package has zero dependencies and supports any i18n solution. Just pass translated strings via inputs:
<form
[formGroup]="myForm"
ngxFormDraft="myForm"
[draftRestoredText]="'form_draft.restored' | translate"
[draftSavedText]="'form_draft.saved' | translate"
[draftSavedLabel]="'form_draft.saved_label' | translate"
[draftDiscardText]="'form_draft.discard' | translate">
</form><form
[formGroup]="myForm"
ngxFormDraft="myForm"
[draftRestoredText]="restoredText"
[draftSavedText]="savedText"
[draftDiscardText]="discardText"
i18n-draftRestoredText="@@formDraftRestored"
i18n-draftSavedText="@@formDraftSaved"
i18n-draftDiscardText="@@formDraftDiscard">
</form>// component.ts
export class MyComponent {
draftTexts = {
restored: 'Brouillon restauré',
saved: 'Brouillon sauvegardé',
savedLabel: 'sauvegardé',
discard: 'Supprimer'
};
}<!-- template.html -->
<form
[formGroup]="myForm"
ngxFormDraft="myForm"
[draftRestoredText]="draftTexts.restored"
[draftSavedText]="draftTexts.saved"
[draftSavedLabel]="draftTexts.savedLabel"
[draftDiscardText]="draftTexts.discard">
</form>If you don't provide any text inputs, defaults are:
draftRestoredText: "Draft restored"draftSavedText: "Draft saved"draftSavedLabel: "saved"draftDiscardText: "Discard"
MIT