TWO PHASES OF ANGULAR 2 APPLICATIONS
Angular 2 separates updating the application model and reflecting the state of the model in the view into two distinct phases. The developer is responsible for updating the application model. Angular, by means of change detection, is responsible for reflecting the state of the model in the view. The framework does it automatically on every VM turn.
Event bindings, which can be added using the () syntax, can be used to capture a browser event execute some function on a component. So they trigger the first phase. In addition, Angular provides the Form module as a replacement for ng-model.
Property bindings, which can be added using the [] syntax, should be used only for reflecting the state of the model in the view (view state propagation).
EXAMPLE
Let’s look at an example showing the two phases. What we have here is a simple application with a list of tech talks, which you can filter, watch, and rate.

An Angular 2 application consists of nested components. So an Angular 2 application will always have a component tree. Let’s say for this app it looks as follows:

Finally, let’s define the application model that will store the state of our application:
{
filters: {speakers: "Rich Hickey"},
talks: [
{
id:898,
title: "Are we there yet",
speaker: "Rich Hickey",
yourRating:null,
rating: 9.1
},
...
]
}
Now, imagine an event changing the model. Let’s say I watched the talk “Are we there yet”, I really liked it, and I decided to give it 9.9.
The code snippet below illustrates one way to do it. The handleRate function is called when the user rates a talk. The Talk component just delegates to the App object that updates the model.
@Component({
selector: "talk",
bind: {talk: "talk"}
})
@Template({url: "talk.html"})
// render a talk object
class TalkComponent {
talk:Talk;
constructor(private app:App){}
handleRate(yourRating:number) {
this.app.rateTalk(talk, yourRating);
}
}
@Component({selector: "talks"})
@Template({url: "talks.html"})
//renders a list of talks
class TalksComponent {
talks:List;
constructor(mb:MessageBus) {
mb.onChange((updateModel) => this.talks = updatedModel.get("talks"));
}
}
// contains the business logic
class App {
model:Map;
constructor(private messageBus:MessageBus){}
rateTalk(talk:Talk, rating:number){
// model is immutable, so we have to construct a new model object
var updatedTalk = updateRecord(talk, {rating});
var updatedTalks = this.model.get("talks").replace(talk.id, updatedTalk);
this.model = this.model.set("talks", updatedTalks);
this.messageBus.emit("change", this.model);
}
}
This example is written using TypeScript 1.5 that supports type and metadata annotations, but it can be easily written in ES6 or ES5. You can find more information on annotations here https://docs.google.com/document/d/1uhs-a41dp2z0NLs-QiXYY-rqLGhgjmTf4iwBad2myzY/edit.
The updated model looks like this:
{
filters: {speakers: "Rich Hickey"},
talks: [
{
id:898,
title: "Are we there yet",
speaker: "Rich Hickey",
yourRating: 9.9,
rating: 9.1
},
...
]
}
At this point nothing has changed in the view. The DOM has not been updated yet. The model is updated, but did not use property bindings or change detection.
Next, at the end of the VM turn, change detection kicks in to execute the view state propagation.
First, change detection goes through every component in the component tree to check if the model it depends on changed. And if it did, it will update the component. In this example, the first Talk component gets updated:

Then, the framework updates the DOM. In our example, the rate button gets disabled because you cannot rate the same talk twice.

Note, the framework has used change detection and property bindings to execute this phase.
In our example we are using global state and immutable data. But even if we used local state and mutable data, it would not change the property that the application model update and the view state propagation are separated. I will explain why this is important below.
HANDLING FORMS
Angular 2 comes with the Forms module, which is useful for handling input. Say, we have a component responsible for adding talks to our database. This is how we can build it using Forms:
@Template({
inline: `
<form [control-group]="form" (submit)="createTalk(form.value)">
Title: <input control="title">
Description: <textarea control=-"description"></textarea>
Save as draft: <input control="isDraft" type="checkbox">
<button type="Submit">Create</button>
</form>
`
})
class CreateTalkComponent {
constructor(b:FormBuilder, private app:App) {
this.form = b.group({
title: ["", required],
description: "",
isDraft: [false, required]
});
}
createTalk(talkData:
{title:string, description:string, isDraft:boolean}) {
this.app.createTalk(talkData);
}
}
First, we declare a form object in a component. In this example it has three fields: title, description and isDraft. The title and isDraft fields are required fields, and description can be blank. Then, we bind to this form in the component’s template and get the value of the form on submission. You can read more about Forms in Angular 2 in this blog post.
Why Forms instead of ng-model? Since the only purpose of change detection in Angular 2 is propagating the view state, the generic two-way data-binding mechanism, like the one in Angular 1, does not add much value. But it is the source of a lot of performance penalties. First, it is easy to misuse to mix the application model update and view state propagation. Second, is that it can create cycles in the change detection graph, which makes the system hard to reason about. That is why complex forms built with ng-model can be non-trivial to reason about.
We believe the Angular Forms module is easier to use and reason about than ng-model, it provides the same conveniences as ng-model, but does not have its drawbacks. We think it is a big improvement over ng-model. Also, to make the migration of Angular 1 apps easier, we are planning to implement the Forms module in Angular 1.
WHY?
Now, when we have understood how we separated the concerns, let’s look at why we did it.
PREDICTABILITY
First, using change detection for the view state propagation limits the number of places where the application model can be changed. In this example it can happen only in the rateTalk function. A watcher cannot “automagically” update it. This makes ensuring invariants, and in general, reasoning about your application model easier.
Second, our reasoning about the view state propagation has been drastically simplified. Consider what we can say about the Talk component just by looking at it in isolation. Since we use immutable data, we know that as long as we do not do talk= in the Talk component, the only way to change what the component displays is by updating the binding. These are strong guarantees that allow us to think about this component in isolation.
It is hard to achieve this kind of guarantee in Angular 1.x.. First, talk=new Talk() could potentially result in cascading changes if the talk were two-way bound to some property on the parent component. Second, since the Angular 1.x way of dealing with forms is to create an ng-model binding, it would be hard to keep Talk immutable.
Finally, by explicitly stating what the application and the framework are responsible for, we can set different constraints on each part. For instance, it is natural to have cycles in your application model. So the framework should support it. On the other hand, html forces components to form a tree structure. So we can model the change detection graph as a tree, which makes the system more predictable.
Let me give you one example. Can you tell which directive will be updated first: Parent or Child?
<parent prop1="exp1" prop2="exp2">
<child prop3="exp3" prop4="exp4"></child>
</parent>
In Angular 1.x the update can be interleaved. How many times prop1 is updated in the same digest run? It can more than 1. This makes implementing certain patterns very difficult. It also makes troubleshooting certain types of problems challenging: you just cannot reason about the template statically, you have to know how the model gets updated.
In Angular 2, you know that Parent will always be updated before Child, and we also know that a property cannot be updated more than once.
Angular 2 makes reasoning about the component easier because it limits the number of ways it can be modified, and makes this modification predictable.
PERFORMANCE
A big part of the separation is that it allows us to constrain the view state propagation. This makes the system more predictable, but it also makes it a lot more performant. For example, the fact that the change detection graph in Angular 2 can be modelled as a tree allowed us to get rid of digest TTL. Now the system gets stable after a single pass.
HOW DOES ANGULAR ENFORCE IT?
What happens if I try to break the separation? What if complect the two phases by changing the application model inside a setter that is invoked by the change detection system?
Angular tries to make sure that the setter you define for you component should only update the view state of this component or its children, and not the application model. To do that change detection will walk the graph twice in developer mode. First time to propagate changes, and second time to make sure there are no changes. If it finds a change during the second pass, it means that one of your setters updated the application model, the framework will throw an exception, giving you the feedback you need to ensure it runs properly in production.
WHAT ABOUT ANGULAR 1.X?
Although Angular 1.x was not built with this separation in mind, you can still write your code in this way to make your migration easier. Here’s a few things you can do:
- Prefer one-way data-bindings to two-way data-bindings.
- Do not update application state inside watch functions.
- Try to implement unidirectional data-flow in your application. Read more about it here.
SUMMARY
- Angular 2 separates updating the application model and the view state propagation.
- Event bindings trigger the application model update phase.
- Change detection uses property bindings to execute the view state propagation phase. The view state propagation is unidirectional and top-down. This makes the system a lot more predictable and performant.
- Angular 2 embraces unidirectional data-flow and provides the Angular Forms module instead of ng-model.
- You can use the same mindset when building Angular 1.x applications.
- Csaba Tamas • 16 days ago
Hy! Can You share the working full source code? It would be a great help for me, becasue I worked on Angular 2 POC and I can't how buld component hierarchy. I using only component-decorator tree like: yet: https://github.com/angular/ang...
Thanks!
Victor Savkin Mod Csaba Tamas • 16 days agoThis would be one example: https://github.com/angular/ang.... More are coming.
Jinto Jose • 14 days agoNice post. Just a small doubt. AngularJS 2.0 is following the concepts which ReactJS is already having?
Victor Savkin Mod Jinto Jose • 14 days agoThank you. Glad you liked the post.
The idea of being more explicit about the data flow, in my opinion, is a good one. But I would not think about it as a feature or a capability. Rather you design other capabilities in the way that allows that.
You are right in the sense that React developers do a great job paying attention to the data flow in their applications. I think this is important, and that is why Angular 2 is designed with the data flow management in mind. But the fact that Angular 2 makes data flow more explicit does not mean it is React now.
seba Victor Savkin • 5 days agoThen what is the advantage of angular over react, or what does it do different?
Victor Savkin Mod seba • 5 days agoThis is a very generic question. It is similar to asking if Backbone, Ember, Angular 1 are different from each other, or which one is better. The answer is it depends on your experience, your application's architecture, etc.
Yevgen Bushuyev • 16 days agoHi,
Could you please have a look at http://stackoverflow.com/tags/... and give some answers.
Thanks!
Sekib Omazic • 18 days agoCould you say a few words about MessageBus? Is this a BindingPropagationConfig and EventEmiiter combined together or something totally different?
Victor Savkin Mod Sekib Omazic • 17 days agoMessageBus is here used for illustrative purposes, so it is not provided by Angular. It allows App to broadcast events, and it can be implemented using EventEmitter.
TWO PHASES OF ANGULAR 2 APPLICATIONS的更多相关文章
- JHipster - Generate your Spring Boot + Angular/React applications!
JHipster - Generate your Spring Boot + Angular/React applications!https://www.jhipster.tech/
- [Angular 2] ngrx/store
@ngrx/store builds on the concepts made popular by Redux and supercharges it with the backing of RxJ ...
- Translate Angular >=4 with ngx-translate and multiple modules
原文:https://medium.com/@lopesgon/translate-angular-4-with-ngx-translate-and-multiple-modules-7d9f0252 ...
- 史上最全的Angular.js 的学习资源
Angular.js 的一些学习资源 基础 官方: http://docs.angularjs.org angularjs官方网站已被墙,可看 http://www.ngnice.com/: 官方zi ...
- JavaScript周报#183
This week’s JavaScript news Read this issue on the Web | Issue Archive JavaScript Weekly Issue 183Ma ...
- [Angular] Use Angular components in AngularJS applications with Angular Elements
When migrating AngularJS (v1.x) applications to Angular you have different options. Using Angular El ...
- [Angular 8] Take away: Tools for Fast Angular Applications
Based on the talk from NG-CONF. Check it out by yourself, here is just my own take away :) Different ...
- Angular之Providers (Value, Factory, Service and Constant )
官方文档Providers Each web application you build is composed of objects that collaborate to get stuff do ...
- angular 学习理解笔记
原文:https://github.com/eoinkelly/notes/blob/master/angular/book-building-web-apps-w-angular/angular.m ...
随机推荐
- soql取第一件数据
User u = [select ID,Name from User Limit 1];
- django 模板关闭特殊字符转化
默认情况下,在django admin管理界面和页面中,如果输出的对象中包含HTML特殊字符,在django中默认的处理方式是将对象中的HTML特殊字符转化,例如会将 "<" ...
- angular指令的详细讲解,不断补充
独立作用域:就是在指令中设置了scope: **** ·false 共享父作用域 ·true 继承父作用域,并且新建独立作用域 ·object 不继承父作用域,创建新的独立作用域 一般来说我们会使用第 ...
- mac自己安装python的路径
自己安装的python包的路径( 编辑器 import sys print sys.path 可以看到很多需要的东西的位置 如python的解析器等) /Library/Python/2.7/s ...
- linux下今天遇到的问题
之前由于测试需要,必须用mysql5.7的客户端, 现在由于产品完善,开始支持5.6,所以需要装5.6的客户端做测试,考虑到手工测试的效率及不可重复性,准备自动化执行原来的用例. 老的用例是用MySQ ...
- python3调用阿里云语音服务
步骤 1 创建阿里云账号,包括语音服务里的企业实名 为了访问语音服务,您需要有一个阿里云账号.如果没有,可首先按照如下步骤创建阿里云账号: 访问阿里云 官方网站,单击页面上的 免费注册 按钮. 按照屏 ...
- Java之POI的excel导入导出
一.Apache POI是一种流行的API,它允许程序员使用Java程序创建,修改和显示MS Office文件.这由Apache软件基金会开发使用Java分布式设计或修改Microsoft Offic ...
- 在window的IIS中搭配Php的简单方法
在window的IIS中搭配Php的简单方法.搭配php的时候找到的一个超级简单方法 关键的核心是 PHP Manager for IIS 这是微软开发的一个项目,使用它可以在window下最方便简单 ...
- 好久不见(致win7)
7月8号,电脑上装了pgp,然后说让重启,重启之后蓝屏,自此,就一直蓝屏了 电脑装了双系统,工作时用centos,我不愿重装系统,怕centos受影响 网上说安装模式下可以卸载软件,可我在安全模式下, ...
- unicat,multicast,broadcast区别
单播.多播和广播单播”(Unicast).“多播”(Multicast)和“广播”(Broadcast)这三个术语都是用来描述网络节点之间通讯方式的术语.那么这些术语究竟是什么意思?区别何在? 1.单 ...