# Angular Templates

Course: [Angular](https://www.devtutorials.ai/learn/angular) · Section: Angular Fundamentals
Source: https://www.devtutorials.ai/learn/angular/fundamentals/templates

> 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.

<figure className="ngc-fig">
<svg viewBox="0 0 640 200" role="img" aria-label="Data moves from the class to the template through interpolation and property binding, events move from the template to the class, and two-way binding moves both ways.">
<defs><marker id="ngc-c1" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-node ngc-solid c-purple"><rect x="20" y="60" width="170" height="110" rx="9" /><text x="105.0" y="113" textAnchor="middle" style={{fontSize:"18px"}}>Class</text><text className="ngc-sub" x="105.0" y="131" textAnchor="middle">data and methods</text></g><g className="ngc-node ngc-solid c-blue"><rect x="450" y="60" width="170" height="110" rx="9" /><text x="535.0" y="113" textAnchor="middle" style={{fontSize:"18px"}}>Template</text><text className="ngc-sub" x="535.0" y="131" textAnchor="middle">HTML on screen</text></g><line className="ngc-link ngc-flow c-green" x1="195" y1="72" x2="445" y2="72" markerEnd="url(#ngc-c1)" /><text className="ngc-title c-green" x="320" y="67" textAnchor="middle" style={{fontSize:"12px",fontFamily:"var(--mono)"}}>&#123;&#123; value &#125;&#125;  interpolation</text><line className="ngc-link ngc-flow c-orange" x1="195" y1="100" x2="445" y2="100" markerEnd="url(#ngc-c1)" /><text className="ngc-title c-orange" x="320" y="95" textAnchor="middle" style={{fontSize:"12px",fontFamily:"var(--mono)"}}>[property]  property binding</text><line className="ngc-link ngc-flow c-red" x1="445" y1="128" x2="195" y2="128" markerEnd="url(#ngc-c1)" /><text className="ngc-title c-red" x="320" y="123" textAnchor="middle" style={{fontSize:"12px",fontFamily:"var(--mono)"}}>(event)  event binding</text><line className="ngc-link ngc-flow c-purple" x1="195" y1="156" x2="445" y2="156" markerEnd="url(#ngc-c1)" /><line className="ngc-link c-purple" x1="445" y1="156" x2="195" y2="156" markerEnd="url(#ngc-c1)" /><text className="ngc-title c-purple" x="320" y="151" textAnchor="middle" style={{fontSize:"12px",fontFamily:"var(--mono)"}}>[(ngModel)]  two-way binding</text>
</svg>
<figcaption>Square brackets send data into the page. Round brackets send events back to the class.</figcaption>
</figure>

## 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.

```angular runnable title="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.

```angular runnable title="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.

```angular runnable title="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 is `true` and removes it when it is `false`.
- `[class]="expression"` sets several classes at once from a string, an array or an object.

```angular runnable title="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`.

```angular runnable title="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.

```angular runnable title="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`.

<figure className="ngc-fig">
<svg viewBox="0 0 640 185" role="img" aria-label="Two-way binding: typing in the input updates displayName, and changing displayName updates the input.">
<defs><marker id="ngc-c2" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0L10 5L0 10z" className="ngc-arrowhead" /></marker></defs><g className="ngc-node ngc-soft c-blue"><rect x="30" y="60" width="200" height="70" rx="9" /><text x="130.0" y="93" textAnchor="middle" style={{fontSize:"16px"}}>&lt;input&gt;</text><text className="ngc-sub" x="130.0" y="111" textAnchor="middle">what the user types</text></g><g className="ngc-node ngc-solid c-purple"><rect x="410" y="60" width="200" height="70" rx="9" /><text x="510.0" y="93" textAnchor="middle" style={{fontSize:"16px"}}>displayName</text><text className="ngc-sub" x="510.0" y="111" textAnchor="middle">class property</text></g><path className="ngc-link ngc-flow c-green" d="M232 80 C 300 40, 340 40, 408 80" markerEnd="url(#ngc-c2)" style={{fill:"none"}} /><path className="ngc-link ngc-flow c-orange" d="M408 112 C 340 152, 300 152, 232 112" markerEnd="url(#ngc-c2)" style={{fill:"none"}} /><text className="ngc-note" x="320" y="34" textAnchor="middle">(ngModelChange): typing updates the property</text><text className="ngc-note" x="320" y="170" textAnchor="middle">[ngModel]: the property updates the field</text>
</svg>
<figcaption>[(ngModel)] keeps the field and the property in sync in both directions.</figcaption>
</figure>

```angular runnable title="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:

```html
<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 `? :`.

```angular runnable title="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` |

```angular runnable title="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`.

```angular runnable title="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.

```angular runnable title="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.

```angular runnable title="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

<div className="ngc-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 |

</div>

## 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-container` groups content without adding an element. `ng-template` defines HTML that renders only when you ask for it.

Next: **Styling Components**, how to add CSS to components and control where it applies.
