In the last week, I’ve been trying to make sense how two-way data binding (Angular) and one-way data flow (React/Flux) are different. They say that one-way data flow is more powerful and easier to understand and follow: it is deterministic and helps avoiding side-effects. In my newbie eyes though, they both look pretty much the same: the view listens to the model, and the model reacts on actions done to the view. Both claim that the model is the single source of truth.
Could anybody comprehensively explain in understandable way how they are really different and how one-way data flow is more beneficial and easier to reason about?
Angular's two-way data binding
It's made possible by a mechanism that synchronizes the view and the model whenever either change. In Angular, you update a variable and its change detection mechanism will take care of updating the view, and viceversa. What's the problem? You don't control the change detection mechanism. I found myself having to resort to ChangeDetectorRef.detectChanges or NgZone.run to force the view to update.
To not dive too deep into change detection in Angular, you trust it will update what you need when you change a variable, or when it gets changed after an observable resolves, but you'll find you have no idea how and when it runs, and sometimes it will not update your view after a variable changes. Needless to say, it can sometimes
be a pain to find where and when a problem occured.
React's one-way data flow
It means that the view always gets its state from the model. To update the view, you need to update the model first, and then redraw the view. React makes the view redrawing process extremely efficient because it compares not the actual DOM but a virtual DOM it keeps on memory. But how does change detection work in this dynamic? Well, you trigger it manually.
In React, you set the state's new value, which then causes a ReactDOM.render, which causes the DOM comparing/updating process. In React/Redux you dispatch actions which update the store (single source of truth) and then the rest. Point is, you always know when the stuff changes, and what caused the change. This makes problem solving quite straight forward. If your app depends on the state, you look at it before and after the action that triggered the change, and you make sure variables have the value they're supposed to.
Implementations aside
From a platform independent point of view, they're not so different. What separates one-way flow from two-way binding is a variable update on change. So your impression that that they're conceptually not too far from each other is not too divorced from their practical uses.
In Angular you have many controllers. One example would be a user triggering an action on View 1 that is managed by Controller 1. Controller 1 does something but also fires an event that is caught by another Controller 2. Controller 2 updates some property on the $scope and View 2 is suddenly changed.
Suddenly an operation on View 1, updated View 2. If we now throw in some Async callbacks and a bit more event chains, you might no longer know exactly when/how your views are being updated.
With Flux/Redux, you have a one way data flow. The view never updates the model, the views can only dispatch an action (intention to update), but lets the store/reducer deciding how to handle the update. You can more easily reason about the data flow because you can easily see which actions can be fired by each view. Then follow up to see how that action is being handled by the store and you can know exactly what can be updated.
Data flow here is a flow of write events - i.e. state updates
These events are flowing between views and controllers (and services, such as HTTP backends)
One-way flow is basically the giant cycle:
app view uses (reads, not writes) app state to render
when application gets some stimuli from outside (user typed some text in input field, or result of HTTP request has arrived), it emits write event - or, in Redux/Flux slang, dispatches an action
all events, from all controllers and views, are flowing into the single sink - dispatch function (reducer); although the nature of dispatch function allows it to be composed from simpler dispatch functions, conceptually, there's only one dispatcher for the whole app
dispatcher uses an event to figure out which part of the state is to be updated
go to start
Two-way flow aka data binding binds two pieces of state: in most cases, one inside the controller (e. g. some variable), and one inside the view (e. g. contents of textbox). Binding means that, when one piece changes, the other piece changes as well and gets the same value, so you can pretend that there's only one piece of state involved (while there's two actually). Write events are going back and forth between controllers and views - thus two-way.
Data-binding is cool when you need to figure out what variable holds the contents of this particular textbox - it shows immediately. But it requires complex framework to maintain the illusion of one piece of state where there's two pieces really. Usually you'll be forced to use framework-specific syntax to write your views' code - i. e. to learn yet another language.
One-way data flow is cool when you can leverage that extra entity - events flow. And, usually, you can - it's useful for Undo/Redo, user actions replay (e. g. for debug), replication, etc, etc. And the code to support this is much, much simpler, and usually can be written in plain JavaScript instead of framework-specific syntax. On the other hand, since you no longer have data-binding, it no longer saves you some boilerplate.
Also, see great visual explanation in this answer: https://stackoverflow.com/a/37566693/1643115. Single-headed and two-headed arrows visually represents one-way and two-way data flow respectively.
Let's say your app is just a wizard flow, but it has some complex interactions i.e. one step might change a following step behavior.
Your app is running great, but one day an user reports a bug on one of the tricky steps.
How does debugging would work on two-way binding and one-way binding?
Two-way binding
I'd start checking what behavior is different and with some luck, get to the same point as the user and pinpoint the bug. But at the same time there might be some weird interaction between different parts of the app. I might have some data-binding that is incorrect (e.g. replicating the model state but not binding) or other weird intricacy between components that is hard to debug. It might be hard to isolate the bug.
One-way binding
You just grab the state object. It has all the information of the app currently in a big javascript object. You load the same state in your development environment, there is a big chance your app will behave exactly the same. You can even write a test with the given state for regression and pinpoint the exact problem that is happening.
Conclusion
In a few words, one-way binding makes it very easy to debug complex apps. You don't have to do much then copy over the current state of the user.
Even that doesn't work, you can log the actions as well. There isn't AFAIR an easy way to track all the state modifying actions on Angular, for instance. With Redux it's pretty, pretty easy.
During profiling of my angularjs application, I found that sometimes $get.Scope.$eval takes more than 100ms. During the single $digest loop there are at least 3 long cases of $get.Scope.$eval and I'd like to optimize this part.
Below the $get.Scope.$eval in profiler I see only invoking of angularjs code.
Here is a screenshot of profile chart.
Could anyone suggest, what's going on and how to optimize this part? I suppose that it can be caused by ng-repeat, ng-include or ng-if, but I'm not sure.
Update:
Here is the simplified structure of my application. Probably, the problem is in the architecture of my application. The app mostly working on the single route and change it only for 3 cases, so application store state in the global controller AppController - fat controller. Also there are 20k+ nodes in the html and the number can grow(maximum I saw is 60k)
$eval is used internally by angular when resolving angular expressions, such as {{variable}}. Without seeing any of your code, it's hard to tell what expressions are using resources unnecessarily, but usually too large or nested ng-repeats (or many ng- directives included within an ng-repeat) are a code smell.
Angular uses dirty checking to evaluate these expressions (for lack of a better option) - that means that every time you create a binding with the {{}} syntax, it creates an implicit $watch expression getting that value, that will be called every digest cycle to see if the value has changed (on which change the relevant parts of the DOM are regenerated).
Here's one optimization I've used successfully in the past:
most of the time, when you bind a value with {{}}, you don't actually expect the value to change (like a label), and this 2-way data binding is completely superflous. Since angular version 1.3 you have the options to create one-time bindings with the :: syntax:
One-time expressions will stop recalculating once they are stable, which happens after the first digest if the expression result is a non-undefined value
which eliminates the corresponding performance overhead of such bindings. (If you're using older angular versions, external libraries can mimick this behaviour, such as bindonce.)
Here are some additional tools I've found useful while profiling/optimizing angular apps:
Batarang, a chrome extension "for debugging and profiling angular applications"
This stackoverflow answer, giving a neat solution for counting how many watch expressions are active on your page. A rule of thumb is that if you're above 2000, you'll start noticing performance issues, and you should think about changing your architecture - employ lazy loading mechanisms, reconsider whether you truly need all the bindings etc.
In production environments disabling the default "debug mode" of angular can also help with performance.
I know a lot of the big name MVC, M** style JavaScript frameworks allow 2-way binding with Observables.
Meaning if I:
Update a DOM input filed, it will also update a JavaScript object variable which could also save to server with AJAX request.
If I update the JavaScript object variable, it will also in turn update the DOM text field.
As I am learning JavaScript, I would love to skip the big name libraries and learn how to do this in it's most basic raw JavaSript form.
Can anyone provide quick easy to understand demo of this functionality without using libraries like BackboneJS, Knockout, Angular, or others?
jQuery is acceptable.
I would appreciate the lesson and help please.
This is different in every framework.
Angular for instance saves every variable on scopes. Iterate through the scopes variables and compare the values with the previous ones and if there is a change it is carried out to the DOM.
This check is made upon they call digest cycles. If one cycle is finished it calls again until every variable 'observed' is the same as it was in the previous cycle. You can also add objects or vars to this 'observer'.
Angular keeping the view current, with calling this digest every time something could change the 'observed' vars, like http calls, user interactions ... but for instance, if you change a variable outside from angular (console), than the variable change is not carried out to the DOM. You have to call a digest cycle manually to do so.
In HTML5 it is a bit easier with Object.observe, but it is not yet supported in every browser currently on the market.
Hope I could help
Recently I've walked through several StackOverflow questions related to AngularJS optimization techniques:
AngularJS really slow at rendering with about 2000 elements?
How does data binding work in AngularJS?
Most of the answers were proposing an optimization technique called One-time binding, but my use case dictates that I can't use this approach, all of my data is interactive, the changes are pushed from server to client, plus there's quite a lot of data is required to be displayed. When server notification happens UI updates are required. So if I were to use the one-time binding it would break my page.
I've tried to analyse the reasons that make AngularJS slow in my case and I've found out that it's not the dirty checking itself that is slowing down the process. Modern javascript engines are able to dirty check tens of thousands object properties in just milliseconds. The root reason that make AngularJS slow is $watch expression itself, each $watch call is backed up with setTimeout calls plus some AngularJS internal overhead. The main problem is that these calls pass control to browser's internal message loop, which from program's perspective does nothing except imposing latency into each step of data displaying process.
So my question is the following: in contrast to one-time binding, I would like to have a way to group my one way binding expressions {{ someObj.someProperty }} into one $watch call, something like
{{ myBulkWatch::someObj.someProperty1 }}
{{ myBulkWatch::someObj.someProperty2 }}
...
{{ myBulkWatch::someObj.somePropertyN }}
I understand that there's no such functionality yet, but maybe it's possible to at least emulate the feature with existing Angular's instruments?
Any thoughts on this subject?
Thank you in advance!
I am working on a project targeting mainly mobile devices.
The app receives a list of different objects from the server which shall be rendered all at once.
Each object type has it's own directive, which takes care of business logic. But because most objects share certain features we created 'sub-directives' which work as mixins to keep it DRY.
If more than just a handful of those objects should be displayed (which is the default case), Angular takes quite some time parsing and instantiating those directives + sub-directives.
Our performance improvements so far:
replaced the ng-repeat with a custom directive, which also works like an infinite-scroll
use bindonce to limit bindings only where they are really necessary
pre-fill the $templateCache with the templates, instead of fetching
them via templateUrl
But even with those improvements, the performance is still not acceptable. (Especially when navigating to the state containing the list)
The approach I have in mind now is as follows:
let Angular fully parse and create one such directive with its
sub-directives
cache the resulting node
if we have another object of the same type simple reuse it and link it with a new Scope
But even after going through Angulars source code I have no clue where I could hook in to receive the finished result before advancing to the next object in the list.
So far it parses the template of the actual directive for each entry in the list, then comes back and finishes the sub-directives.
Hope I was able to make it understandable what I am trying to accomplish.
Thanks in advance!