Part 2 — Structuring User Interfaces with Components
Angular applications are built from components — small, isolated units responsible for rendering part of the UI and handling its logic. Each component controls a section of the screen, can receive data, can output events, and participates in a hierarchical tree of parent and child components.
In this chapter you will learn:
You will also see short notes explaining how older Angular versions handled these tasks, so you understand both modern best practice and past conventions.
1. Anatomy of an Angular Component
A component consists of:
<span class="editor-theme-code">.html</span>)
A style sheet (<span class="editor-theme-code">.css</span>)
Example:
import { Component } from '@angular/core';
import { RouterOutlet } from '@angular/router';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrl: './app.component.css',
imports: [RouterOutlet]
})
export class AppComponent {
title = 'World';
}
Key metadata properties
Property | Purpose |
|---|---|
| Tag name used in templates |
/ | Defines the UI |
/ | Defines component CSS |
| Standalone components this one depends on |
Modern Angular (v16+) note
Standalone components are now the standard.
They use the <span class="editor-theme-code">imports</span> array instead of NgModules.
Legacy note
Older projects used NgModules and did not include the <span class="editor-theme-code">imports</span> array in the <span class="editor-theme-code">@Component</span> decorator.
3. Creating a Component with the CLI
Inside your Angular project folder:
ng generate component product-list
This generates:
product-list/
product-list.component.ts
product-list.component.html
product-list.component.css
product-list.component.spec.ts
To use your new component, import it into another component:
import { ProductListComponent } from './product-list/product-list.component';
@Component({
selector: 'app-root',
imports: [RouterOutlet, ProductListComponent],
templateUrl: './app.component.html',
styleUrl: './app.component.css'
})
export class AppComponent {}
And place its selector in the HTML:
<app-product-list />
4. Displaying Data in the Template
Angular templates support multiple ways to bring component data into the UI.
4.1 Interpolation
<h1>Hello, {{ title }}</h1>
4.2 Property Binding
<h1 [innerText]="title"></h1>
Important
Property binding targets DOM properties, not HTML attributes.
5. Angular’s Modern Control Flow Syntax (@if, @for, @switch)
Angular 17 introduced a new built-in control-flow syntax that is:
5.1 @if (conditional rendering)
@if (products.length > 0) {
<h1>Products ({{ products.length }})</h1>
} @else {
<p>No products found!</p>
}
Legacy note (pre-Angular 17)
<span class="editor-theme-code">*ngIf</span> was used:
<h1 *ngIf="products.length > 0">Products</h1>
5.2 @for (looping)
<ul>
@for (product of products; track product.id) {
<li>{{ product.title }}</li>
} @empty {
<p>No products found</p>
}
</ul>
Why <span class="editor-theme-code">track</span> is important
It helps Angular identify which DOM element corresponds to which data item, improving rendering performance.
Legacy note
Older Angular templates used <span class="editor-theme-code">*ngFor</span>:
<li *ngFor="let product of products">{{ product.title }}</li>
5.3 @switch
@switch (product.title) {
@case ('Keyboard') { 🎹 }
@case ('Microphone') { 🎤 }
@default { 📦 }
}
Legacy note
Previously:
<div [ngSwitch]="product.title">
<p *ngSwitchCase="'Keyboard'"></p>
<p *ngSwitchDefault></p>
</div>
6. Adding Interactivity: Events and User Input
To react to user actions, Angular uses event binding.
<li (click)="selectedProduct = product">{{ product.title }}</li>
<span class="editor-theme-code">(click)</span> — target event
The right-hand expression — TypeScript code executed when clicked
All browser events are supported.
7. Styling Components
7.1 Class Binding
<p [class.selected]="isSelected"></p>
You can pass an entire object with boolean conditions:
currentClasses = {
selected: true,
highlighted: false
};
<p [class]="currentClasses"></p>
7.2 Style Binding
<p [style.color]="'red'"></p>
<p [style.width.px]="120"></p>
Object syntax:
currentStyles = {
color: 'red',
width: '100px'
};
<p [style]="currentStyles"></p>
7.3 View Encapsulation
Angular normally isolates component CSS:
encapsulation: ViewEncapsulation.Emulated // default
Other modes:
Mode | Description |
|---|---|
| Scoped CSS (default) |
| CSS leaks globally |
| Uses browser’s native Shadow DOM |
Example turning isolation off:
encapsulation: ViewEncapsulation.None
Use this carefully.
8. Component Communication
Components interact via:
8.1 Passing Data Down (Input Binding)
Child component:
import { Component, input } from '@angular/core';
import { Product } from '../product';
@Component({
selector: 'app-product-detail',
templateUrl: './product-detail.component.html'
})
export class ProductDetailComponent {
product = input<Product>();
}
Parent template:
<app-product-detail [product]="selectedProduct" />
Required inputs
product = input.required<Product>();
8.2 Sending Events Up (Output Binding)
Child:
import { output } from '@angular/core';
added = output<Product>();
addToCart() {
this.added.emit(this.product()!);
}
Parent:
<app-product-detail
[product]="selectedProduct"
(added)="onAdded($event)"
/>
Parent TS:
onAdded(product: Product) {
alert(`${product.title} added to cart!`);
}
Legacy note
In older Angular:
@Input() product!: Product;
@Output() added = new EventEmitter<Product>();
9. Template Reference Variables
Used to access a component or element instance:
<app-product-detail #detail [product]="selectedProduct"></app-product-detail>
<p>{{ detail.product()!.title }}</p>
A reference variable exposes the public API of the component.
10. Accessing Child Components from TypeScript
Instead of using a template variable, we can query the child via <span class="editor-theme-code">viewChild</span>.
Parent component:
import { viewChild, AfterViewInit } from '@angular/core';
import { ProductDetailComponent } from '../product-detail/product-detail.component';
export class ProductListComponent implements AfterViewInit {
productDetail = viewChild(ProductDetailComponent);
ngAfterViewInit(): void {
console.log('Child product:', this.productDetail()?.product());
}
}
Legacy note
Previously:
@ViewChild(ProductDetailComponent) productDetail!: ProductDetailComponent;
11. Change Detection Strategy
Angular refreshes the UI when it detects changes.
Two strategies are available:
Strategy | Behavior |
|---|---|
| Checks all components often |
| Only updates when @Input references change or events occur |
Enable <span class="editor-theme-code">OnPush</span>:
changeDetection: ChangeDetectionStrategy.OnPush
<span class="editor-theme-code">OnPush</span> dramatically improves performance for:
12. Component Lifecycle Hooks
Lifecycle hooks let you run code at specific moments.
Hook | When it runs |
|---|---|
| After inputs are set; component initialized |
| Before component is removed |
| Whenever an input value changes |
| After child components and views are ready |
12.1 ngOnInit
export class ProductDetailComponent implements OnInit {
ngOnInit() {
console.log('Product:', this.product());
}
}
Why not use the constructor?
Inputs are not yet assigned when the constructor runs.
12.2 ngOnDestroy
Used for cleanup:
export class ProductDetailComponent implements OnDestroy {
ngOnDestroy(): void {
console.log('Component destroyed');
}
}
Common use cases:
Alternative: DestroyRef (modern, recommended)
constructor(destroyRef: DestroyRef) {
destroyRef.onDestroy(() => {
// cleanup
});
}
12.3 ngOnChanges
Triggered when an input changes:
ngOnChanges(changes: SimpleChanges): void {
const product = changes['product'];
if (!product.isFirstChange()) {
console.log('Old:', product.previousValue);
console.log('New:', product.currentValue);
}
}
Modern alternative
Signals can track changes more elegantly (Chapter 7).
12.4 ngAfterViewInit
Useful for reading child component data:
ngAfterViewInit() {
console.log(this.productDetail()?.product());
}
13. Summary
In this chapter, you learned:
<span class="editor-theme-code">@if</span>, <span class="editor-theme-code">@for</span>, <span class="editor-theme-code">@switch</span>)
How to display and update data
How to handle events and user interactions
How components communicate (inputs & outputs)
How to use template reference variables
How to manage CSS with view encapsulation
How to optimize updates with change detection
How to hook into the component lifecycle