form-control-change-tracker
v1.0.2
Published
[](https://www.npmjs.com/package/form-control-change-tracker) [](https://www.npmjs.com/package/form-
Maintainers
Readme
Angular Form Control Change Tracker
🚀 Live Demo
Try it on StackBlitz - Interactive demo with reactive and template-driven forms (Angular 19)
Version Compatibility
| Angular Version | Library Version | Status |
| :-------------- | :-------------- | :---------------- |
| v19+ | ^1.0.0 | 🟢 Stable |
| v15 - v19 | 0.0.4 | 🟡 Legacy Support |
Custom Comparison Strategy
The library uses a SimpleStrategy by default, which performs a fast reference check for primitives and a key-order independent deep comparison for objects. To use a different strategy (e.g., deep-diff):
Install
deep-diff(or your preferred library):npm install deep-diff npm install @types/deep-diff --save-devCreate the Strategy:
import { Injectable } from "@angular/core"; import { ComparisonStrategy } from "form-control-change-tracker"; import { diff } from "deep-diff"; @Injectable() export class DeepDiffStrategy implements ComparisonStrategy { isEqual(a: any, b: any): boolean { // Returns true if no differences found return !diff(a, b); } }Provide it in your Module:
import { HG_COMPARISON_STRATEGY } from "form-control-change-tracker"; import { DeepDiffStrategy } from "./strategies/deep-diff-strategy"; @NgModule({ // ... providers: [ { provide: HG_COMPARISON_STRATEGY, useClass: DeepDiffStrategy, }, ], }) export class AppModule {}
Very often when developers need to know if there were any changes inside the a form in order to present a unsaved changes confirmation dialog when navigating away or in order to disable the save button when there is nothing new to save. The FormControlChangeTrackerModule provides two things:
The
ChangeTrackerDirective (hgChangeTracker)that can be set on the individual form controls in order to track if any changes are madeAnd the
@hasChanges()decorator that is applied over theChangeTrackerDirectivedirectives in order to provide you a boolean value indicating if there are any changes or not.
Usage
1. New Container API (Recommended)
The new API allows you to track changes directly in your template without needing complex decorators.
Angular 19+ (Standalone Components)
import { Component } from "@angular/core";
import { ReactiveFormsModule, FormBuilder, FormGroup } from "@angular/forms";
import { FormControlChangeTrackerModule } from "form-control-change-tracker";
@Component({
selector: "app-my-form",
standalone: true,
imports: [ReactiveFormsModule, FormControlChangeTrackerModule],
template: `
<form [formGroup]="form" (ngSubmit)="submit()" hgChangeTrackerContainer #tracker="hgChangeTrackerContainer">
<div class="form-group">
<label>First Name</label>
<!-- Auto-captures initial value on init -->
<input formControlName="firstName" hgChangeTracker />
</div>
<div class="form-group">
<label>Last Name</label>
<input formControlName="lastName" hgChangeTracker />
</div>
<!-- Check for changes anywhere in the form -->
<button [disabled]="!tracker.hasChanges">Submit</button>
<!-- Reset initial/default values to current values -->
<button type="button" [disabled]="!tracker.hasChanges" (click)="tracker.resync()">Update Defaults</button>
</form>
`,
})
export class MyFormComponent {
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
firstName: [""],
lastName: [""],
});
}
submit() {
console.log("Form submitted:", this.form.value);
}
}Legacy NgModule (Angular 15-18)
// app.module.ts
imports: [
// ...
FormControlChangeTrackerModule,
];template
<form [formGroup]="form" (ngSubmit)="submit()" hgChangeTrackerContainer #tracker="hgChangeTrackerContainer">
<div class="form-group">
<label>First Name</label>
<!-- Auto-captures initial value on init -->
<input formControlName="firstName" hgChangeTracker />
</div>
<div class="form-group">
<label>Last Name</label>
<input formControlName="lastName" hgChangeTracker />
</div>
<!-- Check for changes anywhere in the form -->
<button [disabled]="!tracker.hasChanges">Submit</button>
<!-- Reset initial/default values to current values -->
<button [disabled]="!tracker.hasChanges" (click)="tracker.resync()">Update Defaults</button>
</form>2. Configuration Options
Debounce Time Adjust the debounce time for change detection (default: 20ms).
<input hgChangeTracker [debounceTime]="300" />Multi-Initial Values Allow multiple values to be considered "valid" (unchanged).
<input hgChangeTracker [multiInitialValue]="true" [initialValue]="['A', 'B']" />Auto-Sync Control whether the directive automatically captures the initial value.
<!-- Disable auto sync if you want full manual control -->
<input hgChangeTracker [autoInitialValueSync]="false" [initialValue]="startValue" />3. Legacy Decorator API
The library serves backward compatibility for the @hasChanges() decorator usage.
@Component({...})
export class MyComponent {
@ViewChildren(ChangeTrackerDirective) @hasChanges() hasFormChanges: boolean;
}Features
- Deep Comparison: Uses deep-diff strategies to correctly track object changes.
- Reactive: Built on RxJS for efficient change detection.
- Debounced: Prevents UI thrashing on high-frequency inputs.
- Container Support: Easily aggregate change status for an entire form.
