Which @angular/* package(s) are relevant/related to the feature request?
forms
Description
The new Angular Signal Forms currently implement markAsUntouched() and markAsPristine() methods in the internal FieldNode class, but these methods are not exposed in the public API through the FieldState interface or FieldTree type.
This prevents developers from manually resetting the touched/dirty state of individual fields, which might be for for forms with dependent fields.
Current Behavior:
- Methods exist in
FieldNode class but are not accessible through public API-
- Developers cannot reset field state when implementing dependent field logic
- Only the
reset() method is available, which resets the entire field and its descendants
Expected Behavior:
markAsUntouched() and markAsPristine() methods should be available on field instances
- Developers should be able to reset individual field states programmatically
Use Case:
Forms with conditional/dependent fields where field visibility and validation depend on other field values.
When dependencies change, developers need to reset the touched/pristine state of dependent fields.
Example Scenario:
import { Component, input } from '@angular/core';
import { Field, FieldTree, hidden, required, schema } from '@angular/forms/signals';
import { FormError } from '../form-error/form-error';
export interface GenderIdentity {
gender: '' | 'male' | 'female' | 'diverse';
salutation: string; // e. g. "Mx.", "Dr.", etc.
pronoun: string; // e. g. "they/them"
}
export const initialGenderIdentityState: GenderIdentity = {
gender: '',
salutation: '',
pronoun: '',
};
export const identitySchema = schema<GenderIdentity>((path) => {
hidden(path.salutation, (ctx) => {
return !ctx.valueOf(path.gender) || ctx.valueOf(path.gender) !== 'diverse';
});
hidden(path.pronoun, (ctx) => {
return !ctx.valueOf(path.gender) || ctx.valueOf(path.gender) !== 'diverse';
});
required(path.salutation, {
when: (ctx) => ctx.valueOf(path.gender) === 'diverse',
message: 'Please choose a salutation, when diverse gender selected',
});
required(path.pronoun, {
when: (ctx) => ctx.valueOf(path.gender) === 'diverse',
message: 'Please choose a pronoun, when diverse gender selected',
});
});
@Component({
selector: 'app-identity-form',
imports: [Field, FormError],
template: `
<label>
Gender
<select
name="gender-identity"
[field]="identity().gender"
(change)="maybeUpdateSalutationAndPronoun()"
>
<option value="" selected>Please select</option>
<option value="male">Male</option>
<option value="female">Female</option>
<option value="diverse">Diverse</option>
</select>
</label>
<div class="group-with-gap">
@if (!identity().salutation().hidden()) {
<label
>Salutation
<input type="text" placeholder="e. g. Mx." [field]="identity().salutation" />
<app-form-error [fieldRef]="identity().salutation" />
</label>
} @if (!identity().pronoun().hidden()) {
<label
>Pronoun
<input type="text" placeholder="e. g. they/them" [field]="identity().pronoun" />
<app-form-error [fieldRef]="identity().pronoun" />
</label>
}
</div>
`,
styleUrl: './identity-form.scss',
})
export class IdentityForm {
readonly identity = input.required<FieldTree<GenderIdentity>>();
protected maybeUpdateSalutationAndPronoun() {
const gender = this.identity().gender().value();
if (gender !== 'diverse') {
this.identity().salutation().value.set('');
this.identity().pronoun().value.set('');
} else {
this.identity().salutation().markAsUntouched();
this.identity().salutation().markAsPristine();
this.identity().pronoun().markAsUntouched();
this.identity().pronoun().markAsPristine();
}
}
}
Environment:
- Angular version:
21.0.0-rc.1
- Package:
@angular/forms/signals
Proposed solution
Expose the existing markAsUntouched() and markAsPristine() methods in the public FieldState interface, making them available through FieldTree instances.
The implementation already exists in FieldNode class and just needs to be added to the public API contract.
Alternatives considered
- Using
reset() method: This resets the entire field and its descendants, which is too broad for the use case of resetting individual field states
- Workarounds: No viable workarounds exist for this specific requirement
- Manual state management: Would require reimplementing form state logic outside of Angular's Signal Forms system
The proposed solution is the most straightforward as it leverages existing, tested implementation and simply exposes it through the public API.
Which @angular/* package(s) are relevant/related to the feature request?
forms
Description
The new Angular Signal Forms currently implement
markAsUntouched()andmarkAsPristine()methods in the internalFieldNodeclass, but these methods are not exposed in the public API through theFieldStateinterface orFieldTreetype.This prevents developers from manually resetting the touched/dirty state of individual fields, which might be for for forms with dependent fields.
Current Behavior:
FieldNodeclass but are not accessible through public API-reset()method is available, which resets the entire field and its descendantsExpected Behavior:
markAsUntouched()andmarkAsPristine()methods should be available on field instancesUse Case:
Forms with conditional/dependent fields where field visibility and validation depend on other field values.
When dependencies change, developers need to reset the touched/pristine state of dependent fields.
Example Scenario:
Environment:
21.0.0-rc.1@angular/forms/signalsProposed solution
Expose the existing
markAsUntouched()andmarkAsPristine()methods in the publicFieldStateinterface, making them available throughFieldTreeinstances.The implementation already exists in
FieldNodeclass and just needs to be added to the public API contract.Alternatives considered
reset()method: This resets the entire field and its descendants, which is too broad for the use case of resetting individual field statesThe proposed solution is the most straightforward as it leverages existing, tested implementation and simply exposes it through the public API.