Every Angular application is a tree of components. Understanding how components and templates work together is the most foundational skill you can build. This is Part 1 of the Mastering Angular series.

What Is a Component?

A component controls a patch of screen called a view. It consists of three things:

  1. A TypeScript class — holds the logic and state
  2. An HTML template — defines what the user sees
  3. CSS styles — scoped styles for the view
import { Component } from '@angular/core';

@Component({
  selector: 'app-hero-card',
  standalone: true,
  template: `
    <div class="card">
      <h2>{{ hero.name }}</h2>
      <p>Power: {{ hero.power }}</p>
      <button (click)="activate()">Activate</button>
    </div>
  `,
  styles: [`
    .card { padding: 1rem; border: 1px solid #ccc; border-radius: 8px; }
  `]
})
export class HeroCardComponent {
  hero = { name: 'Angular', power: 'Reactivity' };

  activate() {
    console.log(`${this.hero.name} activated!`);
  }
}

Data Binding in Templates

Angular templates support four types of data binding that connect your class to your view.

Interpolation ({{ }})

The simplest form — it reads a value from the class and renders it as text.

<p>Welcome, {{ username }}!</p>

Property Binding ([property])

Binds a class property to a DOM element’s property.

<img [src]="profileImageUrl" [alt]="username" />
<button [disabled]="isLoading">Submit</button>

Event Binding ((event))

Listens to DOM events and calls a method in your class.

<button (click)="onSave()">Save</button>
<input (input)="onSearch($event)" />

Two-Way Binding ([(ngModel)])

Syncs a value in both directions — from class to template and back.

<input [(ngModel)]="searchQuery" placeholder="Search..." />
<p>You typed: {{ searchQuery }}</p>

Input & Output: Component Communication

Angular components communicate via @Input() for parent-to-child data flow and @Output() for child-to-parent events.

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-counter',
  standalone: true,
  template: `
    <div>
      <span>{{ count }}</span>
      <button (click)="increment()">+</button>
    </div>
  `
})
export class CounterComponent {
  @Input() count = 0;
  @Output() countChange = new EventEmitter<number>();

  increment() {
    this.count++;
    this.countChange.emit(this.count);
  }
}

And the parent uses it like this:

<app-counter [count]="myCount" (countChange)="myCount = $event" />

Lifecycle Hooks

Components go through a predictable lifecycle. The most important hooks are:

HookWhen it runs
ngOnInitAfter first ngOnChanges, once
ngOnChangesWhen input properties change
ngAfterViewInitAfter the component’s view is initialized
ngOnDestroyJust before the component is destroyed
import { Component, OnInit, OnDestroy } from '@angular/core';

@Component({ selector: 'app-timer', template: `<p>{{ elapsed }}s</p>` })
export class TimerComponent implements OnInit, OnDestroy {
  elapsed = 0;
  private intervalId!: ReturnType<typeof setInterval>;

  ngOnInit() {
    this.intervalId = setInterval(() => this.elapsed++, 1000);
  }

  ngOnDestroy() {
    clearInterval(this.intervalId);
  }
}

Summary

You now have a solid understanding of Angular’s component model. In Part 2, we’ll go deeper into Signals — Angular’s modern reactive state management system — and learn how they replace the need for OnPush change detection and manual subscriptions.

Continue to → Part 2: Signals & Reactivity