← Angular: components, reactivity and application decisions
01 / 10 · 65 MIN

Components, templates and view identity

Compose standalone components, interpret bindings and control list identity. Relate templates to data and events without confusing TypeScript imports, providers and view dependencies.

Anatomy and standalone composition

A component combines a TypeScript class, template and selector through Component decorator metadata. The selector identifies usage in a consumer template; the class name references the TypeScript implementation. In the example, OrderReview imports OrderLine and includes it in the decorator’s imports array.

The two operations have different roles. The TypeScript import makes the class available in the file, while metadata makes the dependency available to the template. Providers belongs to a different contract, dependency injection.

Angular 22 components are standalone by default, but the example declares the option explicitly to make the premise visible.

Interpolation and reactive reads

Double-brace interpolation presents an expression result as text. To read a signal, call its getter: orders() obtains the list and marked() obtains the boolean. Referencing the function without parentheses is not equivalent to reading its value.

Angular tracks signals read in templates and can update the view when values change. Interpolated text should not be confused with an event declaration: placing toggle() in interpolation would evaluate a presentation expression rather than wait for a click. Keep output calculation separate from intent to change state.

Properties, attributes and events

Square brackets bind an expression to a property: [order] supplies the object to the row input and [disabled] on a native button writes the corresponding DOM property. For an attribute without the intended property, use the attr contract, such as [attr.role]; null removes that attribute.

Parentheses declare a listener, such as (click)="reverse()". The $event argument contains the native event in a native listener; component outputs carry their emitted payload. Syntax choice depends on the information destination, not merely the name appearing in quotes.

Control flow with explicit meaning

@if selects content according to expression truthiness, with alternative branches when needed. @for repeats content per item and provides context such as $index and $count; @empty presents an alternative for a collection with no items. It is not a JavaScript loop with break and continue. @switch compares values strictly and has no fallthrough.

Number 2 therefore does not match a string "2" case. Before fixing a view choosing the wrong branch, confirm data type and expression contract instead of adding redundant cases or imagined implicit coercions.

Record identity and view reuse

The example uses track order.id because every order has persistent unique identity. Reversing order changes position while retaining the same record. A random key or repeated total does not meet that contract. $index can suit a static collection that never changes; that premise should not be generalized to a reorderable list.

Documentation adds a nuance: @for prioritizes reuse. If a tracked property changes while the object reference stays the same, Angular can update bindings of the existing view. Do not import a destroy-and-recreate guarantee from another framework without checking it.

Read the example and diagnose the boundary

OrderLine receives order as a required input and retains marked as local state. Toggle changes only that mark. OrderReview creates a new array when reversing while preserving objects and ids.

There is no service call or remote approval decision. If a mark appears next to the wrong order, start with the id and track expression. If the selector is not recognized, start with consumer imports.

These are different problems requiring different evidence. Assessment asks for code interpretation and decisions; it does not require building or running the application.

Compatibility and composition summary

An older component can declare standalone: false and be exported by an NgModule. In that case, the consumer imports the corresponding module instead of treating the class as standalone. templateUrl and styleUrl paths are relative to the component directory, not the browser’s open route.

This path uses Angular 22, with TypeScript as a separate conceptual prerequisite. A project’s compiler version must follow the Angular matrix: the consulted 22.0.x row accepts TypeScript 6.0 and excludes 6.1. Studying a newer TypeScript path does not imply installing that version in an incompatible application.

// Reading example: Angular 22 standalone components; no bootstrap shown.
import {Component, input, signal} from '@angular/core';
type Order = {id: string; title: string};

@Component({
  selector: 'order-line',
  standalone: true,
  template: `<p>{{ order().title }}: {{ marked() ? 'marked' : 'unmarked' }}</p>
    <button type="button" (click)="toggle()">Toggle mark</button>`,
})
export class OrderLine {
  readonly order = input.required<Order>();
  readonly marked = signal(false);
  toggle() { this.marked.update(value => !value); }
}

@Component({
  selector: 'order-review',
  standalone: true,
  imports: [OrderLine],
  template: `<button type="button" (click)="reverse()">Reverse order</button>
    @for (order of orders(); track order.id) {
      <order-line [order]="order" />
    } @empty { <p>No orders</p> }`,
})
export class OrderReview {
  readonly orders = signal<Order[]>([
    {id: 'A7', title: 'First order'}, {id: 'B8', title: 'Second order'},
  ]);
  reverse() { this.orders.update(rows => [...rows].reverse()); }
}
IN PRACTICE

OrderLine has a local mark per instance; OrderReview retains unique ids and reverses an array copy. The binding supplies the object to the row and track order.id retains the record relationship. The mark is only interface state: it saves no approvals and does not modify the received object. The excerpt does not include application bootstrap.

Common pitfalls

Importing the TypeScript class but forgetting component imports; using providers for template dependencies; reading a signal without calling it; using position as identity for reorderable data; expecting fallthrough in @switch.

Related topics: Signals, inputs and output contracts · Dependency injection and service lifetimes · Testing behavior and integration limits

Take this idea with you

Class, metadata and template form a joint contract. Use imports for view dependencies, appropriate bindings for each interaction and persistent ids for data changing position. Check documented semantics before treating track as a reset policy.

Create account

Reference: Standalone component composition · Angular 22; standalone components