In this article I will talk in depth about the Angular 2 change detection system.

HIGH-LEVEL OVERVIEW

An Angular 2 application is a tree of components.

An Angular 2 application is a reactive system, with change detection being the core of it.

Every component gets a change detector responsible for checking the bindings defined in its template. Examples of bindings:{{todo.text}} and [todo]=”t”. Change detectors propagate bindings[c] from the root to leaves in the depth first order.

In Angular 2 there are no two-way data-bindings. That is why the change detection graph is a directed tree and cannot have cycles (i.e., it is an arborescence). This makes the system significantly more performant. And what is more important we gain guarantees that make the system more predictable and easier to reason about. How Fast Is It?

By default the change detection goes through every node of the tree to see if it changed, and it does it on every browser event. Although it may seem terribly inefficient, the Angular 2 change detection system can go through hundreds of thousands of simple checks (the number are platform dependent) in a few milliseconds. How we managed to achieve such an impressive result is a topic for another blog post.

Angular has to be conservative and run all the checks every single time because the JavaScript language does not give us object mutation guarantees. But we may know that certain properties hold if we, for example, use immutable or observable objects. Previously Angular could not take advantage of this, now it will.

IMMUTABLE OBJECTS

If a component depends only on its bindings, and the bindings are immutable, then this component can change if and only if one of its bindings changes. Therefore, we can skip the component’s subtree in the change detection tree until such an event occurs. When it happens, we can check the subtree once, and then disable it until the next change (gray boxes indicate disabled change detectors).

If we are aggressive about using immutable objects, a big chunk of the change detection tree will be disabled most of the time.

Implementing this is trivial.

class ImmutableTodoCmp {
todo:Todo;
constructor(private bingings:BindingPropagationConfig){}
// onChange is a part of the component’s lifecycle.
// It is called when any the bindings changes.
onChange(_) => bingings.shouldBePropagated();
}

The framework can make it even easier to use through a mixin.

class ImmutableTodoCmp {
todo:Todo;
}
applyMixins(ImmutableTodoCmp, [ComponentWithImmutableBindings]);

OBSERVABLE OBJECTS

If a component depends only on its bindings, and the bindings are observable, then this component can change if and only if one of its bindings emits an event. Therefore, we can skip the component’s subtree in the change detection tree until such an event occurs. When it happens, we can check the subtree once, and then disable it until the next change.

Although it may sound similar to the Immutable Objects case, it is quite different. If you have a tree of components with immutable bindings, a change has to go through all the components starting from the root. This is not the case when dealing with observables.

Let me sketch out a small example demonstrating the issue.

type ObservableTodo = Observable<Todo>;
type ObservableTodos = Observable<Array<ObservableTodo>>; @Component({selector:’todos’})
class ObservableTodosCmp {
todos:ObservableTodos;
//...
}

The template of ObservableTodosCmp:

<todo template=”ng-repeat: var t in todos” todo=”t”></todo>

Finally, ObservableTodoCmp:

@Component({selector:’todo’})
class ObservableTodoCmp {
todo:ObservableTodo;
//...
}

As you can see, here the Todos component has only a reference to an observable of an array of todos. So it cannot see the changes in individual todos.

The way to handle it is to check the path from the root to the changed Todo component when its todo observable fires an event. The change detection system will make sure this happens.

Say our application uses only observable objects. When it boots, Angular will check all the objects.

So the state after the first pass will look as follows.

Let’s say the first todo observable fires an event. The system will switch to the following state:

And after checking App_ChangeDetector, Todos_ChangeDetector, and the first Todo_ChangeDetector, it will go back to this state.

Assuming that changes happen rarely and the components form a balanced tree, using observables changes the complexity of change detection from O(N) to O(logN), where N is the number of bindings in the system.

Angular will come with mixins for the most popular libraries and Object.observe.

class ObservableTodoCmp {
todo:ObservableTodo;
}
applyMixins(ImmutableTodoCmp, [ComponentWithRxObservableBindings]);

But this capability is not tied to those libraries. Implementing such a mixin for another library should be a matter of a few lines of code.

DO OBSERVABLES CAUSE CASCADING UPDATES?

Observable objects have bad reputation because they can cause cascading updates. Anyone who has experience in building large applications in a framework that relies on observable models knows what I am talking about. One observable object update can cause a bunch of other observable objects trigger updates, which can do the same. Somewhere along the way views get updated. Such systems are very hard to reason about.

Using observable objects in Angular 2 as shown above will not have this problem. An event triggered by an observable just marks a path from the component to the root as to be checked next time. Then the normal change detection process kicks in and goes through the nodes of the tree in the depth first order. So the order of updates does not change whether you use observables or not. This is very important. Using an observable object becomes a simple optimization that does not change the way you think about the system.

DO I HAVE TO USE OBSERVABLE/IMMUTABLE OBJECTS EVERYWHERE TO SEE THE BENEFITS?

No, you don’t have to. You can use observables in one part of your app (e.g., in some gigantic table.), and that part will get the performance benefits. Even more, you can compose different types of components and get the benefits of all of them. For instance, an “observable component” can contain an “immutable component”, which can itself contain an “observable component”. Even in such a case the change detection system will minimize the number of checks required to propagate changes.

NO SPECIAL CASES

Support for immutable and observable objects is not baked into the change detection. These types of components are not in any way special. So you can write your own directives that use change detection in a “smart” way. For instance, imagine a directive updating its content every N seconds.

SUMMARY

  • An Angular 2 application is a reactive system.
  • The change detection system propagates bindings from the root to leaves.
  • Unlike Angular 1.x, the change detection graph is a directed tree. As a result, the system is more performant and predictable.
  • By default, the change detection system walks the whole tree. But if you use immutable objects or observables, you can take advantage of them and check parts of the tree only if they “really change”.
  • These optimizations compose and do not break the guarantees the change detection provides.

API CHANGES

This described functionality is already in the Angular 2 master branch on GitHub. The current API, however, may change before the first stable release of the framework.

    • Sebastian Sebald • 2 months ago

      So Angular 2 basically uses the Flux Architecture? :-)

    • 2
    • Reply
    • Share ›
    •  
       
    •  
       
      Foo • 2 months ago

      Interesting stuff, thanks! One question: how exactly do observables avoid cascading updates? My understanding is that in Angular 1.x, cascading updates are dealt with by repeating the $digest() loop until no further changes are detected. Won't that still be true? Thanks to the new immutable and observable features you outline, each loop will have less to process, but it still needs to repeat until a stable state is reached, right?

    • 1
    • Reply
    • Share ›
    •  
       
      •  
         
        Victor Savkin Mod Foo • 2 months ago

        Thanks.

        In Angular2 we walk the change detection graph only once (TTL = 1). This is possible because the graph does not have any cycles.

        What I meant by cascading updates is the following:

        Say we have three observable objects: ObjectA, ObjectB, ObjectC. Consider the following situation.
        - ObjectA fires an event
        - The view rendering ObjectA catches the event and rerenders
        - ObjectB catches the event and fires its own event
        - The view rendering ObjectB catches the event and rerenders
        - ObjectC catches the event and fires its own event
        - ObjectA catches the event fired by ObjectC
        ...

        Note how the framework calls (view.render) are interleaved with the events. It is hard to tell what gets rerendered when. This is a real problem of many front-end frameworks.

        In Angular2 it works as follows.
        - ObjectA fires an event
        - ObjectB catches the event and fires its own event
        - ObjectC catches the event and fires its own event
        - ObjectA catches the event fired by ObjectC

        While it is happening the framework is marking the parts of the change detection graph that are updated. This marking has no observable side effects. At the end of the VM turn the framework will check only the marked paths and update the bindings.

        Note that there is a clear separation of when the model is updated and when the framework does its magic. The bindings would be updated in the same order if ObjectA, ObjectB, and ObjectC were not observable. This is very important.

        see more

      • Reply
      • Share ›
      •  
         
    •  
       
      Nate • 2 months ago

      > bingings.shouldBePropagated();

      Not so sure about propagating bingings!!!

    • 1
    • Reply
    • Share ›
    •  
       
    •  
       
      Csaba Tamas • 15 days ago

      How can I manual run change detection or subscripe it for example in component.js
      this.service.ajaxCall((result) => {
      this.todos = result.todos; //change
      this.findNewElementInDOM(); // dont't work
      --> Don't runned the change detection here, but I only can here cach the async call and detect change
      });
      --> change detection only run after ajaxCall callback executed

    • Reply
    • Share ›
    •  
       
    •  
       
      ajaybeniwal • 19 days ago

      Its nearly similar to React and Flux using immutable.js along with it

    • Reply
    • Share ›
    •  
       
    •  
       
      Manfred Steyer • a month ago

      I'm wondering, when the digest-phase is triggered. You mentioned that it's triggered on every browser-event. But I've noticed, that the view is even updated, when I update the bindings in an async success-handler, angular2 isn't aware of. As I don't use observables this seems quite magic to me ...

    • Reply
    • Share ›
    •  
       
    •  
       
      seba • a month ago

      So, how does it differ from React? It looks to be almost the same now... Well, except they've invented their own language now, Hooray :( Sure you can do it in plain JS, but then you end up with the same mess as in Angular 1. Moving stuff out of your api and into annotations, doesn't make your api better.

      It seems angular 2 will loose a huge amount of convenience for the developer in trade for performance, while the angular folks been preaching angular 1 didn't have a performance problem because you never need to show more than 2000 bound items at once etc. And to be honest, angular 1 is performing just fine and I'd rather keep my speed of development than get performance I don't need. If people need it, let them use React. Now we seem to loose the choice and get 2 times React.

    • Reply
    • Share ›
    •  
       
      •  
         
        Manfred Steyer seba • a month ago

        While I see this point just like you, I hope, that there will be some syntax sugar, that will bring back the convenience from 1.x to 2.x ...

      • Reply
      • Share ›
      •  
         
    •  
       
      kowdermeister • a month ago

      It seems it's safe to conclude that Angular apps are not written anymore in JavaScript, but in Angular language. Probably this will never make it popular.

    • Reply
    • Share ›
    •  
       
    •  
       
      Jimmy Breck-McKye • 2 months ago

      Forgive me, but didn't Knockout.js effectively implement this four years ago?

    • Reply
    • Share ›
    •  
       
      •  
         
        Victor Savkin Mod Jimmy Breck-McKye • 2 months ago

        If you are referring to the notion of Observables, then, yes, most frameworks support observable models. Most frameworks, however, require you to use a particular kind of observables and suffer from cascading updates.

        Angular does not force you to use one kind of observables: you can use rxjs, object.observe, backbone models, probably even knockout models. It can take advantage of any of them because the described mechanism is more generic.

        The most important thing is that you don't have to use observables at all and instead take advantage of immutable objects. The framework remains model agnostic.

      • Reply
      • Share ›
      •  
         
        •  
           
          Jimmy Breck-McKye Victor Savkin • 2 months ago

          Knockout does not require you to use observables if a model property does not mutate; it will quite happily bind plain properties - it just won't react to mutations. You can even place computed properties in front of multiple observables, and Knockout will only update the computed's subscribers when the computed's result actually changes.

          Knockout provides a great deal of fine control over how mutation propagates; the utils it provides make it easy to write performant code naively. I think it's a real shame that so many developers dismiss it without even looking at it.

        • Reply
        • Share ›
        •  
           
        •  
           
          johnpapa7 Victor Savkin • 2 months ago

          This is an important statement here: "The most important thing is that you don't have to use observables at all and instead take advantage of immutable objects."

          One of the biggest draws to Angular 1.x was the idea of not requiring an implementation of wrapping objects with observability. We could use raw javascript objects. Anyone who has done this in Knockout or other similar observable tech (IObservable in .NET even) knows the cost of it is you always have to convert your objects (or wrap them) with this feature. Can you clarify your statement here on how immutable objects can be used, say, given a todo:

          [
          { id: 1, description: 'go shopping'},
          {id: 2, description: 'take kids to school'}
          ]

        • Reply
        • Share ›
        •  
           
          •  
             
            Victor Savkin Mod johnpapa7 • 2 months ago

            There are three problems with using immutable objects in Angular 1.x:
            - Angular does not know that they are immutable and keeps checking them. So it is not efficient.
            - Custom immutable collections do not work with NgRepeat out of the box .
            - NgModel does not play nicely with immutable objects.

            Angular2 solves all of them.

            - The change detection gives you a mechanism to express that the component depends only on immutable objects and, therefore, should not be checked until one of the bindings gets updated, as follows:

            // There will be a way to express it declaratively
            class ImmutableTodoCmp {
            todo:Todo;
            constructor(private bingings:BindingPropagationConfig){}
            onChange(_) => bingings.shouldBePropagated(); 
            }

            - The change detection is pluggable, so you can teach it to detect changes in any collection.
            - The new Forms API is immutable object friendly.

            In Angular2 using immutable objects feels natural. You can pass an immutable collection to a repeater.

            <todo !foreach="var t in todos" [todo]="t">

            Use an immutable object in your template

            {{todo.description}}

            Even though Angular2 "just works", you will have to change the way your app is architected if you make most of your models immutable. This is not Angular specific. This will be true in any framework. That is why there are such things as Flux and Cursors (https://github.com/omcljs/om/w....

            see more

          • Reply
          • Share ›
          •  
             
            •  
               
              johnpapa7 Victor Savkin • 2 months ago

              It appears that json objects will have to be converted to an object of this type and cannot be used as is. Unless I misunderstand. Right?

              We may gain Perf but we lose something big there.

            • Reply
            • Share ›
            •  
               
              •  
                 
                Victor Savkin Mod johnpapa7 • 2 months ago

                Sorry, I was not clear. We can define Todo in TypeScript as follows:

                interface Todo {
                description:string;
                checked:boolean;
                }

                So Todo just defines the shape of the object. It can be a regular JSON object with the two fields.

              • Reply
              • Share ›
              •  
                 
    •  
       
      Sekib Omazic • 2 months ago

      Will A2 apply DOM changes all at once (batch update) like ReactJS? I couldn't find something like "task queue" in the code base.

    • Reply
    • Share ›
    •  
       
    •  
       
      Sekib Omazic • 2 months ago

      So to use immutable objects I have to inform angular2 about it by implementing onChange method in the component? And if I want to use observables I'd have to use objects of type Observable? 
      A working TodoApp would help better understand the change detection in A2.

    • Reply
    • Share ›
    •  
       
      •  
         
        Victor Savkin Mod Sekib Omazic • 2 months ago

        >> So to use immutable objects I have to inform angular2 about it by implementing onChange method in the component?

        You can use immutable and observable objects without telling Angular about it, but then you won't get any performance benefits out of it. To get the performance benefits you need to tell the framework that you depend only on immutable or observable objects.

        >> And if I want to use observables I'd have to use objects of type Observable?

        You won't have to use any special Observable class. Any push-based object can be used here (e.g., Object.observe or Rx observables). The framework does not make any assumptions about the type of your observables. Likewise, the framework does not make any assumptions about what is immutable.

        >> A working TodoApp would help better understand the change detection in A2.

        I am putting something together that will show advanced scenarios of using the change detection.

      • 2
      • Reply
      • Share ›
      •  
         
        •  
           
          Badru Victor Savkin • 2 months ago

          Waiting for that "something together" victor. It will help to understand better.
          One more question, the above syntax is not native js. Is it AtScript syntax?

CHANGE DETECTION IN ANGULAR 2的更多相关文章

  1. angular 2 - 006 change detection 脏治检查 - DC

    ANGULAR CHANGE DETECTION EXPLAINED 引发脏治检查有三种方式: Events - click, submit, - XHR - Fetching data from a ...

  2. [Angular & Unit Testing] Automatic change detection

    When you testing Component rendering, you often needs to call: fixture.detectChanges(); For example: ...

  3. [Audio processing] Harmonic change detection function (HCDF)

    Harmonic change detection function (HCDF) 是根据 Tonal Centroid (TC)实现的,首先TC如何提取? Step 1. 提取PCP特征 Step ...

  4. [Angular] Angular Custom Change Detection with ChangeDetectorRef

    Each component has its own ChangeDetectorRef, and we can inject ChangeDetectorRef into constructor: ...

  5. Angular中Constructor 和 ngOnInit 的本质区别

    在Medium看到一篇Angular的文章,深入对比了 Constructor 和 ngOnInit 的不同,受益匪浅,于是搬过来让更多的前端小伙伴看到,翻译不得当之处还请斧正. 本文出处:The e ...

  6. TWO PHASES OF ANGULAR 2 APPLICATIONS

    Angular 2 separates updating the application model and reflecting the state of the model in the view ...

  7. Angular 4+ 修仙之路

    Angular 4.x 快速入门 Angular 4 快速入门 涉及 Angular 简介.环境搭建.插件表达式.自定义组件.表单模块.Http 模块等 Angular 4 基础教程 涉及 Angul ...

  8. 来自 Thoughtram 的 Angular 2 系列资料

    Angular 2 已经正式 Release 了,Thoughtram 已经发布了一系列的文档,对 Angular 2 的各个方面进行深入的阐释和说明. 我计划逐渐将这个系列翻译出来,以便对大家学习 ...

  9. [Angular 2] How To Debug An Angular 2 Application - Debugging via Augury or the Console

    In this lesson we will learn several ways to debug an Angular 2 application, including by using Augu ...

随机推荐

  1. bzoj 4570: [Scoi2016]妖怪 凸包

    题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=4570 题解 我们知道如果一个怪物要取到攻击力的最大值,那么一定是把防御力都转化了 所以我 ...

  2. Python ---- list和dict遍历

    refer to: http://www.cnblogs.com/icejoywoo/p/3531869.html 对于python3, 可能有不一样之处,   refer to: http://do ...

  3. redhat 连接mysql数据库Can't get hostname for your address

    redhat 连接mysql数据库Can't get hostname for your address Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQ ...

  4. Remi 安装源

    Remi repository 是包含最新版本 PHP 和 MySQL 包的 Linux 源,由 Remi 提供维护.有个这个源之后,使用 YUM 安装或更新 PHP.MySQL.phpMyAdmin ...

  5. "WannaCry"勒索病毒用户处置指南

    "WannaCry"勒索病毒用户处置指南   原文: http://mp.weixin.qq.com/s/ExsribKum9-AN1ToT10Zog    卡巴斯基,下载官网:h ...

  6. C#使用OpcNetApi.dll和OpcNetApi.Com.dll操作OPC

    本人学习了一下.Net,恰好,51自学网,又要用这个.而网上很多VC6,VB6,VB .Net的但,很少C#的.现在研究一下,给出例子: 测试平台,是VS2008,KEPServer,OpcNetAp ...

  7. composer中文镜像

    王赛先生维护的 phpcomposer 全局修改 composer config -g repo.packagist composer https://packagist.phpcomposer.co ...

  8. <转--大话session>

    大话Session 原文地址:http://www.cnblogs.com/shoru/archive/2010/02/19/1669395.html 结语 到这里,读者应该对session有了更多的 ...

  9. 6.1课堂笔记—DML(数据操作语言),DQL查询语句

    一.DML(数据操作语言) InnoDB MyISAM 支持事务 不支持事务 不支持全文索引 支持全文索引 支持外键约束 不支持 命令查看默认存储引擎 show variables like '%st ...

  10. Vim中异步语法检查ale配置

    注意 在设置let g:ale_sign_error = '✗'和let g:ale_sign_warning = '⚡'这些时,可能vim不让你保存,提示fenc这个东西. 所以,为了保险起见,你最 ...