Two-way binding keeps an HTML element and application data synchronized. A one-way binding displays a component value; an event binding carries user changes back to the component. Combining both lets either side change the other. It is especially useful for form controls. section 11.2 section 13.3
Marking a task complete
A checkbox bound with `[(ngModel)]="item.complete"` displays a task’s completion state and updates that property when toggled. Other bindings that depend on the property update too, including the displayed true/false value and the count of incomplete tasks. A separate toggle can bind to `showComplete` to control whether completed tasks appear. section 2.6 section 2.9
The two directions can be written separately. For an input tied to a `selectedProduct` signal, `(input)` sets the signal from the element’s value, while `[value]` reads the signal into the element. Two inputs using that pair stay synchronized; editing either also updates dependent interpolation and a highlighted table row. section 13.3
The `ngModel` directive replaces that repeated pair on form elements. Write `[(ngModel)]="property"`: the round brackets inside square brackets are nicknamed “banana-in-a-box.” Import `FormsModule` to make the form feature available. Check the brackets if a binding does not behave as expected. section 13.3.1 section 13.0
Entering a product
Bind three inputs to `newProduct.name`, `newProduct.category`, and `newProduct.price` with `[(ngModel)]`. A JSON display makes changes to the object visible while the user types. A separate click binding passes `newProduct` to the Create action; two-way binding handles synchronization, not the button action. section 13.4.1
In Angular 16, `ngModel` does not bind directly to signals. A getter and setter can expose a simple signal value as a regular property, but that approach is awkward for many bindings. For forms that update properties of a data object, use conventional component properties in this setup. section 13.3.1 section 13.4.1
A custom directive can offer the same syntax. An input named `paModel` receives application changes; an output named `paModelChange` emits user changes. Angular’s matching names let `[(paModel)]="newProduct.name"` replace the separate property and event bindings. This example supports input elements rather than the range of controls supported by `ngModel`. section 14.6
Listing 14.19. The contents of the twoway.directive.ts file in the src/app folder
import {
Input, Output, EventEmitter, Directive,
HostBinding, HostListener, SimpleChange
} from "@angular/core";
@Directive({
selector: "input[paModel]"
})
export class PaModel {
@Input("paModel")
modelProperty: string | undefined = "";
@HostBinding("value")
fieldValue: string = "";
ngOnChanges(changes: { [property: string]: SimpleChange }) {
let change = changes["modelProperty"];
if (change.currentValue != this.fieldValue) {
this.fieldValue = changes["modelProperty"].currentValue || "";
}
}
@Output("paModelChange")
update = new EventEmitter<string>();
@HostListener("input", ["$event.target.value"])
updateValue(newValue: string) {
this.fieldValue = newValue;
this.update.emit(newValue);
}
}
The directive listing shows the two directions: `ngOnChanges` updates the host input’s value, and an `input` listener emits the new value through `paModelChange`. section 14.6

