Angular Templates
By Syncfusion®, Inc. · Last updated
How a template connects HTML to the component class: interpolation, property, attribute, class and style binding, event and two-way binding, expressions and statements, reference variables, ng-container and ng-template.
What Are Angular Templates?
An Angular template is the HTML of a component, extended with Angular syntax that connects the HTML to the component's class.
The class holds the data and the methods. The template reads that data, displays it, and calls the methods when the user interacts with the page. When the data changes, Angular updates the HTML automatically.
Real-World Example
On an order-tracking page, the template displays the order number and status from the class, colors the status badge red or green, and disables the Cancel button once the order has shipped.
Clicking Cancel calls a method in the class. No code in the class touches the DOM directly.
Classdata and methodsTemplateHTML on screen{{ value }} interpolation[property] property binding(event) event binding[(ngModel)] two-way binding
Square brackets send data into the page. Round brackets send events back to the class.
Template Syntax Overview
Angular adds a small set of symbols to HTML. Each symbol tells Angular which way the data moves.
| Syntax | Name | Data direction | Example |
|---|---|---|---|
{{ }} | Interpolation | Class → template (text) | {{ userName }} |
[ ] | Property binding | Class → template (element property) | [disabled]="isSaving" |
[attr.x] | Attribute binding | Class → template (HTML attribute) | [attr.aria-label]="label" |
[class.x] | Class binding | Class → template (CSS class) | [class.active]="isActive" |
[style.x] | Style binding | Class → template (inline style) | [style.width.px]="size" |
( ) | Event binding | Template → class | (click)="save()" |
[( )] | Two-way binding | Both directions | [(ngModel)]="email" |
#name | Template reference variable | Inside the template | #searchBox |
A simple way to remember the brackets:
- Square brackets
[ ]send a value into the element. - Round brackets
( )send an event out to the class. - Both
[( )]do both. This shape is often called "banana in a box".
Interpolation
Interpolation inserts a value from the class into the text of the page. You write the value inside double curly braces: {{ value }}.
Angular evaluates the expression between the braces, converts the result to text and places it in the HTML. When the value changes, the text updates.
Interpolation inserts class values into text
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<h3>Order #{{ orderId }}</h3>
<p>Customer: {{ customer }}</p>
<p>Items: {{ items }} · Total: ₹{{ items * price }}</p>
`,
})
export class App {
orderId = 10482;
customer = 'Asha Kumar';
items = 3;
price = 450;
}
Interpolation works only in text and in attribute values written as text. To set a property such as disabled, use property binding.
Interpolation always inserts plain text. If a value contains HTML, such as <b>Sale</b>, Angular displays the tags as text instead of running them. This protects your app from code injected through user data.
Property Binding
Property binding sets a property of an HTML element, or an input of a component, from a value in the class. You write the property name in square brackets: [property]="expression".
The value on the right is an expression, not plain text. [disabled]="isSaving" reads the isSaving property from the class and passes its true or false value to the button.
Property binding controls the disabled state and the image
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<img [src]="avatarUrl" [alt]="userName" width="48" height="48" />
<p>{{ userName }}</p>
<button [disabled]="isSaving">Save profile</button>
<p>Saving: {{ isSaving }}</p>
`,
})
export class App {
userName = 'Ravi';
avatarUrl = 'data:image/svg+xml,%3Csvg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 48 48%22%3E%3Ccircle cx=%2224%22 cy=%2224%22 r=%2224%22 fill=%22%232563eb%22/%3E%3C/svg%3E';
isSaving = true;
}
Change isSaving to false and run the code again. The button becomes clickable.
| Point | Interpolation {{ }} | Property binding [ ] |
|---|---|---|
| Where it writes | Text content | An element or component property |
| Value type | Always converted to text | Keeps its type: boolean, number, object |
| Example | <p>{{ name }}</p> | <button [disabled]="busy"> |
| Use it for | Displaying values | disabled, src, value, hidden, component inputs |
Attribute Binding
Most HTML attributes have a matching DOM property, so property binding works for them. Some attributes have no matching property, for example aria-label, colspan and data-* attributes.
For these, use attribute binding: [attr.name]="expression". Angular calls setAttribute on the element. If the value is null, Angular removes the attribute.
Attribute binding sets ARIA and data attributes
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<button [attr.aria-label]="'Delete ' + fileName" [attr.data-file-id]="fileId">🗑️</button>
<table border="1" cellpadding="6">
<tr><td [attr.colspan]="columns">Quarterly report</td></tr>
<tr><td>Q1</td><td>Q2</td><td>Q3</td></tr>
</table>
`,
})
export class App {
fileName = 'invoice-2026.pdf';
fileId = 77;
columns = 3;
}
The heading cell spans all three columns because colspan is set from the columns property.
Class Binding
Class binding adds or removes CSS classes based on values in the class.
[class.name]="condition"adds one class when the condition istrueand removes it when it isfalse.[class]="expression"sets several classes at once from a string, an array or an object.
Class binding colors each status badge
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<p>Order 1: <span class="badge" [class.ok]="paid" [class.warn]="!paid">{{ paid ? 'Paid' : 'Pending' }}</span></p>
<p>Order 2: <span class="badge" [class]="{ badge: true, ok: false, warn: true }">Pending</span></p>
`,
styles: `
.badge { padding: 2px 10px; border-radius: 10px; font-size: 13px; }
.ok { background: #dcfce7; color: #166534; }
.warn { background: #fef3c7; color: #92400e; }
`,
})
export class App {
paid = true;
}
Style Binding
Style binding sets a single inline CSS property from a value in the class: [style.property]="expression".
You can add a unit to the binding name. [style.width.px]="progress" turns the number 60 into 60px.
Style binding draws a progress bar
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<p>Upload: {{ progress }}%</p>
<div class="track">
<div class="bar" [style.width.%]="progress" [style.background-color]="progress < 100 ? '#2563eb' : '#16a34a'"></div>
</div>
`,
styles: `
.track { width: 240px; height: 10px; background: #e5e7eb; border-radius: 5px; overflow: hidden; }
.bar { height: 100%; }
`,
})
export class App {
progress = 65;
}
Change progress to 100 and run it again. The bar fills up and turns green.
| Point | Class binding | Style binding |
|---|---|---|
| What it changes | The element's CSS classes | One inline style property |
| Where the design lives | In the stylesheet | In the binding itself |
| Best for | Named states: active, error, selected | Values calculated at run time: widths, positions, colors |
Prefer class binding for visual states. The colors and spacing stay in the stylesheet, where they are easier to change. Use style binding when the value is calculated, such as a percentage.
Event Binding
Event binding runs a method in the class when an event happens on an element. You write the event name in round brackets: (event)="statement".
Common events are click, input, change, submit, keyup and blur. Angular also supports key filters, such as (keyup.enter), which fires only when the Enter key is pressed.
To read details of the event, pass $event. It is the browser's native event object.
Event binding calls class methods
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<p>Items in cart: {{ count }}</p>
<button (click)="add()">Add to cart</button>
<button (click)="clear()">Clear</button>
<p><input placeholder="Type and press Enter" (keyup.enter)="onSearch($event)" /></p>
<p>Last search: {{ lastSearch }}</p>
`,
})
export class App {
count = 0;
lastSearch = '(none)';
add() {
this.count = this.count + 1;
}
clear() {
this.count = 0;
}
onSearch(event: Event) {
this.lastSearch = (event.target as HTMLInputElement).value;
}
}
Click the buttons, then type a word in the box and press Enter.
Two-Way Data Binding
Two-way data binding keeps a form field and a class property in sync. When the user types, the property updates. When the property changes in code, the field updates.
The syntax combines both brackets: [(ngModel)]="property". ngModel comes from Angular's forms package, so the component must import FormsModule.
<input>what the user typesdisplayNameclass property(ngModelChange): typing updates the property[ngModel]: the property updates the field
[(ngModel)] keeps the field and the property in sync in both directions.
Two-way binding keeps the input and the class in sync
import { Component } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-root',
imports: [FormsModule],
template: `
<label>Display name: <input [(ngModel)]="displayName" /></label>
<p>Preview: <strong>{{ displayName }}</strong> ({{ displayName.length }} characters)</p>
<button (click)="displayName = 'Guest'">Reset</button>
`,
})
export class App {
displayName = 'Asha';
}
Type in the box. The preview updates on every key press. Click Reset. The class changes the value, and the input follows.
[(ngModel)]="displayName" is a short form of two bindings:
<input [ngModel]="displayName" (ngModelChange)="displayName = $event" />
If you forget imports: [FormsModule], Angular reports NG0303: Can't bind to 'ngModel' since it isn't a known property of 'input'.
Template Expressions
A template expression is the JavaScript-like code inside {{ }} or on the right side of a property binding. Angular runs it to produce a value.
Expressions can read class properties, call methods, do arithmetic, compare values and use the conditional operator ? :.
Expressions calculate values for the template
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<p>Subtotal: ₹{{ price * quantity }}</p>
<p>Shipping: {{ price * quantity >= 999 ? 'Free' : '₹49' }}</p>
<p>Discount code: {{ code.toUpperCase() }}</p>
<p>Total items: {{ cartSize() }}</p>
`,
})
export class App {
price = 349;
quantity = 3;
code = 'save10';
cart = ['Keyboard', 'Mouse', 'Cable'];
cartSize() {
return this.cart.length;
}
}
Expressions have rules:
- No assignments.
{{ total = 5 }}is not allowed. - No
new,++or--. - Keep them fast. Angular may run an expression many times, so avoid heavy work inside it.
- No side effects. An expression should only read data, never change it.
Template Statements
A template statement is the code on the right side of an event binding. Angular runs it when the event fires.
Unlike expressions, statements are allowed to change data. They usually call a method or assign a value.
| Point | Template expression | Template statement |
|---|---|---|
| Where it appears | {{ }} and [property]="..." | (event)="..." |
| When it runs | Every time Angular checks the view | Only when the event fires |
| Can change data | No | Yes |
| Typical example | price * quantity | save() or count = 0 |
Statements change data when an event fires
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<p>Notifications: {{ unread }}</p>
<button (click)="unread = unread + 1">Simulate new message</button>
<button (click)="markAllRead(); lastAction = 'Marked as read'">Mark all read</button>
<p>{{ lastAction }}</p>
`,
})
export class App {
unread = 2;
lastAction = '';
markAllRead() {
this.unread = 0;
}
}
A statement can run several actions separated by a semicolon, as the second button shows. For anything longer, move the code into a method.
Template Reference Variables
A template reference variable gives a name to an element inside the template, so other parts of the same template can use it. You declare it with #name on the element.
The variable refers to the DOM element itself. For example, #query on an <input> lets a button read query.value.
A reference variable reads an input without a class property
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<input #tag placeholder="New tag" />
<button (click)="addTag(tag.value); tag.value = ''">Add tag</button>
<p>Tags: {{ tags.join(', ') }}</p>
`,
})
export class App {
tags = ['angular', 'frontend'];
addTag(value: string) {
if (value.trim()) {
this.tags.push(value.trim());
}
}
}
Type a tag and click Add tag. The button reads the input through tag.value and then clears it.
A reference variable is visible only inside the template where it is declared. The class cannot read it directly.
ng-container
<ng-container> is a grouping element that Angular does not render in the final HTML. Only its content appears on the page.
Use it when you need to group elements for a control-flow block or a directive, but an extra <div> would break your layout or your HTML structure. For example, a <tr> may contain only cells, so a wrapper <div> inside it is invalid HTML.
ng-container groups cells without adding an element
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
template: `
<table border="1" cellpadding="6">
<tr>
<th>Plan</th>
<ng-container>
<th>Price</th>
<th>Users</th>
</ng-container>
</tr>
<tr>
<td>Team</td>
@if (showDetails) {
<ng-container>
<td>₹999</td>
<td>10</td>
</ng-container>
}
</tr>
</table>
`,
})
export class App {
showDetails = true;
}
Open the browser's developer tools on a real project and you will see no ng-container element in the page. Only the <th> and <td> cells remain.
ng-template
<ng-template> defines a block of HTML that Angular does not render by itself. The block stays hidden until your code tells Angular where and when to render it.
You give the template a reference variable, such as #loading, and render it with NgTemplateOutlet or use it in a control-flow block. This lets you define one piece of HTML and display it in several places.
ng-template defines reusable HTML that renders only on request
import { Component } from '@angular/core';
import { NgTemplateOutlet } from '@angular/common';
@Component({
selector: 'app-root',
imports: [NgTemplateOutlet],
template: `
<ng-template #emptyState>
<p class="empty">📭 Nothing here yet.</p>
</ng-template>
<h4>Inbox</h4>
<ng-container *ngTemplateOutlet="emptyState" />
<h4>Archive</h4>
<ng-container *ngTemplateOutlet="emptyState" />
`,
styles: `.empty { color: #6b7280; margin: 4px 0 12px; }`,
})
export class App {}
The empty-state message is written once and rendered twice.
| Point | ng-container | ng-template |
|---|---|---|
| Rendered automatically | Yes, its content appears in place | No, only when you render it |
| Adds an element to the page | No | No |
| Main use | Group elements without a wrapper | Define HTML to render later or in several places |
Common Mistakes
| Mistake | What you see | Fix |
|---|---|---|
| Interpolation inside a property binding | [src]="{{ url }}" causes a template parse error | Use one form: [src]="url" |
| Quotes missing around a text value | [label]="Save" looks for a property named Save | Wrap text in inner quotes: [label]="'Save'" |
| Property binding for an ARIA attribute | Can't bind to 'aria-label' | Use attribute binding: [attr.aria-label] |
[(ngModel)] without FormsModule | NG0303: Can't bind to 'ngModel' | Add FormsModule to the component's imports |
Changing data inside {{ }} | A template parse error about assignments | Change data in an event statement or a method |
| Calling a slow method in an expression | The page becomes slow | Store the result in a property instead |
Expecting ng-template to appear | Its content never shows | Render it with *ngTemplateOutlet or a control-flow block |
Check Yourself
Exercises
What to Remember
- A template is HTML plus Angular syntax that connects it to the component class.
{{ }}and[ ]send data from the class to the page.( )sends events from the page to the class.[( )]does both.ng-containergroups content without adding an element.ng-templatedefines HTML that renders only when you ask for it.
Next: Styling Components, how to add CSS to components and control where it applies.