Skip to content

Signal Forms dirty() state not updating when field values change from initial state #64465

@Arigatouz

Description

@Arigatouz

Which @angular/* package(s) are the source of the bug?

forms

Is this a regression?

No

Description

The dirty() method on signal form controls always returns false even when field values have been changed from their initial state.
According to standard form behavior, dirty should be true when the current value changes from the initial value.


  userModel = signal<IUserModel>({
    name: '',
    email: '',
    password: '',
    confirmPassword: ''
  })

  userForm = form(this.userModel, (field) => {
    required(field.name, {message: 'Name is required'});
    required(field.email, {message: 'Email is required'});
    email(field.email, {message: 'Email is not valid'});
    required(field.password, {message: 'Password is required'});
    required(field.confirmPassword, {message: 'Confirm Password is required'});
  })

  onSubmit(event: Event) {
    event.preventDefault();
    console.log('Form Value ==>', this.userForm().value());
    console.log('Form valid ==>', this.userForm().valid());
    console.log('Form touched ==>', this.userForm().touched());
    console.log('Form dirty ==>', this.userForm().dirty());
  }
<form (submit)="onSubmit($event)"
      class="flex flex-col gap-4 p-4 border border-gray-300 rounded-lg max-w-md mx-auto mt-20">
  <label for="name" class="flex flex-col gap-1">
    Name
    <input id="name" [field]="userForm.name" type="text" class="border border-indigo-500 p-2"
           placeholder="please enter your name"/>
  </label>
  <label for="email" class="flex flex-col gap-1">
    Email
    @if (userForm.email().errors().length) {
      @for (error of userForm.email().errors(); track error.message) {
        <span class="text-red-500 text-sm">{{ error.message }}</span>
      }
    }

    <input id="email" [field]="userForm.email" type="email" class="border border-indigo-500 p-2"
           placeholder="please enter your email"/>
  </label>
  <label for="password" class="flex flex-col gap-1">
    Enter Password
    <input id="password" [field]="userForm.password" type="password" class="border border-indigo-500 p-2"
           placeholder="please enter your password"/>
  </label>
  <label for="confirm-password" class="flex flex-col gap-1">
    Confirm Password
    <input id="confirm-password" [field]="userForm.confirmPassword" type="password" class="border border-indigo-500 p-2"
           placeholder="please enter your confirm password"/>
  </label>

  <button type="submit" class=" cursor-pointer bg-indigo-500 text-white p-2 rounded hover:bg-indigo-600 transition"
  >
    Submit
  </button>
</form>

I have also revised this with @SanderElias

Please provide a link to a minimal reproduction of the bug

https://stackblitz.com/edit/stackblitz-starters-pusdytvj?file=package.json

Please provide the exception or error you saw


Please provide the environment you discovered this bug in (run ng version)

Angular CLI       : 21.0.0-next.8
Angular           : 21.0.0-next.8
Node.js           : 22.14.0
Package Manager   : npm 10.9.2
Operating System  : win32 x64

┌───────────────────────────┬───────────────────┬───────────────────┐
│ Package                   │ Installed Version │ Requested Version │
├───────────────────────────┼───────────────────┼───────────────────┤
│ @angular/build            │ 21.0.0-next.8     │ ^21.0.0-next.7    │
│ @angular/cli              │ 21.0.0-next.8     │ ^21.0.0-next.7    │
│ @angular/common           │ 21.0.0-next.8     │ ^21.0.0-next.0    │
│ @angular/compiler         │ 21.0.0-next.8     │ ^21.0.0-next.0    │
│ @angular/compiler-cli     │ 21.0.0-next.8     │ ^21.0.0-next.0    │
│ @angular/core             │ 21.0.0-next.8     │ ^21.0.0-next.0    │
│ @angular/forms            │ 21.0.0-next.8     │ ^21.0.0-next.0    │
│ @angular/platform-browser │ 21.0.0-next.8     │ ^21.0.0-next.0    │
│ @angular/router           │ 21.0.0-next.8     │ ^21.0.0-next.0    │
│ rxjs                      │ 7.8.2             │ ~7.8.0            │
│ typescript                │ 5.9.3             │ ~5.9.2

Anything else?

No response

Metadata

Metadata

Assignees

No one assigned

    Type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions