In an Angular standalone component, why does [(ngModel)] fail with NG8002, and why does an ngModel inside a <form> also need a name?
answer
- NgModel is not a standalone directive
- the component's imports array
- the name is the key in the group
- ngModelOptions standalone: true
- duplicate names share a control
basics
~20 sNgModel is declared in FormsModule, so a standalone component that does not import FormsModule has no ngModel input and the compiler reports NG8002. Inside a form, NgForm registers each ngModel under its name, so a missing name throws unless the control is marked standalone.
solid answer
~40 sThe compiler error `NG8002: Can't bind to 'ngModel' since it isn't a known property of 'input'` means no directive with an `ngModel` input is in scope. `NgModel` is not a standalone directive; it is declared in `FormsModule`, so the component must list `FormsModule` in its `imports` (or the NgModule must import it). The second rule comes from `NgForm`: once a `<form>` has an `NgForm`, every `ngModel` inside registers its `FormControl` into the form's `FormGroup`, and the `name` attribute is the key. Without it, development builds throw `NG01352`, telling you to add a `name` or set `[ngModelOptions]="{standalone: true}"`, which keeps the control out of the form. Two inputs with the same `name` in one form share a single control.
code
ts · 20 linesimport {Component} from '@angular/core';
import {FormsModule} from '@angular/forms';
@Component({
selector: 'app-signup-row',
imports: [FormsModule], // without this: NG8002 on [(ngModel)]
template: `
<form>
<input name="email" [(ngModel)]="email" />
<label>
<input type="checkbox" [(ngModel)]="showMore" [ngModelOptions]="{standalone: true}" />
Show more options
</label>
</form>
`,
})
export class SignupRow {
email = '';
showMore = false;
}go deeper
Remember the two fixes: import FormsModule into the component, and give every ngModel inside a form a unique name.
Explain why: NgModel lives in FormsModule, and NgForm keys each registered control by its name, which is why standalone: true opts out.
Spot the silent failures too: duplicate names that share one control, and standalone options that drop a field from the submitted value.
Standardise form scaffolding so these errors never reach review, for example with a shared template or lint rule for names.
## The first error: the directive is not in scope Angular binds `[ngModel]` (and the two-way form `[(ngModel)]`) only when a directive declaring an `ngModel` input matches the element. That directive is **`NgModel`**, and it is **not a standalone directive**: it is declared and exported by **`FormsModule`**. In a standalone component, which is the default since v19, the template can only use what the component's `imports` array brings in. Forget `FormsModule` and the template type-checker reports: ```text NG8002: Can't bind to 'ngModel' since it isn't a known property of 'input'. ``` The fix is one line: `imports: [FormsModule]` on the component, or `imports: [FormsModule]` on the NgModule that declares a non-standalone component. Importing `ReactiveFormsModule` does **not** help, because it does not export `NgModel`. ## The second error: every control needs a key `FormsModule` also puts an **`NgForm`** directive on every `<form>` element (unless it carries `ngNoForm`). `NgForm` owns a `FormGroup`, and every `NgModel` inside it registers its `FormControl` into that group **under its `name`**. The name becomes the property in `form.value`, the key in `form.controls`, and the path used for errors. If an `ngModel` sits inside a form with no name, a development build throws **`NG01352`** with the message: *"If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as 'standalone' in ngModelOptions."* ## The three ways out 1. **Add a `name`**: `<input name="email" [(ngModel)]="email">`. This is the normal case. 2. **Mark it standalone**: `[ngModelOptions]="{standalone: true}"` keeps the control out of the form, so it does not affect `form.value` or validity. Useful for a "show advanced options" checkbox that is UI state rather than data. 3. **Supply the name through options**: `[ngModelOptions]="{name: 'user'}"` for a custom control whose own `name` input means something else. ## Name collisions are silent `FormGroup.registerControl()` returns the **existing** control when the name is already taken. So two inputs with `name="email"` in the same form do not produce two controls; they **share one**. Typing in either updates both. Radio buttons rely on exactly this (one control, one value for the whole set), but for anything else a copy-pasted duplicate name is a subtle bug: the form reports one field while the page shows two. ## Quick diagnosis table | Symptom | Cause | Fix | |---|---|---| | `NG8002 ... 'ngModel' ... 'input'` at compile time | `FormsModule` missing from `imports` | import `FormsModule` | | `NG01352` at runtime in development | `ngModel` in a form without `name` | add `name` or `standalone: true` | | two inputs mirror each other | duplicate `name` in one form | give each field a unique name | | a field is missing from `form.value` | `ngModelOptions` marks it standalone | remove the option if it is data | ## Habits that prevent both errors - Import `FormsModule` in every standalone component whose template uses `ngModel`, `ngForm` or `ngModelGroup`; a shared "form fields" component does not import it for its parents. - Use the **same string** for the `name` attribute and the model property, so `form.value` keys match your data object. - Treat `standalone: true` as a deliberate signal that a control is UI state, and review any use of it. - Search for duplicate `name` values when two inputs seem to "echo" each other. - Remember that the name check runs only in development builds; production will not tell you about a missing name, it will just produce an odd value. ## Why Angular insists on the name Reactive forms name controls in code (`formControlName="email"`), so the template only points at an existing key. In template-driven forms the template **is** the model definition, and HTML's `name` attribute is the natural place to put the key. Keeping it required prevents anonymous controls whose values would vanish from the submitted object.
- Why doesn't importing ReactiveFormsModule make [(ngModel)] work?`ReactiveFormsModule` exports the reactive directives (`formControl`, `formGroup`, `formControlName` and friends) and the shared value accessors, but not `NgModel`, `NgForm` or `NgModelGroup`. Those three are the template-driven directives and come only from `FormsModule`.
- Does an ngModel outside any <form> need a name?No. With no parent form container, `NgModel` treats itself as standalone: it creates and manages its own `FormControl` and never registers anywhere, so there is no group that needs a key. The name requirement exists only because `NgForm` or `ngModelGroup` builds a group from the names.
saying these in an interview costs you the question
- NG8002 means the component property is not declared
- Importing ReactiveFormsModule also enables ngModel
- NgModel can be imported directly as a standalone directive
- Two inputs with the same name create two separate controls
- An ngModel outside any form also needs a name attribute