Data Binding

Understand how Angular connects component data with templates using interpolation, property binding, event binding, attribute binding, class and style binding, and two-way binding.

Lesson content

What is Data Binding? Data binding creates a dynamic connection between an Angular component and its template. It allows the template to display component data, update element properties, respond to user actions, and synchronize values. Simple Definition Data Binding = Connection Between Component and Template Data Binding Flow Component Data ↓ Angular Binding ↓ Template or DOM User Event ↓ Angular Binding ↓ Component Logic Main Types of Data Binding Binding Type Syntax Direction Interpolation {{ value }} Component → Template Property binding [property]="value" Component → DOM Event binding (event)="handler()" Template → Component Two-way binding [(value)]="property" Both directions Attribute binding [attr.name]="value" Component → HTML attribute Class binding [class.name]="condition" Component → CSS class Style binding [style.name]="value" Component → Inline style 1. Interpolation Interpolation displays component values as text inside a template. Syntax {{ expression }} Component import { Component, signal } from '@angular/core'; @Component({ selector: 'app-profile', standalone: true, template: ` <h2> Welcome, {{ userName() }} </h2> ` }) export class ProfileComponent { userName = signal('Ajay'); } Output Welcome, Ajay Interpolation Expressions Interpolation can evaluate template expressions. firstName = signal('Ajay'); lastName = signal('Rajpoot'); price = signal(100); quantity = signal(3); <p> {{ firstName() + ' ' + lastName() }} </p> <p> Total: {{ price() * quantity() }} </p> Method Call getFullName() { return 'Ajay Rajpoot'; } <p> {{ getFullName() }} </p> Avoid expensive method calls in templates because they may run repeatedly during template checking. Interpolation Converts Values to Text Interpolation converts the result into a string before rendering it. count = signal(10); isActive = signal(true); <p>{{ count() }}</p> <p>{{ isActive() }}</p> Output 10 true 2. Property Binding Property binding sets a property on an HTML element, component, or directive. Syntax [property]="expression" Image Property Example imageUrl = signal('/images/profile.png'); <img [src]="imageUrl()" alt="Profile" /> Disabled Property Example isSaving = signal(true); <button [disabled]="isSaving()" > Save </button> Property Binding Flow Component Expression ↓ [property] ↓ DOM Property Updated Static Value vs Property Binding Static Value <button disabled> Save </button> The button remains disabled. Dynamic Value <button [disabled]="isSaving()" > Save </button> The button state changes according to the component value. Property Binding to Child Components Property binding passes data to a component input. Child Component import { Component, input } from '@angular/core'; @Component({ selector: 'app-user-card', standalone: true, template: ` <h2>{{ name() }}</h2> ` }) export class UserCardComponent { name = input.required<string>(); } Parent Template <app-user-card [name]="selectedUserName()" /> Flow Parent Component ↓ [name] Binding ↓ Child Input ↓ Child Template Interpolation vs Property Binding Interpolation Property Binding Commonly displays text Sets DOM, component, or directive properties Converts result to a string Preserves the original value type {{ userName() }} [disabled]="isDisabled()" Useful inside text content Useful for booleans, objects, arrays, and other values Important Difference <input value="{{ userName() }}" /> Interpolation converts the value to text. <input [value]="userName()" /> Property binding passes the expression result directly to the property. 3. Event Binding Event binding listens for events and runs component logic. Syntax (event)="statement" Click Event import { Component, signal } from '@angular/core'; @Component({ selector: 'app-counter', standalone: true, template: ` <p>{{ count() }}</p> <button (click)="increment()" > Increment </button> ` }) export class CounterComponent { count = signal(0); increment() { this.count.update( value => value + 1 ); } } Event Binding Flow User Action ↓ DOM Event ↓ (event) Binding ↓ Component Method ↓ State Changes ↓ Template Updates The $event Object Angular provides the current event through the $event variable. <input (input)="updateName($event)" /> updateName(event: Event) { const input = event.target as HTMLInputElement; console.log( input.value ); } Keyboard Event Example <input (keyup)="handleKey($event)" /> handleKey( event: KeyboardEvent ) { console.log(event.key); } Event Modifiers Angular templates can listen for specific keyboard keys. <input (keyup.enter)="search()" /> <input (keydown.escape)="close()" /> Modifier Flow Keyboard Event ↓ Specified Key Matches? ┌──────┴──────┐ │ │ Yes No │ │ ▼ ▼ Run Handler Ignore Custom Component Events Event binding also listens to outputs emitted by child components. Child Component import { Component, output } from '@angular/core'; @Component({ selector: 'app-save-button', standalone: true, template: ` <button (click)="save()" > Save </button> ` }) export class SaveButtonComponent { saved = output<string>(); save() { this.saved.emit( 'User saved' ); } } Parent Template <app-save-button (saved)="handleSaved($event)" /> 4. Two-Way Binding Two-way binding sends data from the component to the template and sends user changes back to the component. Syntax [(property)]="value" The syntax combines: [property] + (event) = [(property)] It is commonly called banana-in-a-box syntax. Two-Way Binding with ngModel Use ngModel to synchronize a native form control with component state. Component import { Component } from '@angular/core'; import { FormsModule } from '@angular/forms'; @Component({ selector: 'app-name-editor', standalone: true, imports: [ FormsModule ], template: ` <input [(ngModel)]="userName" /> <p> Hello {{ userName }} </p> ` }) export class NameEditorComponent { userName = 'Ajay'; } Flow Component Property ↓ Input Value ↓ User Types ↓ ngModel Change ↓ Component Property Two-Way Binding Without ngModel The same behavior can be written using property and event bindings. <input [value]="userName" (input)=" userName = $any($event.target).value " /> The ngModel version provides a cleaner abstraction for form controls. Two-Way Component Binding with model() A child component can expose a model input for two-way binding. Child Component import { Component, model } from '@angular/core'; @Component({ selector: 'app-counter', standalone: true, template: ` <p>{{ count() }}</p> <button (click)="increment()" > Increment </button> ` }) export class CounterComponent { count = model(0); increment() { this.count.update( value => value + 1 ); } } Parent Component import { Component, signal } from '@angular/core'; @Component({ selector: 'app-dashboard', standalone: true, imports: [ CounterComponent ], template: ` <app-counter [(count)]="total" /> <p> Parent total: {{ total() }} </p> ` }) export class DashboardComponent { total = signal(0); } Traditional Two-Way Component Pattern Two-way binding can also be created with an input and an output whose name ends with Change . Child Component import { Component, input, output } from '@angular/core'; @Component({ selector: 'app-rating', standalone: true, template: ` <button (click)="increase()" > {{ value() }} </button> ` }) export class RatingComponent { value = input(0); valueChange = output<number>(); increase() { this.valueChange.emit( this.value() + 1 ); } } Parent Usage <app-rating [(value)]="rating" /> Equivalent Expanded Syntax <app-rating [value]="rating" (valueChange)="rating = $event" /> 5. Attribute Binding Attribute binding sets an HTML attribute rather than a DOM property. Syntax [attr.attributeName]="expression" ARIA Attribute Example isExpanded = signal(false); <button [attr.aria-expanded]="isExpanded()" > Menu </button> Table Attribute Example columnSpan = signal(2); <td [attr.colspan]="columnSpan()" > Total </td> SVG Attribute Example circleRadius = signal(40); <svg> <circle cx="50" cy="50" [attr.r]="circleRadius()" /> </svg> Removing an Attribute When an attribute-binding expression returns null , Angular removes that attribute. <button [attr.aria-label]=" label() || null " > Save </button> Property vs Attribute Binding Property Binding Attribute Binding Sets a live DOM property Sets an HTML attribute [value] [attr.aria-label] Used for normal element behavior Used when no matching property exists Preserves the expression value type Generally represented as attribute text Input Example <input value="Initial" /> The HTML attribute provides the initial value. <input [value]="currentValue()" /> The property binding updates the live input value. 6. Class Binding Class binding dynamically adds or removes CSS classes. Single Class isActive = signal(true); <button [class.active]="isActive()" > Account </button> The active class is present when the expression is truthy. Multiple Classes classes = signal({ active: true, disabled: false, highlighted: true }); <div [class]="classes()" > User </div> Class String classNames = signal( 'card selected' ); <article [class]="classNames()" > Product </article> Static and Dynamic Classes Together <button class="button" [class.primary]="isPrimary()" [class.loading]="isLoading()" > Save </button> 7. Style Binding Style binding sets CSS style properties dynamically. Single Style textColor = signal('red'); <p [style.color]="textColor()" > Important message </p> Style with Unit width = signal(250); <div [style.width.px]="width()" > Panel </div> Percentage Unit progress = signal(75); <div [style.width.%]="progress()" > Progress </div> Multiple Styles styles = signal({ color: 'white', backgroundColor: 'navy', padding: '12px' }); <div [style]="styles()" > Styled content </div> Class Binding vs Style Binding Class Binding Style Binding Adds or removes CSS classes Sets individual inline styles Preferred for reusable styling rules Useful for calculated values [class.active] [style.width.px] Keeps styling in CSS Adds styles directly to the element 8. Binding to Signals Signals work naturally with Angular template bindings. Read a signal by calling it inside the binding expression. import { Component, signal } from '@angular/core'; @Component({ selector: 'app-theme', standalone: true, template: ` <section [class.dark]="isDark()" > <h2> {{ themeName() }} </h2> <button (click)="toggleTheme()" > Change Theme </button> </section> ` }) export class ThemeComponent { isDark = signal(false); themeName = signal('Light'); toggleTheme() { this.isDark.update( value => !value ); this.themeName.set( this.isDark() ? 'Dark' : 'Light' ); } } When a template reads a signal, Angular tracks that dependency and updates the related view when the signal changes. 9. Safe Navigation Operator Angular template expressions support optional chaining for values that may be null or undefined. user = signal<User | null>( null ); <p> {{ user()?.name }} </p> The expression avoids an error when the user value is unavailable. 10. Nullish Coalescing Use ?? to provide a fallback only when a value is null or undefined. <p> {{ user()?.name ?? 'Guest' }} </p> 11. Binding Expressions Angular template expressions support many standard JavaScript operations. {{ price() * quantity() }} {{ user()?.name ?? 'Guest' }} {{ isLoggedIn() ? 'Logout' : 'Login' }} {{ firstName() + ' ' + lastName() }} Keep expressions small and readable. Move complex calculations into computed signals, pipes, or component logic. 12. Complete Binding Example import { Component, computed, signal } from '@angular/core'; import { FormsModule } from '@angular/forms'; @Component({ selector: 'app-product-editor', standalone: true, imports: [ FormsModule ], template: ` <section [class.unavailable]=" !inStock() " > <h2> {{ productName }} </h2> <img [src]="imageUrl()" [alt]="productName" /> <input [(ngModel)]="productName" /> <p> Price: {{ totalPrice() }} </p> <button [disabled]="!inStock()" [attr.aria-label]=" 'Buy ' + productName " (click)="buyProduct()" > Buy </button> </section> ` }) export class ProductEditorComponent { productName = 'Keyboard'; imageUrl = signal( '/images/keyboard.png' ); price = signal(50); quantity = signal(2); inStock = signal(true); totalPrice = computed( () => this.price() * this.quantity() ); buyProduct() { console.log( `Buying ${this.productName}` ); } } One-Way vs Two-Way Binding One-Way Binding Two-Way Binding Data moves in one direction Data moves in both directions Interpolation or property binding Property and change-event combination Event binding moves data back separately Uses [()] shorthand More explicit data flow Useful for editable values Data Binding Direction Component → Template {{ interpolation }} [property]="value" [attr.name]="value" [class.name]="condition" [style.name]="value" Template → Component (event)="handler($event)" Component ↔ Template [(ngModel)]="value" [(componentModel)]="value" Common Mistakes 1. Forgetting Parentheses with Signals Wrong: {{ userName }} Correct: {{ userName() }} 2. Confusing Property and Attribute Binding Use: [disabled]="isDisabled()" Do not use an attribute binding when a normal DOM property exists. 3. Using String Interpolation for Boolean Properties Avoid: <button disabled="{{ isDisabled() }}" > Save </button> Prefer: <button [disabled]="isDisabled()" > Save </button> 4. Forgetting FormsModule The ngModel directive requires FormsModule . 5. Using Expensive Template Methods Avoid: {{ calculateLargeReport() }} Prefer a computed signal, pipe, or pre-calculated property. 6. Mutating Parent Input Data A child component should avoid directly modifying objects received through inputs. 7. Writing Complex Logic in Templates Keep templates declarative. Move complex conditions and transformations into component logic. 8. Trusting Bound HTML Content Avoid binding untrusted content to HTML-related properties. Angular applies security protections to supported binding contexts, but applications must still treat external data as untrusted. Best Practices Use interpolation for text display Use property binding for DOM and component properties Use event binding for user actions Use two-way binding only when both directions are required Use attribute binding for ARIA, SVG, and attributes without matching properties Prefer class binding over repeated inline style logic Use style binding for calculated style values Read signals by calling them in templates Keep binding expressions small and predictable Prefer computed signals for derived values Use strongly typed event handlers Treat all external data as untrusted Interview Questions What is data binding in Angular? Data binding is the mechanism that connects component data and behavior with an Angular template. What are the main types of data binding? The main types are interpolation, property binding, event binding, and two-way binding. Angular also supports specialized attribute, class, and style bindings. What is interpolation? Interpolation displays a component expression as text using double curly braces. {{ userName() }} What is property binding? Property binding sets a DOM, component, or directive property using square brackets. [disabled]="isDisabled()" What is event binding? Event binding listens to DOM or component events using parentheses. (click)="save()" What is two-way binding? Two-way binding combines property binding and event binding so values can move in both directions. [(ngModel)]="userName" What is the difference between property and attribute binding? Property binding updates a live DOM property. Attribute binding sets or removes an HTML attribute. Why is [disabled] preferred over disabled="{{ value }}"? Property binding preserves the boolean value, while interpolation converts the result to text. What is $event? $event contains the event object or custom output value emitted by the current binding. How does model() support two-way component binding? The model() API creates a writable component input that can receive a value from its parent and propagate updates back through [(property)] syntax. Key Point Data binding keeps the component and template connected so state changes update the UI and user events update application logic. Easy Formula Interpolation = {{ value }} = Component → Text Property Binding = [property]="value" = Component → DOM Event Binding = (event)="handler()" = DOM → Component Two-Way Binding = [(property)]="value" = Component ↔ Template Attribute Binding = [attr.name]="value" Class Binding = [class.name]="condition" Style Binding = [style.name]="value" Component State ↓ Binding ↓ Template Updates ↓ User Interaction ↓ Event Binding ↓ Component State Updates