What is two way binding?
Two-way binding just means that:
- When properties in the model get updated, so does the UI.
- When UI elements get updated, the changes get propagated back to the model.
Backbone doesn't have a "baked-in" implementation of #2 (although you can certainly do it using event listeners). Other frameworks like Knockout do wire up two-way binding automagically.
In Backbone, you can easily achieve #1 by binding a view's "render" method to its model's "change" event. To achieve #2, you need to also add a change listener to the input element, and call model.set
in the handler.
Here's a Fiddle with two-way binding set up in Backbone.
Two-way binding means that any data-related changes affecting the model are immediately propagated to the matching view(s), and that any changes made in the view(s) (say, by the user) are immediately reflected in the underlying model. When app data changes, so does the UI, and conversely.
This is a very solid concept to build a web application on top of, because it makes the "Model" abstraction a safe, atomic data source to use everywhere within the application. Say, if a model, bound to a view, changes, then its matching piece of UI (the view) will reflect that, no matter what. And the matching piece of UI (the view) can safely be used as a mean of collecting user inputs/data, so as to maintain the application data up-to-date.
A good two-way binding implementation should obviously make this connection between a model and some view(s) as simple as possible, from a developper point of view.
It is then quite untrue to say that Backbone does not support two-way binding: while not a core feature of the framework, it can be performed quite simply using Backbone's Events though. It costs a few explicit lines of code for the simple cases; and can become quite hazardous for more complex bindings. Here is a simple case (untested code, written on the fly just for the sake of illustration):
Model = Backbone.Model.extend
defaults:
data: ''
View = Backbone.View.extend
template: _.template("Edit the data: <input type='text' value='<%= data %>' />")
events:
# Listen for user inputs, and edit the model.
'change input': @setData
initialize: (options) ->
# Listen for model's edition, and trigger UI update
@listenTo @model, 'change:data', @render
render: ->
@$el.html @template(@model.attributes)
@
setData: (e) =>
e.preventDefault()
@model.set 'data', $(e.currentTarget).value()
model: new Model()
view = new View {el: $('.someEl'), model: model}
This is a pretty typical pattern in a raw Backbone application. As one can see, it requires a decent amount of (pretty standard) code.
AngularJS and some other alternatives (Ember, Knockout…) provide two-way binding as a first-citizen feature. They abstract many edge-cases under some DSL, and do their best at integrating two-way binding within their ecosystem. Our example would look something like this with AngularJS (untested code, see above):
<div ng-app="app" ng-controller="MainCtrl">
Edit the data:
<input name="mymodel.data" ng-model="mymodel.data">
</div>
angular.module('app', [])
.controller 'MainCtrl', ($scope) ->
$scope.mymodel = {data: ''}
Rather short!
But, be aware that some fully-fledged two-way binding extensions do exist for Backbone as well (in raw, subjective order of decreasing complexity): Epoxy, Stickit, ModelBinder…
One cool thing with Epoxy, for instance, is that it allows you to declare your bindings (model attributes <-> view's DOM element) either within the template (DOM), or within the view implementation (JavaScript). Some people strongly dislike adding "directives" to the DOM/template (such as the ng-* attributes required by AngularJS, or the data-bind attributes of Ember).
Taking Epoxy as an example, one can rework the raw Backbone application into something like this (…):
Model = Backbone.Model.extend
defaults:
data: ''
View = Backbone.Epoxy.View.extend
template: _.template("Edit the data: <input type='text' />")
# or, using the inline form: <input type='text' data-bind='value:data' />
bindings:
'input': 'value:data'
render: ->
@$el.html @template(@model.attributes)
@
model: new Model()
view = new View {el: $('.someEl'), model: model}
All in all, pretty much all "mainstream" JS frameworks support two-way binding. Some of them, such as Backbone, do require some extra work to make it work smoothly, but those are the same which do not enforce a specific way to do it, to begin with. So it is really about your state of mind.
Also, you may be interested in Flux, a different architecture for web applications promoting one-way binding through a circular pattern. It is based on the concept of fast, holistic re-rendering of UI components upon any data change to ensure cohesiveness and make it easier to reason about the code/dataflow. In the same trend, you might want to check the concept of MVI (Model-View-Intent), for instance Cycle.