An important feature of web frameworks is Unidirectional Data Flow: let's talk about what does the term mean, and what it corresponds to in Angular.
Related to it, let's also learn about the Angular development mode, why it's important to use it and how we can troubleshoot it if needed. Let's start first with the Data part of the term Unidirectional Data Flow.
Here is a video on the same topic as this post:
What Data are we referring to?
The data that we refer to in the term Unidirectional Data Flow is simply the data of our application. More concretely, it's the View Model data that gets passed to the View to be rendered on the screen.
Here is an example, as we can see it's just a plain JavaScript object:
If we want to display the course object on the screen, we then need to define a template via a Component, and the template will define how the data should be displayed.
Here is an example of an Angular template:
Note: we are assuming in this post that the application is a web application, and not a native mobile application or a server-side application
So as we can see in Angular the template can simply be just an inline string, but it's also possible to put it in an external file.
You will notice that in Angular whenever something goes wrong with the template we will get helpful error messages. So clearly Angular is not handling the template simply as a string. So how does that work?
How is the template used by Angular internally?
How does Angular use the template to display the data of the screen, what is going on under the hood?
It's actually quite simple: Angular is taking the data and it's applying it a function. The output of that function is the DOM data structure that corresponds to this HTML template.
So we might think initially that what was going on was the following:
- Angular takes the data and creates the actual HTML based on the template by replacing some variables
- it then passes this HTML to the browser
- the browser then parses the HTML and generates a DOM data structure
- the browser rendering engine then renders the view on the screen
But this is actually not the case, instead something close to that is going on, but with one important difference:
Angular is not generating HTML as a string, its generating DOM data structures directly.
For example, in our case Angular will generate the DOM data structure directly from the data via a component renderer.
If the application would be a native mobile application or a server-side application then the output of the renderer would be something else. But in this case, the output of the component renderer is the DOM tree that represents the component view.
This DOM component renderer gets defined via something similar to the following code:
This is the function that initializes the renderer of the AppComponent
that we have shown above. The name of the variables do look strange, but we can see the overall idea of what is going on here:
- we are creating a DIV
- we are adding it the CSS class course
- we are adding inside it a span with the CSS class description
- inside the span we are adding some text
So was we can see, this function does correspond to the template that we have just defined above.
Where can I find this function for my components?
If you want to see a similar function for one of your components, simply go to the Chrome Dev Tools Sources tab, hit Ctrl + O and type in the name of your component.
You should find a folder with the name of your component, and inside it, a file named component.factory.js
.
You might be wondering at this point, where does Angular get this function from? This function is simply generated by the Angular compiler, based on the component template.
When is this code generated?
Depending on the way that we are running Angular, this code will be generated at different moments in time. If we are using Angular in JIT mode, then these functions are generated at the application startup in the browser.
This means that we need to ship the compiler to the browser, and that we need to wait for this process to finish before being able to display data on the screen.
We might also be running Angular in Ahead of Time Mode, or AOT. This means that this function was generated at build time, but not at application startup time. This also means that the application will be faster because of two reasons:
- we don't need to ship the compiler to the browser, because the template compilation is done built time
- we don't need to compile the templates at startup, so the application will start up faster
So what does this have to do with unidirectional data flow?
What flow of data are we referring to?
Now that we know how Angular it transforms the data into DOM data structures, let's see how the whole process works.
For example: If we trigger an event handler such as for example a button click handler, we can modify the data of the application as much as we want at any level of the component tree. But once the click handler code returns, the control is yielded back to the Angular framework.
Before ending the JavaScript VM turn, Angular is going to do the following:
- Angular will go through the whole component tree starting at the root component of our application
- for each component, Angular is going to run the change detection mechanism that is associated with that component and will determine if the component needs to be re-rendered
- if the component needs to be re-rendered, Angular is going to run its DOM generating function, that produces a new DOM data structure that corresponds to a new version of the component View
- this process is done from the root of the component tree down until the leaf components of the application
- during this process for each component several lifecycle methods are called, such as for example
ngAfterViewChecked
So the Data Flow that we are referring to in the term Unidirectional Data Flow is the flow of application data that goes from the component classes into the output DOM data structures generated by the rendering process, during the top to bottom rendering sweep of the component tree.
What does it mean for the flow of data to be unidirectional?
During the rendering process, template expressions are evaluated and lifecycle hooks are called all over the component tree. This means that code that we have written is being called during that process.
We want to make sure that during that process where we are transforming data into a view, that the view generation process itself does not further modify the data.
The data flows from the component classes to the DOM data structures that represent them, but the act of generating those DOM data structures does not itself cause further modifications of the data.
Why would we want to avoid that?
Because that would cause the following, we would have to either:
- repeat the process multiple times until the data stabilizes
- or leave the data and the view in an inconsistent state where the view after the rendering process finished does not reflect the actual state of the data
Both of these outcomes are to be avoided, let's see why:
- having the data and the view in an inconsistent state would lead to an application that is hard to reason about
- repeating the process multiple times waiting for the data to stabilize could lead to performance issues
And that is why it's important that the data only flows from the component classes to the view during rendering, and not the other way around - which explains the term unidirectional data flow.
Why is unidirectional directional data flow important?
We can see know why this property is important: first because it helps to get great performance out of the rendering process.
But most of all, because it ensures that the application is simple to reason about: it ensures that whenever our event handlers return and the framework takes over to render the results, nothing unpredictable occurs.
Using Angular means that we have built-in framework protection against a whole category of bugs that are very hard to troubleshoot otherwise: data vs view inconsistency bugs.
Let's do a Demo - How to break unidirectional data flow?
For example, the following components will break that unidirectional data flow rule - try to spot why:
Did you see where the problem was? The problem is that we have a template expression which is description
(which is a Typescript getter) that returns different values each time that we call it due to the use of Math.random()
.
This is just a simple example, but in general doing modification operations inside operations that should be read-only could easily lead to the same situation.
If we run this program we are going to get the following error:
EXCEPTION: Error in ./AppComponent class AppComponent - inline template:2:34 caused by: Expression has changed after it was checked. Previous value: 'Angular For Beginners0.4769352143577472'. Current value: 'Angular For Beginners0.8202702497824956'.
So as you can see the Angular Development Mode prevented us from writing a program that would be very hard to reason about - a program like this would have all sorts of inconsistencies at runtime.
Another common way of breaking unidirectional data flow
Another frequent situation where this type of issue tends to occur is when using certain lifecycle hooks. Let's see here another example of an application that also breaks unidirectional data flow:
This application will trigger an error identical to the one above. What happened here is that the method ngAfterViewChecked
is called after the view is already generated, so if we would modify here further the data we would fall into the same 'view updates the data while rendering' scenario.
So how did Angular detected and prevented this problem in our application?
The Angular Development mode
Angular detected this issue thanks to its development mode. The way that the development mode works is the following: Angular will run the whole rendering process a second time to make sure that the first rendering pass did not cause further changes to the application data.
If angular finds that any of the template expressions changed between the first and the second rendering pass, it will throw an error similar to the one above.
So how can we avoid such errors?
These types of situations will be relatively rare, but in case you run into a similar error message here is a potential solution.
We can always defer the data modifications to the next JavaScript VM turn by using setTimeout
without any delay:
This will schedule the function inside setTimeout
for execution after the current JavaScript turn ends. Once the setTimeout
callback is executed and control is yielded back to the framework the rendering process will take place again.
At that time any further data changes will be reflected on the screen - but no error will be thrown this time.
Using setTimeout
is not always the solution for this type of problems, we can for example try to refactor our code to implement the same functionality in a different way, maybe outside a given lifecycle method if possible.
Conclusions
With this last example, we can see why it really helps if we use the Angular development mode at all times during development, and only turn it off in production.
This is because the development mode helps us to build an application that is easy to reason about and maintain, where the data is always kept in sync with the view in a transparent way by the framework - and we have a strong guarantee by the framework that we won't run into data vs view inconsistency bugs.
I hope you enjoyed the post, I invite you to have a look at the list below for other similar posts and resources on Angular.
I invite you to subscribe to our newsletter to get notified when more posts like this come out:
If you are just getting started learning Angular, have a look at the Angular for Beginners Course:
Other posts on Angular
If you enjoyed this post, have also a look also at other popular posts that you might find interesting:
- Angular Router - How To Build a Navigation Menu with Bootstrap 4 and Nested Routes
- Angular Router - Extended Guided Tour, Avoid Common Pitfalls
- Angular Components - The Fundamentals
- How to build Angular apps using Observable Data Services - Pitfalls to avoid
- Introduction to Angular Forms - Template Driven vs Model Driven
- Angular ngFor - Learn all Features including trackBy, why is it not only for Arrays ?
- Angular Universal In Practice - How to build SEO Friendly Single Page Apps with Angular
- How does Angular Change Detection Really Work ?