> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/angular/angular/llms.txt
> Use this file to discover all available pages before exploring further.

# Lifecycle Hooks

> Interfaces for component and directive lifecycle events

## Overview

Lifecycle hooks provide visibility into key moments in a component or directive's lifecycle, from creation to destruction. Angular calls these hook methods in a specific sequence.

## Lifecycle Sequence

1. **Constructor** - Class instantiation (not a hook)
2. **OnChanges** - Input property changes
3. **OnInit** - Initialization after first change detection
4. **DoCheck** - Custom change detection
5. **AfterContentInit** - Content projection initialized (once)
6. **AfterContentChecked** - Content projection checked
7. **AfterViewInit** - View initialized (once)
8. **AfterViewChecked** - View checked
9. **OnDestroy** - Cleanup before destruction

## OnChanges

### Interface

```typescript theme={null}
interface OnChanges {
  ngOnChanges(changes: SimpleChanges): void;
}
```

### Description

Called when any data-bound input property changes. Receives a `SimpleChanges` object containing current and previous values.

### Parameters

<ParamField path="changes" type="SimpleChanges">
  Object containing changed properties with their current and previous values.

  ```typescript theme={null}
  {
    propertyName: {
      currentValue: any,
      previousValue: any,
      firstChange: boolean
    }
  }
  ```
</ParamField>

### Example

```typescript user-profile.component.ts theme={null}
import { Component, Input, OnChanges, SimpleChanges } from '@angular/core';

@Component({
  selector: 'app-user-profile',
  standalone: true,
  template: `
    <div>
      <h3>{{userName}}</h3>
      <p>Changes: {{changeCount}}</p>
    </div>
  `
})
export class UserProfileComponent implements OnChanges {
  @Input() userName: string = '';
  @Input() userId: number = 0;
  changeCount = 0;

  ngOnChanges(changes: SimpleChanges) {
    this.changeCount++;
    
    if (changes['userName']) {
      console.log('userName changed:', {
        previous: changes['userName'].previousValue,
        current: changes['userName'].currentValue,
        first: changes['userName'].firstChange
      });
    }
    
    if (changes['userId'] && !changes['userId'].firstChange) {
      // Reload user data when ID changes (but not on first load)
      this.loadUserData(changes['userId'].currentValue);
    }
  }
  
  private loadUserData(id: number) {
    console.log('Loading data for user:', id);
  }
}
```

<Note>
  `ngOnChanges` is called before `ngOnInit` and whenever input properties change. It's not called for internal state changes.
</Note>

## OnInit

### Interface

```typescript theme={null}
interface OnInit {
  ngOnInit(): void;
}
```

### Description

Called once after the first `ngOnChanges`. Used for component initialization logic.

### Example

```typescript data-list.component.ts theme={null}
import { Component, OnInit } from '@angular/core';
import { DataService } from './data.service';

@Component({
  selector: 'app-data-list',
  standalone: true,
  template: `
    <ul>
      <li *ngFor="let item of items">{{item}}</li>
    </ul>
  `
})
export class DataListComponent implements OnInit {
  items: string[] = [];

  constructor(private dataService: DataService) {
    // Constructor should be lightweight
    console.log('Constructor called');
  }

  ngOnInit() {
    // Initialization logic goes here
    console.log('ngOnInit called');
    this.loadData();
  }

  private loadData() {
    this.dataService.getData().subscribe(data => {
      this.items = data;
    });
  }
}
```

<Tip>
  Use `ngOnInit` for initialization logic that requires input properties or depends on services. Keep constructors lightweight.
</Tip>

## DoCheck

### Interface

```typescript theme={null}
interface DoCheck {
  ngDoCheck(): void;
}
```

### Description

Called during every change detection cycle. Use for custom change detection logic.

### Example

```typescript custom-check.component.ts theme={null}
import { Component, Input, DoCheck } from '@angular/core';

@Component({
  selector: 'app-custom-check',
  standalone: true,
  template: `
    <div>
      <p>Items: {{items.length}}</p>
      <p>Last check: {{lastCheck}}</p>
    </div>
  `
})
export class CustomCheckComponent implements DoCheck {
  @Input() items: string[] = [];
  lastCheck: Date = new Date();
  private previousLength = 0;

  ngDoCheck() {
    this.lastCheck = new Date();
    
    // Custom check for array length changes
    if (this.items.length !== this.previousLength) {
      console.log('Array length changed:', this.previousLength, '->', this.items.length);
      this.previousLength = this.items.length;
    }
  }
}
```

<Warning>
  `ngDoCheck` is called very frequently. Keep its logic lightweight to avoid performance issues.
</Warning>

## AfterContentInit

### Interface

```typescript theme={null}
interface AfterContentInit {
  ngAfterContentInit(): void;
}
```

### Description

Called once after Angular projects external content into the component's view (content children).

### Example

```typescript tab-container.component.ts theme={null}
import { Component, ContentChildren, QueryList, AfterContentInit } from '@angular/core';
import { TabComponent } from './tab.component';

@Component({
  selector: 'app-tab-container',
  standalone: true,
  template: `
    <div class="tab-container">
      <ng-content></ng-content>
    </div>
  `
})
export class TabContainerComponent implements AfterContentInit {
  @ContentChildren(TabComponent) tabs!: QueryList<TabComponent>;

  ngAfterContentInit() {
    console.log('Content initialized');
    console.log(`Found ${this.tabs.length} tabs`);
    
    // Activate the first tab
    if (this.tabs.length > 0) {
      this.tabs.first.active = true;
    }
  }
}
```

## AfterContentChecked

### Interface

```typescript theme={null}
interface AfterContentChecked {
  ngAfterContentChecked(): void;
}
```

### Description

Called after Angular checks the content projected into the component.

### Example

```typescript content-wrapper.component.ts theme={null}
import { Component, ContentChild, AfterContentChecked, ElementRef } from '@angular/core';

@Component({
  selector: 'app-content-wrapper',
  standalone: true,
  template: `
    <div class="wrapper">
      <ng-content></ng-content>
      <p>Content height: {{contentHeight}}px</p>
    </div>
  `
})
export class ContentWrapperComponent implements AfterContentChecked {
  @ContentChild('content') content?: ElementRef;
  contentHeight = 0;

  ngAfterContentChecked() {
    if (this.content) {
      this.contentHeight = this.content.nativeElement.offsetHeight;
    }
  }
}
```

## AfterViewInit

### Interface

```typescript theme={null}
interface AfterViewInit {
  ngAfterViewInit(): void;
}
```

### Description

Called once after Angular initializes the component's views and child views.

### Example

```typescript chart.component.ts theme={null}
import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';

@Component({
  selector: 'app-chart',
  standalone: true,
  template: `
    <canvas #chartCanvas></canvas>
  `
})
export class ChartComponent implements AfterViewInit {
  @ViewChild('chartCanvas') canvas!: ElementRef<HTMLCanvasElement>;

  ngAfterViewInit() {
    console.log('View initialized');
    this.initializeChart();
  }

  private initializeChart() {
    const ctx = this.canvas.nativeElement.getContext('2d');
    if (ctx) {
      // Initialize chart library
      console.log('Canvas ready:', this.canvas.nativeElement.width);
    }
  }
}
```

<Note>
  View children are guaranteed to be initialized in `ngAfterViewInit`. Don't try to access them in `ngOnInit`.
</Note>

## AfterViewChecked

### Interface

```typescript theme={null}
interface AfterViewChecked {
  ngAfterViewChecked(): void;
}
```

### Description

Called after Angular checks the component's views and child views.

### Example

```typescript scroll-monitor.component.ts theme={null}
import { Component, ViewChild, ElementRef, AfterViewChecked } from '@angular/core';

@Component({
  selector: 'app-scroll-monitor',
  standalone: true,
  template: `
    <div #scrollContainer class="container">
      <div class="content">{{content}}</div>
    </div>
    <p>Scroll position: {{scrollPosition}}</p>
  `
})
export class ScrollMonitorComponent implements AfterViewChecked {
  @ViewChild('scrollContainer') container!: ElementRef;
  scrollPosition = 0;
  content = 'Some content...';

  ngAfterViewChecked() {
    if (this.container) {
      this.scrollPosition = this.container.nativeElement.scrollTop;
    }
  }
}
```

<Warning>
  Be cautious with `ngAfterViewChecked` - it's called very frequently. Avoid expensive operations or state changes that trigger additional change detection.
</Warning>

## OnDestroy

### Interface

```typescript theme={null}
interface OnDestroy {
  ngOnDestroy(): void;
}
```

### Description

Called just before Angular destroys the component or directive. Used for cleanup logic.

### Example

```typescript timer.component.ts theme={null}
import { Component, OnInit, OnDestroy } from '@angular/core';
import { interval, Subscription } from 'rxjs';

@Component({
  selector: 'app-timer',
  standalone: true,
  template: `
    <div>
      <p>Elapsed: {{elapsed}} seconds</p>
    </div>
  `
})
export class TimerComponent implements OnInit, OnDestroy {
  elapsed = 0;
  private subscription?: Subscription;

  ngOnInit() {
    console.log('Timer started');
    this.subscription = interval(1000).subscribe(() => {
      this.elapsed++;
    });
  }

  ngOnDestroy() {
    console.log('Timer stopped');
    // Clean up subscription to prevent memory leaks
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }
}
```

<Tip>
  Always clean up in `ngOnDestroy`:

  * Unsubscribe from Observables
  * Clear intervals and timeouts
  * Detach event listeners
  * Cancel pending HTTP requests
</Tip>

## Complete Lifecycle Example

```typescript full-lifecycle.component.ts theme={null}
import {
  Component,
  Input,
  OnChanges,
  OnInit,
  DoCheck,
  AfterContentInit,
  AfterContentChecked,
  AfterViewInit,
  AfterViewChecked,
  OnDestroy,
  SimpleChanges
} from '@angular/core';

@Component({
  selector: 'app-full-lifecycle',
  standalone: true,
  template: `
    <div>
      <h3>{{title}}</h3>
      <ng-content></ng-content>
    </div>
  `
})
export class FullLifecycleComponent implements
  OnChanges,
  OnInit,
  DoCheck,
  AfterContentInit,
  AfterContentChecked,
  AfterViewInit,
  AfterViewChecked,
  OnDestroy {
  
  @Input() title = 'Component';
  
  constructor() {
    console.log('1. Constructor');
  }

  ngOnChanges(changes: SimpleChanges) {
    console.log('2. ngOnChanges', changes);
  }

  ngOnInit() {
    console.log('3. ngOnInit');
  }

  ngDoCheck() {
    console.log('4. ngDoCheck');
  }

  ngAfterContentInit() {
    console.log('5. ngAfterContentInit');
  }

  ngAfterContentChecked() {
    console.log('6. ngAfterContentChecked');
  }

  ngAfterViewInit() {
    console.log('7. ngAfterViewInit');
  }

  ngAfterViewChecked() {
    console.log('8. ngAfterViewChecked');
  }

  ngOnDestroy() {
    console.log('9. ngOnDestroy');
  }
}
```

## Hook Call Frequency

| Hook                    | Frequency        | When to Use                            |
| ----------------------- | ---------------- | -------------------------------------- |
| `ngOnChanges`           | On input changes | React to input changes                 |
| `ngOnInit`              | Once             | Initialization logic                   |
| `ngDoCheck`             | Every CD cycle   | Custom change detection                |
| `ngAfterContentInit`    | Once             | Access content children                |
| `ngAfterContentChecked` | Every CD cycle   | Monitor content changes                |
| `ngAfterViewInit`       | Once             | Access view children, DOM manipulation |
| `ngAfterViewChecked`    | Every CD cycle   | Monitor view changes                   |
| `ngOnDestroy`           | Once             | Cleanup                                |

## Best Practices

<Tip>
  * Use `ngOnInit` for most initialization logic
  * Keep `ngDoCheck`, `ngAfterContentChecked`, and `ngAfterViewChecked` lightweight
  * Always implement `ngOnDestroy` for components with subscriptions
  * Use `ngAfterViewInit` to access child components and DOM elements
  * Prefer `ngOnChanges` over getters/setters for input change detection
</Tip>

<Warning>
  * Don't modify component state in `ngAfterViewChecked` (causes ExpressionChangedAfterItHasBeenCheckedError)
  * Avoid heavy computations in frequently called hooks
  * Don't forget to unsubscribe from Observables in `ngOnDestroy`
  * Be careful with async operations in lifecycle hooks
</Warning>

## Signal-Based Alternative

With Angular signals, many lifecycle hooks can be replaced:

```typescript signal-component.ts theme={null}
import { Component, input, effect, OnDestroy } from '@angular/core';

@Component({
  selector: 'app-signal-component',
  standalone: true,
  template: `<p>{{name()}}: {{count()}}</p>`
})
export class SignalComponent implements OnDestroy {
  name = input('Item');
  count = input(0);
  
  // Automatically runs when signals change
  private logEffect = effect(() => {
    console.log(`${this.name()} count is ${this.count()}`);
  });
  
  ngOnDestroy() {
    // Effects are automatically cleaned up
  }
}
```

## Related APIs

* [@Component](/api/core/component) - Define components
* [@Directive](/api/core/directive) - Define directives
* [SimpleChanges](/api/core/simple-changes) - Input change information
* [effect()](/api/core/effect) - Signal-based reactive effects

## See Also

<CardGroup cols={2}>
  <Card title="Lifecycle Guide" icon="book" href="/concepts/components">
    Component lifecycle guide
  </Card>

  <Card title="Change Detection" icon="book" href="/advanced/change-detection">
    How change detection works
  </Card>

  <Card title="Queries" icon="book" href="/api/core/input-output">
    Query child components
  </Card>

  <Card title="Signals" icon="book" href="/advanced/signals">
    Modern reactive patterns
  </Card>
</CardGroup>
