Angular Development Mode: Understanding Unidirectional Data Flow
Unidirectional Data Flow is a core concept in modern web frameworks. Let's examine what this term actually means and how it applies specifically to Angular applications.
Along the way, we'll also explore Angular's development mode, understand why it matters, and learn how to debug issues when they arise. Let's begin by looking at the "Data" component of Unidirectional Data Flow.
For those who prefer video content, here's a presentation covering the same material:
Which Data Are We Talking About?
When we mention "data" in the context of Unidirectional Data Flow, we're referring to the application state. Specifically, this means the View Model information that gets passed to the template for display purposes.
Consider this straightforward example—it's essentially a plain JavaScript object:
To render this course object on screen, we need a Component that defines a template. That template dictates how the data should be presented visually.
Here's what an Angular template looks like:
Note: throughout this discussion, we're assuming a standard web application rather than a native mobile or server-side implementation.
In Angular, templates can be defined inline as strings, but they can also live in separate external files.
One thing you'll quickly notice is that Angular provides detailed, helpful error messages whenever something goes wrong with a template. This clearly indicates that Angular isn't merely treating templates as plain strings. So what's the actual mechanism?
How Does Angular Process Templates Internally?
What happens behind the scenes when Angular uses a template to display data?
The explanation is actually pretty straightforward: Angular applies a function to the data, and the result of that function is a DOM structure that corresponds to the defined HTML template.
You might assume the process works something like this:
- Angular takes the data, substitutes variables into the template, and produces raw HTML
- That HTML gets handed off to the browser
- The browser parses the HTML and builds a DOM tree
- The rendering engine then paints the view onto the screen
But that's not quite what happens. The reality is similar, yet with one crucial distinction:
Rather than generating HTML strings, Angular constructs DOM data structures directly.
In our example, Angular uses a component renderer to build the DOM structure straight from the data.
For native mobile or server-side applications, the renderer would produce a different output type. But for our purposes, the renderer outputs a DOM tree that represents the component's view.
This renderer gets initialized through code that resembles the following:
That function initializes the renderer for the AppComponent shown earlier. The variable names might look unusual, but the intent is clear:
- A DIV element is being created
- The CSS class "course" is applied to it
- A span with the "description" class is added inside
- Text content is placed within that span
As you can see, this function maps directly to the template we defined above.
Where Can I Find This Function for My Components?
Curious about how your own components look? Open Chrome Dev Tools, navigate to the Sources tab, press Ctrl + O, and type your component's name.
You should see a directory named after your component, containing a file called component.factory.js.
At this point you might wonder where this function originates. The answer is that the Angular compiler generates it based on your component's template.
When Does This Code Get Generated?
The timing depends on how Angular is running. In Just-In-Time (JIT) mode, these functions are created during application startup in the browser.
That means the compiler must be shipped to the browser, and users have to wait for compilation to finish before any data appears on screen.
Alternatively, with Ahead-of-Time (AOT) compilation, the function is generated during the build step rather than at runtime. This approach yields two performance benefits:
- The compiler doesn't need to be sent to the browser since template compilation happens at build time
- Startup is faster because templates don't need compiling in the browser
So how does all this connect to unidirectional data flow?
What Flow of Data Are We Referring To?
Now that we understand how Angular converts data into DOM structures, let's trace the complete process.
Consider an event handler, like a button click. Inside that handler, we can freely modify application data at any component tree level. However, once the handler finishes, control returns to the Angular framework.
Before the JavaScript VM turn ends, Angular takes several steps:
- It traverses the entire component tree, starting from the root component
- For each component, it runs change detection to determine if a re-render is needed
- If re-rendering is required, it executes the component's DOM-generating function to produce an updated DOM structure
- This proceeds from the root down through all leaf components
- Along the way, various lifecycle hooks fire, including
ngAfterViewChecked
The "data flow" in Unidirectional Data Flow refers to how application data moves from component classes into the resulting DOM structures during this downward rendering pass.
What Makes Data Flow Unidirectional?
During the rendering sweep, template expressions get evaluated and lifecycle hooks execute across the entire component tree. Essentially, our own code runs during this process.
The key constraint is ensuring that the view-generation process itself doesn't accidentally modify the underlying data.
Data should flow from component classes into the DOM structures that represent them, but creating those DOM structures shouldn't trigger further data changes.
Why Is This Constraint Necessary?
Without it, we'd face an unpleasant choice:
- We'd need to repeat the rendering process repeatedly until the data settles down
- Or we'd accept a state where the view doesn't accurately reflect the data after rendering completes
Neither option is desirable, for these reasons:
- Inconsistent data and view states make applications difficult to reason about
- Repeated rendering passes waiting for stabilization can create serious performance problems
This is precisely why data must flow in one direction only—from component classes to the view—and never back. That's the essence of unidirectional data flow.
Why Does Unidirectional Data Flow Matter?
The importance is twofold. First, it promotes excellent rendering performance.
More critically, it keeps applications predictable: once our event handlers return and the framework takes over rendering, we can trust that nothing unexpected will happen.
With Angular, we get built-in framework protection against a whole class of difficult-to-debug issues: data/view inconsistency bugs.
A Practical Demonstration—Breaking Unidirectional Data Flow
Here's an example of components that violate the unidirectional data flow rule. Can you spot the problem?
Did you identify it? The issue lies in the template expression description—a TypeScript getter that returns a different value each time it's called because it uses Math.random().
This is a simplified example, but performing mutations within supposedly read-only operations can easily trigger the same problem.
Running this program produces 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'.
As you can see, Angular's development mode caught a program that would be extremely difficult to debug and maintain due to runtime inconsistencies.
Another Common Way to Break Unidirectional Data Flow
Lifecycle hooks are another frequent source of these issues. Let's examine an example that also violates the rule:
This application produces an error identical to the one above. The culprit here is the ngAfterViewChecked method, which executes after the view has already been generated. Modifying data at that point recreates the "view updates data during rendering" scenario.
So how exactly did Angular detect and prevent this problem?
Understanding Angular Development Mode
Angular's development mode is what caught the issue. Here's how it works: Angular runs the entire rendering process a second time, specifically to verify that the first pass didn't alter any application data.
If any template expression changes between the first and second rendering passes, Angular throws an error like the one we saw above.
How Can We Avoid These Errors?
While such situations are relatively uncommon, here's a potential fix if you encounter this error message.
One option is deferring data modifications to the next JavaScript VM turn using setTimeout with no delay:
This schedules the function inside setTimeout to execute after the current JavaScript turn concludes. When the callback runs and control returns to the framework, the rendering process executes again.
At that point, data changes get reflected on screen—but this time no error is thrown.
Keep in mind that setTimeout isn't always the right answer. Sometimes refactoring the code to move operations outside lifecycle methods is a better approach.
Final Thoughts
The examples above illustrate why keeping Angular's development mode enabled throughout development—and disabling it only for production—is so valuable.
Development mode helps us create applications that are predictable and maintainable. Data stays synchronized with the view transparently, and the framework provides strong guarantees against data/view inconsistency bugs.
If you found this discussion helpful, you might enjoy exploring the other Angular resources listed below.
Consider subscribing to our newsletter to stay updated on future posts:
New to Angular? Check out the Angular for Beginners Course:
More Angular Articles
Here are some other popular posts you might find valuable:
- 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 ?
