fix(forms): warn in dev mode when ngModel cannot reach parent NgForm across component boundary by arturovt · Pull Request #69585 · angular/angular · GitHub
Skip to content

fix(forms): warn in dev mode when ngModel cannot reach parent NgForm across component boundary - #69585

Merged
thePunderWoman merged 1 commit into
angular:mainfrom
arturovt:fix/forms_47580
Aug 17, 2026
Merged

thePunderWoman merged 1 commit into
angular:mainfrom
arturovt:fix/forms_47580

Conversation

@arturovt

Copy link
Copy Markdown
Contributor

NgModel injects ControlContainer with @host(), which stops the injector at the component host element boundary. When NgForm lives in a parent component and ngModel lives in a child component, the injection returns null silently and the control acts standalone — never registering with the form.

To surface this invisible failure, emit a dev-mode warning (NG01354) when ngModel's @host() injection finds nothing but the element Injector can still reach a ControlContainer further up the hierarchy. The warning identifies the cross-boundary issue and points developers to the viewProviders fix or the standalone option.

Adds the NG01354 reference page explaining why the warning fires and providing two remediation paths: bridging ControlContainer via viewProviders, or opting out with [ngModelOptions]="{standalone: true}".

Fixes #47580

@pullapprove
pullapprove Bot requested a review from JeanMeche June 30, 2026 14:28
@ngbot ngbot Bot added this to the Backlog milestone Jun 30, 2026
Comment thread adev/src/content/reference/errors/NG01354.md Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happens if we don't have a NgForm as parent but a FormGroup ?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Valid concert, updated the code.

@JeanMeche JeanMeche left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AGENT : Here are two prominent cases where the change would erroneously log a warning (false positives):

1. Explicitly standalone ngModel with [ngModelOptions]="{standalone: true}"

If a developer explicitly opts out of form registration using [ngModelOptions]="{standalone: true}", the warning will still be logged.
This occurs because the warning logic evaluates synchronously inside the NgModel constructor. During instantiation, @Host() parent resolves to null, and the constructor uses injector?.get(ControlContainer) to find the parent form and logs the warning. However, Angular @Input() bindings (such as ngModelOptions) are not initialized until after the constructor runs. The directive has no way of knowing the developer explicitly passed {standalone: true}, leading to a frustrating developer experience where the warning instructs them to apply a fix they have already applied.

2. Internal ngModel usage within a custom ControlValueAccessor

A very common pattern is creating a custom form component that implements ControlValueAccessor and using [(ngModel)] internally in its template to easily bind to the native input element.
If a user drops this custom component inside a <form>, the inner ngModel's constructor will run. Its @Host() boundary correctly stops at the custom component (parent === null), but injector.get(ControlContainer) traverses up the element injector tree and successfully finds the NgForm attached to the parent's <form> tag. The warning fires, assuming the developer accidentally disconnected the control, but the developer intentionally kept the inner ngModel isolated as an internal implementation detail of their custom component. Furthermore, because of the flaw in Case 1, the component author cannot even suppress this warning by adding standalone: true to the inner template.

@JeanMeche JeanMeche left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AGENT: Great addition for developer ergonomics (#47580)! I have left a couple of minor inline suggestions regarding DI fallback type naming for sub-groups and doc code block formatting for your consideration.

Comment thread packages/forms/src/directives/ng_model.ts
Use the same class that the warning names — `NgForm` for template-driven forms (`<form>`), or
`FormGroupDirective` for reactive forms (`[formGroup]`):

```typescript

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AGENT: Per Angular documentation guidelines, code blocks containing inline Angular templates should use angular-ts instead of typescript so that template syntax inside @Component definitions is properly highlighted in adev.

Also, consider capitalizing the first word after the colon in headings (e.g. ### Option 1: Bridge the ControlContainer with viewProviders).

@arturovt

Copy link
Copy Markdown
Contributor Author

@JeanMeche addressed comments 🔝

…across component boundary

NgModel injects ControlContainer with @host(), which stops the injector at
the component host element boundary. When NgForm lives in a parent component
and ngModel lives in a child component, the injection returns null silently
and the control acts standalone — never registering with the form.

To surface this invisible failure, emit a dev-mode warning (NG01354) when
ngModel's @host() injection finds nothing but the element Injector can still
reach a ControlContainer further up the hierarchy. The warning identifies the
cross-boundary issue and points developers to the viewProviders fix or the
standalone option.

Adds the NG01354 reference page explaining why the warning fires and providing
two remediation paths: bridging ControlContainer via viewProviders, or opting
out with [ngModelOptions]="{standalone: true}".

Fixes angular#47580
@pullapprove
pullapprove Bot requested review from JeanMeche and crisbeto August 16, 2026 19:44
@pullapprove
pullapprove Bot requested a review from kirjs August 16, 2026 19:44

@JeanMeche JeanMeche left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reviewed-for: public-api

@JeanMeche
JeanMeche removed request for crisbeto and kirjs August 16, 2026 19:45
@JeanMeche JeanMeche added action: merge The PR is ready for merge by the caretaker target: patch This PR is targeted for the next patch release labels Aug 16, 2026
@thePunderWoman
thePunderWoman merged commit 38d0932 into angular:main Aug 17, 2026
26 of 27 checks passed
@thePunderWoman

Copy link
Copy Markdown
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

action: merge The PR is ready for merge by the caretaker area: forms target: patch This PR is targeted for the next patch release

Projects

None yet

Development

Successfully merging this pull request may close these issues.

NgModel fails to register to NgForm when in a child component

4 participants