Skip to content

Commit 5b55200

Browse files
crisbetoAndrewKushnir
authored andcommitted
fix(compiler-cli): allow value to be set on radio fields
Updates the logic that checks for unsupported bindigns to allow `value` to be set on `radio` controls. (cherry picked from commit a61e01d)
1 parent bd322ca commit 5b55200

File tree

2 files changed

+67
-30
lines changed

2 files changed

+67
-30
lines changed

packages/compiler-cli/src/ngtsc/typecheck/src/type_check_block.ts

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -746,7 +746,28 @@ class TcbGenericDirectiveTypeWithAnyParamsOp extends TcbDirectiveTypeOpBase {
746746
*/
747747
abstract class TcbFieldDirectiveTypeBaseOp extends TcbOp {
748748
/** Bindings that aren't supported on signal form fields. */
749-
private unsupportedBindingFields: Set<string>;
749+
protected readonly unsupportedBindingFields = new Set([
750+
// Should be kept in sync with the `FormUiControl` bindings,
751+
// defined in `packages/forms/signals/src/api/control.ts`.
752+
'value',
753+
'checked',
754+
'errors',
755+
'invalid',
756+
'disabled',
757+
'disabledReasons',
758+
'name',
759+
'readonly',
760+
'touched',
761+
'max',
762+
'maxlength',
763+
'maxLength',
764+
'min',
765+
'minLength',
766+
'minlength',
767+
'pattern',
768+
'required',
769+
'type',
770+
]);
750771

751772
constructor(
752773
protected tcb: Context,
@@ -755,32 +776,6 @@ abstract class TcbFieldDirectiveTypeBaseOp extends TcbOp {
755776
protected dir: TypeCheckableDirectiveMeta,
756777
) {
757778
super();
758-
759-
// Should be kept in sync with the `FormUiControl` bindings,
760-
// defined in `packages/forms/signals/src/api/control.ts`.
761-
const commonUnsupportedNames = [
762-
'value',
763-
'checked',
764-
'errors',
765-
'invalid',
766-
'disabled',
767-
'disabledReasons',
768-
'name',
769-
'readonly',
770-
'touched',
771-
'max',
772-
'maxlength',
773-
'maxLength',
774-
'min',
775-
'minLength',
776-
'minlength',
777-
'pattern',
778-
'required',
779-
'type',
780-
];
781-
782-
// `type` can't be bound, but is allowed as a static attribute.
783-
this.unsupportedBindingFields = new Set(commonUnsupportedNames);
784779
}
785780

786781
override get optional() {
@@ -840,6 +835,23 @@ abstract class TcbFieldDirectiveTypeBaseOp extends TcbOp {
840835
* A `TcbOp` which constructs an instance of the signal forms `Field` directive on a native element.
841836
*/
842837
class TcbNativeFieldDirectiveTypeOp extends TcbFieldDirectiveTypeBaseOp {
838+
private readonly inputType: string | null;
839+
840+
constructor(tcb: Context, scope: Scope, node: DirectiveOwner, dir: TypeCheckableDirectiveMeta) {
841+
super(tcb, scope, node, dir);
842+
843+
this.inputType =
844+
(node instanceof TmplAstElement &&
845+
node.name === 'input' &&
846+
node.attributes.find((attr) => attr.name === 'type')?.value) ||
847+
null;
848+
849+
// Radio control are allowed to set the `value`.
850+
if (this.inputType === 'radio') {
851+
this.unsupportedBindingFields.delete('value');
852+
}
853+
}
854+
843855
protected override getExpectedType(): ts.TypeNode {
844856
if (this.node instanceof TmplAstElement) {
845857
return this.getExpectedTypeFromDomNode(this.node);
@@ -858,9 +870,7 @@ class TcbNativeFieldDirectiveTypeOp extends TcbFieldDirectiveTypeBaseOp {
858870
return this.getUnsupportedType();
859871
}
860872

861-
const inputType = node.attributes.find((attr) => attr.name === 'type')?.value;
862-
863-
switch (inputType) {
873+
switch (this.inputType) {
864874
case 'checkbox':
865875
return ts.factory.createKeywordTypeNode(ts.SyntaxKind.BooleanKeyword);
866876

packages/compiler-cli/test/ngtsc/signal_forms_spec.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,33 @@ runInEachFileSystem(() => {
271271
);
272272
});
273273

274+
it('should allow binding to `value` on radio controls', () => {
275+
env.write(
276+
'test.ts',
277+
`
278+
import {Component, signal} from '@angular/core';
279+
import {Field, form} from '@angular/forms/signals';
280+
281+
@Component({
282+
template: \`
283+
<form>
284+
<input type="radio" value="a" [field]="f">
285+
<input type="radio" value="b" [field]="f">
286+
<input type="radio" value="c" [field]="f">
287+
</form>
288+
\`,
289+
imports: [Field]
290+
})
291+
export class Comp {
292+
f = form(signal('a'), {name: 'test'});
293+
}
294+
`,
295+
);
296+
297+
const diags = env.driveDiagnostics();
298+
expect(diags.length).toBe(0);
299+
});
300+
274301
it('should report unsupported static attributes of a field', () => {
275302
env.write(
276303
'test.ts',

0 commit comments

Comments
 (0)