Our content is free thanks to ag-Grid

ag-Grid is the industry leading JavaScript datagrid

ag-grid.com

He who thinks change detection is depth-first and he who thinks it’s breadth-first are both usually right

Post Editor

Do you know if Angular first checks siblings of the current component (breadth-first) or its children (depth-first)? This article demonstrates that the answer depends on what operations of change detection you consider.

3 min read
post

He who thinks change detection is depth-first and he who thinks it’s breadth-first are both usually right

Do you know if Angular first checks siblings of the current component (breadth-first) or its children (depth-first)? This article demonstrates that the answer depends on what operations of change detection you consider.

post
post
3 min read
3 min read

I was once asked if change detection in Angular is depth or breadth first. This basically means whether Angular first checks siblings of the current component (breadth-first) or its children (depth-first). I hadn’t given any prior thought to this question so I just went with my gut and the knowledge of internals. I declared that it was depth-first. Later, to check my assertion, I created a tree of components and put some logging logic inside the ngDoCheck hook:

@Component({
    selector: 'r-comp',
    template: `{{addRender()}}`
})
export class RComponent {
    ngDoCheck() {
        // holds all calls in order and is logged to console
        calls.ngDoCheck.push('R');
    }

And to my surprise, it turned out that some siblings were checked first as depicted on the diagram below:

So here you see that Angular checks K and then V, L and then C and so on. So was I wrong and it’s really a breadth-first algorithm? Well, not exactly. First thing to notice in the above representation is that it’s not a proper breadth-first algorithm. The conventional implementation of the algorithm checks all siblings on the same level, whereas in the diagram above as you can see the algorithm indeed checks L and C sibling components, but instead of checking X and F it goes down to J and O. Also, the implementation of the breadth-first graph traversal algorithm is well defined but I couldn’t find it in the sources. So I decided to run another experiment and put a logging logic in a custom function called when change detection evaluates expressions:

@Component({
    selector: 'r-comp',
    template: `{{addRender()}}`
})
export class RComponent {
    addRender() {
        calls.render.push('R');
    }
}

And this time I got different results:

It’s a proper depth-first graph traversal algorithm. So what’s going on here? It’s actually pretty simple, let me explain.

Change detection operations

To understand the difference in behavior we need to take a look at the operations performed by change detection mechanism when checking a component. If you’ve read my other articles on change detection you probably know that the key operations performed by change detection are the following:

  • update child components properties
  • call NgOnChanges and NgDoCheck lifecycle hooks on child components
  • update DOM on the current component
  • run change detection for child components

I highlighted one interesting specifics above — when Angular checks the current component it calls lifecycle hooks on child components, but renders DOM for the current component. And that’s a very important distinction. This is precisely the reason that makes it seem as if the algorithm runs breadth-first if we put logging into NgDoCheck hook. When Angular checks a current component it calls lifecycle hooks for all its child components which are siblings. Suppose Angular checks K component now and calls NgDoCheck lifecycle hook on L and C. So, we get the following:

Looks like breadth-first algorithm. However, remember that Angular still in the process of checking K component. So after completing all operations for the K component it doesn’t proceed to checking the sibling V component, as it would with the breadth-first implementation. Instead, it goes on to check L component, which is a child of K. This is the depth-first implementation of change detection algorithm. And as we now know it will call ngDoCheck on J and O components and this is exactly what happens:

So, after all, my gut didn’t let me down. Change detection mechanism is implemented as depth-first internally, but involves calling ngDoCheck lifecycle hooks on sibling components first. By the way, I already described this logic in depth in the If you think `ngDoCheck` means your component is being checked — read this article.

Stackblitz demo

Here you can see the demo with logging logic in different places.

Discuss with community

Share

About the author

author_image
Max Koretskyi

Max is a self-taught software engineer that believes in fundamental knowledge and hardcore learning. He’s the founder of inDepth.dev community and one of the top users on StackOverflow (70k rep).

author_image

About the author

Max Koretskyi

Max is a self-taught software engineer that believes in fundamental knowledge and hardcore learning. He’s the founder of inDepth.dev community and one of the top users on StackOverflow (70k rep).

About the author

author_image
Max Koretskyi

Max is a self-taught software engineer that believes in fundamental knowledge and hardcore learning. He’s the founder of inDepth.dev community and one of the top users on StackOverflow (70k rep).

NxAngularCli
NxAngularCli
NxAngularCli

Featured articles

RxJSpost
21 January 20214 min read
RxJS in Angular: Part III

In my previous two articles we have discussed how to change our components which solve problems in imperative ways to do that in functional, reactive, RxJS way, and we of course had a lot of fun doing that.

RxJSpost
21 January 20214 min read
RxJS in Angular: Part III

In my previous two articles we have discussed how to change our components which solve problems in imperative ways to do that in functional, reactive, RxJS way, and we of course had a lot of fun doing that.

Read more
RxJSpostRxJS in Angular: Part III

21 January 2021

4 min read

In my previous two articles we have discussed how to change our components which solve problems in imperative ways to do that in functional, reactive, RxJS way, and we of course had a lot of fun doing that.

Read more
Angularpost
20 January 20216 min read
Angular and SOLID principles

In software engineering, making things work the first time is always easy. But, what if you want to add new functionalities to an existing code? Making iterations on an existing basis can be difficult to do without introducing bugs. This is where SOLID principles come into play.

Angularpost
20 January 20216 min read
Angular and SOLID principles

In software engineering, making things work the first time is always easy. But, what if you want to add new functionalities to an existing code? Making iterations on an existing basis can be difficult to do without introducing bugs. This is where SOLID principles come into play.

Read more
AngularpostAngular and SOLID principles

20 January 2021

6 min read

In software engineering, making things work the first time is always easy. But, what if you want to add new functionalities to an existing code? Making iterations on an existing basis can be difficult to do without introducing bugs. This is where SOLID principles come into play.

Read more
Angularpost
14 January 20216 min read
Demystifying Taiga UI root component: portals pattern in Angular

Just before new year we announced our new Angular UI kit library Taiga UI. If you go through Getting started steps, you will see that you need to wrap your app with the tui-root component. Let's see what it does and explore what portals are and how and why we use them.