Angular Lifecycle Hooks
Lifecycle hooks are methods Angular calls at key moments (create, input changes, view init, destroy) so you can set up, react to changes, access template refs, and clean up.
Lifecycle Essentials
- What: Lifecycle hooks are methods Angular calls at key moments (init, input changes, view init, destroy).
- Setup: Use
ngOnInit
after inputs are set. - React: Handle input changes in
ngOnChanges
. - DOM ready: Use
ngAfterViewInit
to access@ViewChild
refs. - Cleanup: Release timers/subscriptions/listeners in
ngOnDestroy
.
import { OnInit, OnDestroy, AfterViewInit, ViewChild, ElementRef } from '@angular/core';
export class Demo implements OnInit, AfterViewInit, OnDestroy {
@ViewChild('box') box!: ElementRef<HTMLInputElement>;
intervalId: any;
ngOnInit() { /* setup after inputs */ }
ngAfterViewInit() { this.box.nativeElement.focus(); }
ngOnDestroy() { clearInterval(this.intervalId); }
}
// Template: <input #box>
Example explained
ngOnInit
: Run setup that needs inputs already bound.@ViewChild
+ngAfterViewInit
: Access and focus the input only after the view is initialized.ngOnDestroy
: Clean up timers/listeners to prevent leaks.
Notes:
- Related: See Components for creating views, Templates for markup and refs, and Directives for structural rendering like
*ngIf
used in examples. - Avoid DOM access in constructors.
Lifecycle Hooks
- Toggle lifecycle: Showing a component (e.g., with
*ngIf
) runsngOnInit
; hiding it runsngOnDestroy
. - Do/undo work: Start timers/subscriptions on init, clear/unsubscribe on destroy.
export class Child implements OnInit, OnDestroy {
intervalId: any;
ngOnInit() { this.intervalId = setInterval(() => {/* ... */}, 1000); }
ngOnDestroy() { clearInterval(this.intervalId); }
// <child-cmp *ngIf="show"></child-cmp>
Example explained
ngOnInit
: Starts a timer when the component is created.ngOnDestroy
: Clears the timer when the component is removed.*ngIf
: Toggling the condition creates/destroys the child, invoking the hooks.
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { CommonModule } from '@angular/common';
{{ ... }}
show = true;
toggle() { this.show = !this.show; }
}
bootstrapApplication(App);
<app-root></app-root>
Example explained
- Toggle: The button flips
show
to add/remove the child. - Hooks in action: Creating the child runs
ngOnInit
; removing it runsngOnDestroy
. - Cleanup: Clearing the interval prevents background work after destroy.
Notes:
- Cleanup required: Clear intervals/timeouts and unsubscribe in
ngOnDestroy
; Use theasync
pipe when possible. - Heavy work in hooks: Avoid expensive work in frequently called hooks (e.g.,
ngOnChanges
); debounce/throttle or defer to services. - Manual listeners: Remove event listeners you add manually on destroy, or keep the cleanup function returned by
Renderer2
.
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component, OnInit, OnDestroy } from '@angular/core';
import { CommonModule } from '@angular/common';
{{ ... }}
export class App {
text = '';
}
bootstrapApplication(App);
<app-root></app-root>
Example explained
- Parent → Child: The input box updates
text
in the parent; the child receives it via[text]
. - Change record: The child stores the last prev and curr values from
SimpleChanges
for display. - Edge cases: Handles transitions like
undefined → value
orvalue → ''
.
Notes:
- Immutable inputs: Replace arrays/objects instead of mutating to ensure
OnChanges
runs. - With OnPush: Input reference changes trigger checks; in-place mutation may not—emit new references from parents.
- Inspect changes: Use
SimpleChanges
to handle edge cases (e.g., undefined → value).
@ViewChild('box') box!: ElementRef<HTMLInputElement>;
ngAfterViewInit() { this.box.nativeElement.focus(); }
Example explained
@Input()
: The child declares an inputtext
that parents can bind to.ngOnChanges(changes)
: ReceivesSimpleChanges
withpreviousValue
andcurrentValue
for each changed input.- Immutable updates: Prefer replacing references over mutating in place to trigger change detection.
Example
import { bootstrapApplication } from '@angular/platform-browser';
import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
import { CommonModule } from '@angular/common';
{{ ... }}
bootstrapApplication(App);
<app-root></app-root>
Example explained
- Multiple refs: Reads both the input
box
and thepanel
container via@ViewChild
. - Defer DOM work: Uses
setTimeout
inngAfterViewInit
to let the view settle before measuring. - measure(): Reads bounding box and formats a size string.
Notes:
- Don't use DOM in constructor: The view isn't ready; do DOM operations after
ngAfterViewInit
. - Reading too early:
@ViewChild
isundefined
beforengAfterViewInit
—check for existence or defer work.
AfterViewInit & Cleanup
Access template refs after the view initializes and clean up resources when the component is destroyed.
// Example teardown pattern
sub?.unsubscribe?.();
clearInterval(intervalId);
removeListener?.();
Example explained
- Teardown: Unsubscribe, clear intervals, and remove listeners in
ngOnDestroy
. - Safety: Optional chaining (
?.
) guards against missing handles.
Notes:
- Focus and measure safely: Run DOM reads/writes after
ngAfterViewInit
(or insidesetTimeout
to let the view settle). - Observers & listeners: Disconnect
ResizeObserver
/MutationObserver
and remove manual event listeners inngOnDestroy
. - Subscriptions: Use the
async
pipe; if you subscribe manually, unsubscribe on destroy (e.g.,takeUntilDestroyed
).