Two Phases of Angular Applications

Victor Savkin
Angular
Published in
6 min readJul 13, 2016

--

Victor Savkin is a co-founder of nrwl.io, providing Angular consulting to enterprise teams. He was previously on the Angular core team at Google, and built the dependency injection, change detection, forms, and router modules.

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.

Property bindings, which can be added using the [] syntax, should be used only for reflecting the state of the model in the view.

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:

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 shows one way to do it. The handleRate function is called, via an event binding, when the user rates a talk.

The example is written using immutable objects, which results in a few good properties. But it is worth noting that Angular doesn’t require you to use immutable objects, and the example can be rewritten using mutable POJO objects.

All right, after rateTalk executes, the updated model will look like this:

At this point nothing has changed in the view. Only the model has been updated.

Next, at the end of the VM turn, change detection kicks in to propagate changes in the view.

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.

Why?

Now, when we have understood how we’d separated the two phases, let’s talk about why we did it.

Predictability

First, using change detection only for updating the view state 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 easier, which makes code easier to troubleshoot and refactor.

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 only Angular 1.x way of dealing with forms is to create an ngModel 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. We can take advantage of this and make 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: the static template just does not give you enough information to see what is going on. 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 modeled 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 I try to change 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 only updates the view state of this component or its children, and not the application model. To do that Angular will check your bindings 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.
  • Be explicit about how data flows in your application. For example, try to implement unidirectional data-flow. Read more about it here.

Summary

  • Angular 2 separates updating the application model and updating the view.
  • Event bindings are used to update the application model.
  • Change detection uses property bindings to update the view. Updating the view is unidirectional and top-down. This makes the system a lot more predictable and performant.
  • Angular 2 embraces unidirectional data-flow.
  • You can use the same mindset when building Angular 1.x applications.

Victor Savkin is a co-founder of Nrwl. We help companies develop like Google since 2016. We provide consulting, engineering and tools.

If you liked this, click the 👏 below so other people will see this here on Medium. Follow @victorsavkin to read more about monorepos, Nx, Angular, and React.

--

--

Nrwlio co-founder, Xoogler, Xangular. Work on dev tools for TS/JS. @NxDevTools and Nx Cloud architect. Calligraphy and philosophy enthusiast. Stoic.