@@ -746,7 +746,28 @@ class TcbGenericDirectiveTypeWithAnyParamsOp extends TcbDirectiveTypeOpBase {
746746 */
747747abstract 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 */
842837class 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
0 commit comments