Skip to content

useFormDirty

Returns whether a FormGroup is dirty (has been modified) as a signal. The signal updates reactively whenever the form's dirty state changes.

Usage

typescript
import { useFormDirty } from 'ng-reactive-utils';

@Component({
  template: `
    <form [formGroup]="form">
      <input formControlName="title" />
      <textarea formControlName="content"></textarea>

      @if (isDirty()) {
        <div class="unsaved-warning">
          You have unsaved changes
          <button (click)="resetForm()">Discard</button>
        </div>
      }

      <button [disabled]="!isDirty()">Save Changes</button>
    </form>
  `,
})
class EditorComponent {
  form = new FormGroup({
    title: new FormControl(''),
    content: new FormControl(''),
  });

  isDirty = useFormDirty(this.form);

  resetForm() {
    this.form.reset();
  }

  // Use with canDeactivate guard
  canDeactivate(): boolean {
    return !this.isDirty() || confirm('Discard unsaved changes?');
  }
}

Parameters

ParameterTypeDefaultDescription
formFormGrouprequiredThe FormGroup to check dirty state for

Returns

Signal<boolean> - A readonly signal containing the dirty state (true if modified)

Notes

  • Uses toSignal with form.valueChanges observable
  • Returns true when any control value has been changed by the user
  • Returns false when form is pristine (unchanged)
  • Useful for "unsaved changes" warnings and save button states

Source

ts
import { Signal } from '@angular/core';
import { toSignal } from '@angular/core/rxjs-interop';
import { FormGroup } from '@angular/forms';
import { map } from 'rxjs';

/**
 * Returns whether a FormGroup is dirty (has been modified) as a signal.
 * The signal updates reactively whenever the form's dirty state changes.
 *
 * @param form - The FormGroup to check dirty state for
 * @returns A signal containing the dirty state (true if modified)
 *
 * @example
 * ```typescript
 * @Component({
 *   template: `
 *     <form [formGroup]="form">
 *       <input formControlName="name" />
 *       @if (isDirty()) {
 *         <span>You have unsaved changes</span>
 *       }
 *     </form>
 *   `
 * })
 * class MyComponent {
 *   form = new FormGroup({
 *     name: new FormControl('')
 *   });
 *   isDirty = useFormDirty(this.form);
 * }
 * ```
 */
export const useFormDirty = (form: FormGroup): Signal<boolean> => {
  return toSignal(form.valueChanges.pipe(map(() => form.dirty)), {
    initialValue: form.dirty,
  }) as Signal<boolean>;
};

Released under the MIT License.