Data Binding, One-Time, One-Way & Two-Way
Data binding is an important tool used by developers to populate user interfaces with model data. There are many ways to bind data, each with their own benefits and costs. Important factors include performance, data flow, and code complexity. There are many frameworks and libraries that provide various approaches to the data binding, including Handlebars, KnockoutJS, Angular 1 & 2, and React.
Each of these frameworks and libraries are very opinionated about how to bind data, including how model changes are detected and data is propagated. Handlebars (and similar template systems) are great for one-time data binding, React is great for one-way data binding, and Angular 2 provides the right mix of data binding options through one-way for components and two-way within components. Regardless of the method used, the key is to understand the concepts and ideas behind data binding, and how the library you choose approaches this important and complex topic.
Data binding is the mechanism that connects a data model to the user interface (UI). While any kind of code can be used to data bind, for the purposes of this posting, data binding refers to a library or framework that provides data binding services through an API. This does not require the developer to manually retrieve model values, nor perform some kind of DOM or other template string manipulations to populate the UI with model data. Data binding services are very popular with developers because they greatly simplify the process of updating the UI and reduce the amount of boiler plate code in applications.
The data model, the user interface, and the binding mechanism can take many forms, but ultimately the goal is to connect the user interface with some kind of model. There are three primary forms of data binding: one-time, one-way, and two-way. Choosing one over the others involves many factors.
One-time data binding occurs one time between the model and the UI. Typically, when the UI is initially created, the values of the model data at that particular moment in time are used to populate the newly created UI. The binding occurs once and there is no automated mechanism to update the UI when future changes to the model occur.
From a performance perspective, one-time is the highest performance because it happens once; therefore, it does not require any change detection mechanisms to detect changes to the underlying model or in the UI. Change detection mechanisms introduce potential performance problems into a web page because blocking code must be executed to determine if model changes need to be propagated to the UI.
One-time data binding is practical when the data never or very rarely changes. When data changes regularly or very frequently, one-time data binding becomes a hindrance to simple and efficient UI updates. To solve the problem of UI updates for regularly changing model data, one-way (or two-way) data binding is used.
From the perspective of one-way data binding, change detection involves two aspects: knowing when the model has changed, and knowing what changed in the UI as a result of the model changes.
To detect model changes, there are three fundamental patterns that are available: manual, publisher subscriber, and asynchronous wrappers. Manual change detection can occur in two forms – explicit and implicit. Explicit manual change detection is when the developer must tell the library or framework explicitly that a change has occurred (or potentially occurred) and update the UI with the new model data, as appropriate.
Once a model change has been detected, then the UI needs to be updated and the second phase of change detection occurs. With most one-time data binding, and even many one-way data binding systems, the DOM is updated inefficiently to reflect the model changes. Typically, the inefficiency presents itself as destroying the branch of the DOM tree being updated and completely recreating and rendering it again. Generally, this is not the most efficient way to update the UI, but it is the easiest way for a developer to code the UI update process.
One-way data binding creates an ongoing connection between the model and the UI. Changes to the model are reflected in the UI through some kind of process controlled by a library or framework. One-way data binding only propagates changes from the model to the UI, not vice versa. Central to the implementation of one-way (and two-way) data binding is an automated mechanism to determine if changes to the model have occurred. This process is called change detection.
One-time and one-way data bindings flow from the model to the UI. Two-way includes one-way, but it also facilitates the flow of data from the UI to the model. Whether a library or framework explicitly supports two-way data binding, the only means by which to retrieve new data from the UI is through UI events such as key up, input or click.
Updating the model from the UI is implemented using one of two paths: UI element to UI event to model or UI event to model to UI element. Typically, the former describes two-way data binding, while the latter is a form of one-way data binding. The difference is how the UI element is updated. Does the UI element have two sources of data or one source? When the UI element is updated from the model and user input, it has two sources of data. When the UI element is always updated only from the model, it has one source of data. The one source of data results from UI events triggered by user interaction which update the model first, and then the model updates the UI element. The element itself is not directly updated by the triggered event.
Even though both React and Angular 2 update the UI element from user interaction, React forces the model to be updated first, and then the UI element is updated. In contrast Angular 2 (from the developer’s perspective) updates the UI element and then triggers the update of the model. This is why people consider React to be one-way binding, and Angular 2 to use two-way data binding (Angular 1 works the same way as Angular 2 in this regard as well).
On a conceptual level, two sources of data sounds problematic and generally it is. So the the argument of two-way data binding typically falls in favor of opposing it in favor of one-way data binding. Unfortunately, the argument is not that simple. The context or scope of the data binding must be considered as well. When considering the flow of data and data binding, the question must be asked: how is the data flowing and what is being bound?
UI data changes can occur within two scopes within a single component or between components. Using two-way data binding between components results in components receiving data from multiple sources, and this can be problematic. Using two-way data binding strictly within a component is not nearly as problematic.
Take typing into an input field: the updating of the input field occurs locally within a component; it does not affect other components directly. So the argument against two-way data binding does not apply in this situation. The two-way data bind is trivial and controlled completely within the context of one component.
Understanding the scope of the data binding is essential to understanding the benefits and risks of each approach, not just assessing it based upon two sources of truth.
Using two-way data binding between components can results in components transitioning into undesired states because of conflicting data being propagated from multiple sources. Therefore, one-way data binding is preferred even though it will require a more complicated data flow and more coding on the part of the developer.
Avoiding two-way data binding within a component, and implementing the structures needed for one-way data binding for trivial operations such as entering text into an input element, adds unneeded complexity with almost no practical gain. React suffers from this problem of requiring the creation of event handlers, updating of state, and re-rendering of the user interface to allow the simple act of typing text into an input element.
This is where Angular 2 shines: it encourages the use of two-way data binding techniques within a component for trivial model-UI updates, while requiring more one-way data binding interactions between components. This is the best of both worlds.