Skip to content

Commit f20103a

Browse files
crisbetoatscott
authored andcommitted
fix(compiler-cli): report invalid bindings on form controls
Adds validation that users don't bind to unsupported properties on nodes with the `Field` directive.
1 parent 948e2f4 commit f20103a

File tree

5 files changed

+51
-0
lines changed

5 files changed

+51
-0
lines changed

goldens/public-api/compiler-cli/error_code.api.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ export enum ErrorCode {
5353
DUPLICATE_DECORATED_PROPERTIES = 1012,
5454
DUPLICATE_VARIABLE_DECLARATION = 8006,
5555
FORBIDDEN_REQUIRED_INITIALIZER_INVOCATION = 8118,
56+
FORM_FIELD_UNSUPPORTED_BINDING = 8022,
5657
HOST_BINDING_PARSE_ERROR = 5001,
5758
HOST_DIRECTIVE_COMPONENT = 2015,
5859
HOST_DIRECTIVE_CONFLICTING_ALIAS = 2018,

packages/compiler-cli/src/ngtsc/diagnostics/src/error_code.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -436,6 +436,9 @@ export enum ErrorCode {
436436
*/
437437
DEFER_TRIGGER_MISCONFIGURATION = 8021,
438438

439+
/** Raised when the user has an unsupported binding on a `Field` directive. */
440+
FORM_FIELD_UNSUPPORTED_BINDING = 8022,
441+
439442
/**
440443
* A two way binding in a template has an incorrect syntax,
441444
* parentheses outside brackets. For example:

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

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,11 @@ export interface OutOfBandDiagnosticRecorder {
237237
| TmplAstInteractionDeferredTrigger
238238
| TmplAstViewportDeferredTrigger,
239239
): void;
240+
241+
/**
242+
* Reports an unsupported binding on a form `Field` node.
243+
*/
244+
formFieldUnsupportedBinding(id: TypeCheckId, node: TmplAstBoundAttribute): void;
240245
}
241246

242247
export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecorder {
@@ -851,6 +856,19 @@ export class OutOfBandDiagnosticRecorderImpl implements OutOfBandDiagnosticRecor
851856
),
852857
);
853858
}
859+
860+
formFieldUnsupportedBinding(id: TypeCheckId, node: TmplAstBoundAttribute): void {
861+
this._diagnostics.push(
862+
makeTemplateDiagnostic(
863+
id,
864+
this.resolver.getTemplateSourceMapping(id),
865+
node.sourceSpan,
866+
ts.DiagnosticCategory.Error,
867+
ngErrorCode(ErrorCode.FORM_FIELD_UNSUPPORTED_BINDING),
868+
`Binding to '${node.name}' is not allowed on nodes using the [field] directive`,
869+
),
870+
);
871+
}
854872
}
855873

856874
function makeInlineDiagnostic(

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

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -745,6 +745,26 @@ class TcbGenericDirectiveTypeWithAnyParamsOp extends TcbDirectiveTypeOpBase {
745745
* A `TcbOp` which constructs an instance of the signal forms `Field` directive.
746746
*/
747747
class TcbFieldDirectiveTypeOp extends TcbOp {
748+
// Should be kept in sync with the `FormUiControl` bindings,
749+
// defined in `packages/forms/signals/src/api/control.ts`.
750+
private invalidBindingNames = new Set([
751+
'value',
752+
'checked',
753+
'errors',
754+
'invalid',
755+
'disabled',
756+
'disabledReasons',
757+
'name',
758+
'readonly',
759+
'touched',
760+
'max',
761+
'maxLength',
762+
'min',
763+
'minLength',
764+
'pattern',
765+
'required',
766+
]);
767+
748768
constructor(
749769
private tcb: Context,
750770
private scope: Scope,
@@ -760,6 +780,14 @@ class TcbFieldDirectiveTypeOp extends TcbOp {
760780
}
761781

762782
override execute(): ts.Identifier {
783+
const inputs = this.node instanceof TmplAstHostElement ? this.node.bindings : this.node.inputs;
784+
785+
for (const input of inputs) {
786+
if (input.type === BindingType.Property && this.invalidBindingNames.has(input.name)) {
787+
this.tcb.oobRecorder.formFieldUnsupportedBinding(this.tcb.id, input);
788+
}
789+
}
790+
763791
const refType = this.tcb.env.referenceType(this.dir.ref);
764792

765793
if (!ts.isTypeReferenceNode(refType)) {

packages/compiler-cli/src/ngtsc/typecheck/testing/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1029,6 +1029,7 @@ export class NoopOobRecorder implements OutOfBandDiagnosticRecorder {
10291029
illegalForLoopTrackAccess(): void {}
10301030
inaccessibleDeferredTriggerElement(): void {}
10311031
controlFlowPreventingContentProjection(): void {}
1032+
formFieldUnsupportedBinding(): void {}
10321033
illegalWriteToLetDeclaration(id: TypeCheckId, node: AST, target: TmplAstLetDeclaration): void {}
10331034
letUsedBeforeDefinition(
10341035
id: TypeCheckId,

0 commit comments

Comments
 (0)