AcademyAngular
Two-Way Binding and Reactive Forms
[(ngModel)] is sugar over a property and event bound together; reactive forms trade that convenience for FormControl, validators and explicit state.
2 min readUpdated 3 exercises
[(ngModel)] is [value] and (event) fused into one syntax
The banana-in-a-box syntax is not a separate mechanism — it desugars to a property binding paired with an event binding on the same control:
<!-- these two lines do the same thing -->
<input [(ngModel)]="name" />
<input [value]="name" (input)="name = $event.target.value" />A field that visually updates but never changes the component's own state is almost always the second form written by hand, incompletely: [value]="name" alone binds one-way, into the input, and without the matching (input) handler nothing ever flows back out. The same applies to a checkbox bound as [checked] with no (change) — it toggles on screen because the browser owns that visual state natively, while the component's boolean sits frozen at whatever it started as.
Reactive forms make every one of those steps explicit
FormControl, wired through [formControl] or formControlName, replaces the implicit two-way wiring with an object the component owns and can call .setValue(), .disable() or attach validators to directly:
email = new FormControl('', [Validators.required, Validators.email]);An email field that "accepts anything" is usually a validator array that was declared but never actually attached to the control the template binds — the FormControl exists, the validators exist, but they were passed to a different instance, or the template still points at a plain ngModel-bound field instead of formControlName.
touched, dirty and disabled are three different questions
control.invalid alone is rarely what a submit button should check — showing an error message the instant a form loads, before anyone has typed anything, means the template checked invalid without also checking touched (has this field been focused and blurred) or dirty (has its value actually changed from the initial one). A submit button that "never disables" is frequently bound to [disabled]="control.invalid" when the control itself was never marked as required in the first place, so invalid never becomes true no matter what the user types.
What interviewers check
Whether you can explain [(ngModel)] as sugar rather than as its own primitive, and whether you reach for touched/dirty correctly when a reactive form's error state needs to distinguish "not filled in yet" from "filled in wrong."
Now practice it
1 quiz · 2 drillsReading this page is the cheap half. These are the exercises that make you use it.
- Two-way Binding with [(ngModel)]Fix a name field and a checkbox that visually respond to input but never actually update the component’s own state.DrillSolve · EasyDrillSolve · Easy
- Reactive Forms — FormControl & ValidatorsFix an email field that accepts anything, a submit button that never disables, and an error shown before the user has typed.DrillSolve · EasyDrillSolve · Easy
- Components & Directives · Lifecycle & ProjectionComponent-level questions covering the lifecycle and state concerns forms sit on top of.QuizTake · MediumQuizTake · Medium
Read next
Signals: signal(), computed() and effect()
signal() holds a value behind a getter/setter pair; computed() derives from it lazily; effect() reacts to it. Angular's answer to Vue's ref or a React hook.
2 min
Template Refs, ViewChild and Outlets
#ref names an element for the template; @ViewChild reaches the same element from the class. ng-template and ngTemplateOutlet turn a template into a value.
2 min