Skip to content

Lifecycle Hooks

Angular components have a lifecycle: they are created, rendered, updated, and eventually destroyed. Angular provides lifecycle hooks — methods you implement on the component class — that run at specific points in this process.

Angular provides a handful of lifecycle hooks. In practice, you will use two almost exclusively:

HookWhen it runs
ngOnInitOnce, after Angular has set all @Input() properties
ngOnDestroyOnce, just before Angular removes the component from the DOM

There are others (ngOnChanges, ngAfterViewInit, etc.) but ngOnInit and ngOnDestroy cover the vast majority of real use cases.

ngOnInit — running code when a component loads

Section titled “ngOnInit — running code when a component loads”

ngOnInit is the right place to fetch data and run any setup logic. It fires after all @Input() values are set, which means you can safely use input values inside it.

In CinemaVault, Home fetches trending and popular movies when it loads:

import { Component, OnInit } from '@angular/core';
import { MovieService } from '../../services/movie.service';
import { Movie } from '../../models/movie.model';
@Component({
selector: 'app-home',
templateUrl: './home.html',
styleUrl: './home.css'
})
export class Home implements OnInit {
trending: Movie[] = [];
popular: Movie[] = [];
loading = true;
constructor(private movieService: MovieService) {}
ngOnInit(): void {
this.movieService.getTrending().subscribe(movies => {
this.trending = movies;
});
this.movieService.getPopular().subscribe(movies => {
this.popular = movies;
this.loading = false;
});
}
}

Notice implements OnInit. This is a TypeScript interface — it ensures your class has the correct ngOnInit signature. Angular does not require it (it uses duck typing), but it helps with type checking and makes intent clear.

You might wonder: why not fetch data in the constructor? Two reasons:

  1. @Input() values are not set yet when the constructor runs. If you use this.movie inside a constructor, it will be undefined.
  2. Constructors should be simple. Angular uses the constructor for dependency injection. Putting side effects (API calls, subscriptions) in the constructor makes testing harder and violates the principle of minimal constructors.

Reserve the constructor for injecting services. Use ngOnInit for everything else.

ngOnDestroy runs just before Angular removes the component. Use it to cancel subscriptions, clear timers, and release any resources the component holds.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Subscription } from 'rxjs';
@Component({ /* ... */ })
export class Browse implements OnInit, OnDestroy {
private sub?: Subscription;
ngOnInit(): void {
this.sub = this.formService.changes$.subscribe(handleChange);
}
ngOnDestroy(): void {
this.sub?.unsubscribe();
}
}

Forgetting to unsubscribe is a common source of memory leaks in Angular apps. ngOnDestroy is where you prevent them.

  1. In your practice project, add implements OnInit to your Greeting component.
  2. Add a ngOnInit() method that sets a property loadedAt to the current time: this.loadedAt = new Date().toLocaleTimeString().
  3. Display loadedAt in the template.
  4. Run the app and confirm the time appears.
  5. Add implements OnDestroy and a ngOnDestroy() that logs 'Greeting destroyed' to the console. Navigate to another route (add a simple second route) and confirm the message logs.
  • Angular components have a lifecycle with hooks that fire at specific moments.
  • ngOnInit fires after inputs are set — use it for data fetching and setup logic.
  • Do not put side effects in the constructor; use it only for dependency injection.
  • ngOnDestroy fires before the component is removed — use it to cancel subscriptions and clear timers.
  • Implement the OnInit and OnDestroy interfaces to get TypeScript type checking on your hook methods.