Property Binding in Angular
Property binding lets you set the value of a DOM element property or a child component's @Input property from your component's TypeScript class. The square-bracket syntax [property]="expression" tells Angular to evaluate the right-hand side as a live TypeScript expression rather than a static string.
Property binding is a one-way flow: data travels from the component class to the template.
Basic Syntax
@Component({
selector: 'app-image',
standalone: true,
template: `
<!-- Without binding (static string) -->
<img src="logo.png" alt="Logo" />
<!-- With binding (dynamic expression) -->
<img [src]="logoUrl" [alt]="logoAlt" />
`,
})
export class ImageComponent {
logoUrl = '/assets/logo.png';
logoAlt = 'Company Logo';
}
Binding to DOM Properties
Almost any DOM property can be bound. The property name on the left is the JavaScript DOM property name, not the HTML attribute name (they usually match but not always).
@Component({
selector: 'app-form-demo',
standalone: true,
template: `
<!-- Boolean properties -->
<button [disabled]="isSubmitting">Submit</button>
<input [readonly]="isReadOnly" [value]="defaultValue" />
<!-- String properties -->
<a [href]="profileUrl">View Profile</a>
<img [src]="avatarUrl" [alt]="userName + ' avatar'" />
<!-- Numeric properties -->
<progress [value]="uploadProgress" [max]="100"></progress>
<!-- innerHTML (use with caution) -->
<div [innerHTML]="trustedHtml"></div>
`,
})
export class FormDemoComponent {
isSubmitting = false;
isReadOnly = true;
defaultValue = 'John Doe';
profileUrl = '/profile/123';
avatarUrl = 'https://example.com/avatar.jpg';
userName = 'Jane';
uploadProgress = 65;
trustedHtml = '<strong>Formatted</strong> content';
}
Binding to Component @Input Properties
Property binding is also how you pass data to child components. The child declares an @Input() property; the parent binds to it with square brackets.
// progress-bar.component.ts (child)
import { Component, Input } from '@angular/core';
@Component({
selector: 'app-progress-bar',
standalone: true,
template: `
<div class="progress-track">
<div
class="progress-fill"
[style.width.%]="value"
[class.complete]="value >= 100"
></div>
</div>
<span>{{ value }}%</span>
`,
})
export class ProgressBarComponent {
@Input() value = 0;
@Input() label = '';
}
// parent component
@Component({
selector: 'app-dashboard',
standalone: true,
imports: [ProgressBarComponent],
template: `
<app-progress-bar [value]="downloadProgress" [label]="'Download'" />
<app-progress-bar [value]="uploadProgress" [label]="'Upload'" />
`,
})
export class DashboardComponent {
downloadProgress = 72;
uploadProgress = 45;
}
Attribute Binding vs Property Binding
HTML attributes initialise the DOM; JavaScript properties are the live state. They often share names but not always. When a property does not exist on the DOM element, use [attr.name].
Situation | Syntax | Example |
|---|---|---|
DOM property exists | [property] | [disabled]="bool" |
HTML attribute only (no matching property) | [attr.name] | [attr.colspan]="3" |
ARIA attributes | [attr.aria-*] | [attr.aria-label]="label" |
SVG attributes | [attr.name] | [attr.viewBox]="vb" |
Data attributes | [attr.data-*] | [attr.data-id]="id" |
<!-- colspan has no corresponding DOM property — use attr. --> <td [attr.colspan]="columnSpan">Merged</td> <!-- ARIA attributes --> <button [attr.aria-expanded]="isOpen" [attr.aria-label]="buttonLabel"> Toggle </button> <!-- SVG viewBox --> <svg [attr.viewBox]="viewBox"> <circle [attr.cx]="cx" [attr.cy]="cy" [attr.r]="radius" /> </svg>
Class Binding
Angular offers three syntaxes for binding CSS classes dynamically.
@Component({
selector: 'app-status',
standalone: true,
template: `
<!-- Single class toggle -->
<div [class.active]="isActive">Single toggle</div>
<!-- Multiple classes with object map -->
<div [ngClass]="{
'btn-primary': isPrimary,
'btn-lg': isLarge,
'disabled': !isEnabled
}">Multi-class</div>
<!-- Classes from an array -->
<div [ngClass]="['card', cardType, cardSize]">Array classes</div>
<!-- Classes from a computed string -->
<div [class]="computedClasses">Dynamic classes</div>
`,
})
export class StatusComponent {
isActive = true;
isPrimary = true;
isLarge = false;
isEnabled = true;
cardType = 'card-outlined';
cardSize = 'card-lg';
get computedClasses(): string {
return [
'base-class',
this.isActive ? 'active' : 'inactive',
].join(' ');
}
}
Style Binding
Inline styles can be bound using [style.property] or the object-map form [ngStyle].
@Component({
selector: 'app-theme-demo',
standalone: true,
template: `
<!-- Single style property -->
<p [style.color]="textColor">Colored text</p>
<p [style.font-size.px]="fontSize">Sized text</p>
<div [style.width.%]="widthPercent">Width percent</div>
<!-- Multiple styles via object map -->
<div [ngStyle]="{
'background-color': bgColor,
'border-radius.px': borderRadius,
opacity: opacity
}">Styled box</div>
`,
})
export class ThemeDemoComponent {
textColor = '#d63384';
fontSize = 18;
widthPercent = 75;
bgColor = '#f8f9fa';
borderRadius = 8;
opacity = 0.9;
}
One-Time vs Reactive Bindings
All property bindings are reactive by default: Angular re-evaluates the expression on every change detection cycle. If a value will never change after initialisation, you can signal this with a one-time binding using the @ prefix (not standard Angular, but the pattern exists in templates via signals).
In modern Angular, signals make this explicit:
import { Component, signal, computed } from '@angular/core';
@Component({
selector: 'app-signal-binding',
standalone: true,
template: `
<!-- Signal-based property binding — only re-renders when signal changes -->
<img [src]="avatarUrl()" [alt]="userName()" />
<p [style.color]="themeColor()">{{ greeting() }}</p>
`,
})
export class SignalBindingComponent {
userName = signal('Alice');
avatarUrl = signal('/assets/alice.png');
themeColor = signal('#007bff');
greeting = computed(() => `Hello, ${this.userName()}!`);
}
Common Property Binding Patterns
Binding | Purpose |
|---|---|
[src]="url" | Dynamic image source |
[href]="link" | Dynamic anchor link |
[disabled]="bool" | Enable / disable form controls |
[hidden]="bool" | Hide element (keeps in DOM) |
[value]="val" | Set input value |
[checked]="bool" | Checkbox state |
[selected]="bool" | Option selected state |
[placeholder]="text" | Input placeholder |
[class.name]="bool" | Toggle a CSS class |
[style.prop]="val" | Set one inline style |
[attr.x]="val" | Set arbitrary HTML attribute |
Summary
Square brackets [property]="expr" evaluate expr as TypeScript — not a plain string.
Bind any DOM property: src, href, disabled, value, checked, innerHTML, etc.
Bind to child @Input properties the same way.
Use [attr.name] for attributes that have no DOM property equivalent (colspan, aria-*, SVG).
Use [class.name] or [ngClass] to toggle CSS classes dynamically.
Use [style.prop] or [ngStyle] to set inline styles dynamically.
Signals make property bindings fine-grained and efficient in Angular 17+.