-
Notifications
You must be signed in to change notification settings - Fork 27k
Description
Which @angular/* package(s) are relevant/related to the feature request?
forms
Description
Radio input requires FieldTree<string>
With the new signal forms API, it is currently only possible to bind FieldTree<string> to <input type="radio" />. This effectively means all radio options in the form model must be strings.
For example:
@Component({
selector: 'app-radio-group',
imports: [Field],
template: `
<fieldset>
<legend>Do you like the new signal forms API?</legend>
<div>
<input id="option-yes" type="radio" [field]="questionForm.likeSignalForms" value="true" />
<label for="option-yes">Yes</label>
</div>
<div>
<input id="option-no" type="radio" [field]="questionForm.likeSignalForms" value="false" />
<label for="option-no">No</label>
</div>
</fieldset>
`,
styles: ``,
})
export class RadioGroup {
private readonly questionModel = signal<{ likeSignalForms: 'true' | 'false' }>({
likeSignalForms: 'true',
});
protected readonly questionForm = form(this.questionModel);
}This forces the model to use string literal types.
Lack of type-checking for value on radio inputs
Another related issue: the value binding for radio inputs is not type-checked against the form model. It is currently possible to bind any value, even if it is not part of the model’s union type, and the compiler does not complain.
import { Component, effect, signal } from '@angular/core';
import { Field, form } from '@angular/forms/signals';
@Component({
selector: 'app-radio-group',
imports: [Field],
template: `
<fieldset>
<legend>Do you like the new signal forms API?</legend>
<div>
<input id="option-yes" type="radio" [field]="questionForm.likeSignalForms" [value]="true" />
<label for="option-yes">Yes</label>
</div>
<div>
<input
id="option-no"
type="radio"
[field]="questionForm.likeSignalForms"
[value]="'doesNotExistInModel'"
/>
<label for="option-no">No</label>
</div>
</fieldset>
`,
styles: ``,
})
export class RadioGroup {
private readonly questionModel = signal<{ likeSignalForms: 'true' | 'false' }>({
likeSignalForms: 'true',
});
protected readonly questionForm = form(this.questionModel);
constructor() {
effect(() => {
console.log(this.questionModel().likeSignalForms);
});
}
}Selecting the second radio button sets the model value to doesNotExistInModel, which is not a valid value according to the model type.
Ideally, the template should be type-checked so that the [value] binding for a given radio group must be assignable to the corresponding field type in the model.
Proposed solution
Allow non-string field types for radio inputs
Allow binding a FieldTree<T> where T is not necessarily string (e.g. boolean):
@Component({
selector: 'app-radio-group',
imports: [Field],
template: `
<fieldset>
<legend>Do you like the new signal forms API?</legend>
<div>
<input id="option-yes" type="radio" [field]="questionForm.likeSignalForms" [value]="true" />
<label for="option-yes">Yes</label>
</div>
<div>
<input id="option-no" type="radio" [field]="questionForm.likeSignalForms" [value]="false" />
<label for="option-no">No</label>
</div>
</fieldset>
`,
styles: ``,
})
export class RadioGroup {
private readonly questionModel = signal<{ likeSignalForms: boolean }>({ likeSignalForms: true });
protected readonly questionForm = form(this.questionModel);
}Currently this fails with:
TS2322: Type 'boolean' is not assignable to type 'string'
Type-check [value] against the field type
Make the type of [value] for a given <input type="radio" [field]="..." /> be constrained to the field’s type. For example, if the model says:
signal<{ likeSignalForms: 'true' | 'false' }>(...)then [value] for that radio group should only accept 'true' | 'false', and using 'doesNotExistInModel' should be a compilation error.
This would prevent invalid model states and make signal forms more type-safe and ergonomic.
Alternatives considered
- Use string values and map on submit
Keep the field as'true' | 'false', then map to boolean (or another type) when handling form submission.
Downside: more boilerplate, and the form model doesn’t reflect the real domain types. - Wrap in a custom radio group control
Implement a custom component that internally handles mapping between primitive HTML value strings and richer types.
Downside: duplicates logic that might be better handled by the forms/signals integration itself.
Metadata
Metadata
Assignees
Type
Projects
Status