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. 在服务器上运行db:seed数据填充时,出错的问题解决

    在服务器上运行db:seed数据填充时,出错的问题解决 运行composer  dump-autoload

  2. apache伪静态设置

    在网站根目录下新建一个.htaccess文件即可,编辑如下 RewriteEngine On #游戏列表详细介绍 RewriteRule ^g-([0-9]+).html$ game.php?acti ...

  3. Chrome 上传时打开对话框非常慢

    Chrome 上传时打开对话框非常慢 先说解决方法,将 Chrome 中这个选项关闭,打开会飞快. 如果只是图片之类是不会的,但是有 zip apk 之类的就会慢. 主要原因还是 Chrome 太安全 ...

  4. 什么是HBase(五) HBase的运维工具

    常用工具 文件修复 hbck 文件查看 hfile WAL查看工具 hlog 压缩测试工具 compressTest(字段前缀编码以及block压缩设置后进行测试) 数据迁移工具copyTable 导 ...

  5. logback节点配置详解

    一 :根节点  <configuration></configuration> 属性 : debug : 默认为false ,设置为true时,将打印出logback内部日志信 ...

  6. C#多线程编程之:异步事件调用

    当一个事件被触发时,订阅该事件的方法将在触发该事件的线程中执行.也就是说,订阅该事件的方法在触发事件的线程中同步执行.由此,存在一个问 题:如果订阅事件的方法执行时间很长,触发事件的线程被阻塞,长时间 ...

  7. 《js笔记》

    1.判断浏览器是否启用cookie: if (navigator.cookieEnabled==true) { alert("已启用 cookie") } else { alert ...

  8. Linux新手入门:Unable to locate package错误解决办法

    最近刚开始接触Linux,在虚拟机中装了个Ubuntu,当前的版本是Ubuntu 11.10,装好后自然少不了安装一些软件,在设置了软件的源后,就开始了 sudo apt-get install,结果 ...

  9. Charles使用1

    Charles是一款比较常用的全平台的网络封包街区工具,而我们在做移动开发的时候,我们为了调试.测试.分析等目的,经常需要和服务端的网络通讯协议打交道.Charles可以帮我们截取网络数据包来进行分析 ...

  10. 操作表单域中的value值

    HTML <form action=""> <input type="radio" name="sex" value=&q ...