How to manually trigger change detection in Angular

Manually triggering change detection in Angular is necessary when updates occur outside Angular’s zone or with OnPush strategy. As the creator of CoreUI with over 12 years of Angular experience since 2014, I’ve manually controlled change detection in complex applications. ChangeDetectorRef provides methods to manually trigger, detach, and reattach change detection for fine-grained control. This approach optimizes performance and ensures UI updates when Angular doesn’t automatically detect changes.

Use ChangeDetectorRef methods to manually control when Angular runs change detection cycles.

Basic manual trigger:

import { Component, ChangeDetectorRef } from '@angular/core'

@Component({
  selector: 'app-manual',
  template: '<div>{{ data }}</div>'
})
export class ManualComponent {
  data: string

  constructor(private cdr: ChangeDetectorRef) {}

  updateData() {
    // External update outside Angular zone
    setTimeout(() => {
      this.data = 'Updated'
      // Manually trigger change detection
      this.cdr.detectChanges()
    }, 1000)
  }
}

markForCheck vs detectChanges:

import { Component, ChangeDetectorRef, ChangeDetectionStrategy } from '@angular/core'

@Component({
  selector: 'app-comparison',
  template: '<div>{{ count }}</div>',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class ComparisonComponent {
  count = 0

  constructor(private cdr: ChangeDetectorRef) {}

  // markForCheck - marks component and ancestors for check
  useMarkForCheck() {
    this.count++
    this.cdr.markForCheck()
  }

  // detectChanges - runs change detection immediately
  useDetectChanges() {
    this.count++
    this.cdr.detectChanges()
  }
}

Detach and reattach:

import { Component, ChangeDetectorRef, OnDestroy } from '@angular/core'

@Component({
  selector: 'app-detach',
  template: `
    <div>
      <p>Value: {{ value }}</p>
      <button (click)="toggle()">Toggle Change Detection</button>
    </div>
  `
})
export class DetachComponent implements OnDestroy {
  value = 0
  private isDetached = false

  constructor(private cdr: ChangeDetectorRef) {}

  toggle() {
    if (this.isDetached) {
      this.cdr.reattach()
      console.log('Reattached')
    } else {
      this.cdr.detach()
      console.log('Detached')
    }
    this.isDetached = !this.isDetached
  }

  ngOnDestroy() {
    this.cdr.detach()
  }
}

With external libraries:

import { Component, ChangeDetectorRef, OnInit, OnDestroy } from '@angular/core'

@Component({
  selector: 'app-external',
  template: '<div>{{ data }}</div>'
})
export class ExternalComponent implements OnInit, OnDestroy {
  data: any
  private subscription: any

  constructor(private cdr: ChangeDetectorRef) {}

  ngOnInit() {
    // External library callback
    window.addEventListener('customEvent', (event: any) => {
      this.data = event.detail
      this.cdr.detectChanges()
    })

    // WebSocket example
    const socket = new WebSocket('ws://localhost:8080')
    socket.onmessage = (event) => {
      this.data = JSON.parse(event.data)
      this.cdr.markForCheck()
    }
  }

  ngOnDestroy() {
    // Cleanup
  }
}

Performance optimization:

import { Component, ChangeDetectorRef } from '@angular/core'

@Component({
  selector: 'app-optimized',
  template: `
    <canvas #canvas></canvas>
    <div>FPS: {{ fps }}</div>
  `
})
export class OptimizedComponent {
  fps = 0
  private lastUpdate = Date.now()

  constructor(private cdr: ChangeDetectorRef) {
    this.startAnimation()
  }

  startAnimation() {
    const animate = () => {
      // Heavy canvas drawing
      this.drawCanvas()

      // Update FPS only every second
      const now = Date.now()
      if (now - this.lastUpdate > 1000) {
        this.fps = Math.round(1000 / (now - this.lastUpdate))
        this.lastUpdate = now
        this.cdr.detectChanges()
      }

      requestAnimationFrame(animate)
    }
    requestAnimationFrame(animate)
  }

  drawCanvas() {
    // Canvas operations
  }
}

Best Practice Note

Use markForCheck() with OnPush strategy—it schedules check for next cycle. Use detectChanges() for immediate updates outside Angular zone. Detach change detection during heavy operations to improve performance. Always reattach or clean up detached components in ngOnDestroy. Use manual triggering sparingly—let Angular handle most cases automatically. This is how we handle change detection in CoreUI Angular—strategic manual triggers for external updates while relying on Angular’s automatic detection for standard operations.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author