Software Development

Behind the scenes

Understanding Angular’s Change Detection Triggers (Before They Surprise You)

If you’ve worked with Angular for a while, you’ve probably seen it: the UI looks fine at first glance, no errors in sight, but something just isn’t updating the way it should. It’s the kind of thing we often catch during testing — or if we’re lucky, during a code review.

One of the most common culprits? Misunderstanding how Angular’s Change Detection Triggers work, especially when using ChangeDetectionStrategy.OnPush. Let’s talk about it.

The Problem

In our team, we frequently use ChangeDetectionStrategy.OnPush to boost performance. It works great — until it doesn’t.

One recurring issue we’ve noticed is that views sometimes don’t reflect the latest state changes. This often slips by unnoticed until a tester clicks something and says, “Wait, why didn’t that update?” A quick look shows the data changed, but the UI stayed frozen in time.

Everything looks fine — until change detection bites us.

The Solution

In cases like this, the underlying issue usually comes down to Angular not knowing it needs to check the component for changes. With OnPush, Angular is selective: it only runs change detection when it knows something changed.

That means if you mutate an object or rely on services that update values behind the scenes, Angular won’t update the view unless you:

  • Change the object’s reference (immutability FTW),
  • Trigger an event from the component,
  • Or manually call ChangeDetectorRef.markForCheck().

Deeper Explanation

Here’s a short example we’ve run into:

@Component({
  selector: 'my-component',
  template: `{{ user.name }}`,
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyComponent {
  @Input() user!: { name: string };
}

this.user.name = 'Alice'; // UI doesn't update

Angular doesn’t detect any change — because the reference stayed the same. But:

this.user = { ...this.user, name: 'Alice' }; // UI updates

With a new object reference, Angular knows to re-check the component.

These bugs often come up when values are updated via services or in async calls — especially outside Angular’s zone or without proper event handling.

Best Practices

  • Favor immutable data structures — replace instead of mutate.
  • Be cautious, but don’t fear ChangeDetectorRef.markForCheck() — it’s useful when changes happen outside Angular’s normal triggers.
  • Use markForCheck() sparingly, and only when really needed.
  • Understand what triggers Angular’s change detection with OnPush.
  • Watch out for third-party libraries or background tasks that may operate outside Angular’s zone.

Conclusion

This isn’t a rare edge case — it’s something we regularly catch in reviews or late in QA. The good news? Once you understand how ChangeDetectionStrategy.OnPush and change detection triggers work, it becomes easy to prevent.

Let this be your reminder: when things “look fine,” take a second look. Because sometimes, it only looks fine — until change detection reminds you who’s really in charge.

Happy debugging!


Leave a Reply

Your email address will not be published. Required fields are marked *

About Me

I’m a software developer sharing thoughts, tips, and lessons from everyday coding life — the good, the bad, and the buggy.