Two-way binding

Angular.js has been popularized as two-way data binding in the frontend; the idea behind two-way data binding is to keep views and models in sync. When you make a change in an input field the view the model should be updated immediately, and if you change a property in the model the view should show the current value immediately:

Two-way binding

Figure 3.1 Two-way data binding with Backbone

Backbone does not provide a mechanism to achieve this easily; however, we can do it using the event system that Backbone models provide. Figure 3.1 shows how you can make an implementation.

Backbone.View listens for keyup and change events on input controls at the DOM; when a change is triggered from the DOM, Backbone.View can extract the new value from the input and set the Model:

class FormView extends ModelView {
  // ...

  events() {
    return {
      'click button[type="submit"]': 'saveContact',
      'keyup input': 'inputChanged',
      'change input': 'inputChanged'
    };
  }

  inputChanged(event) {
    var $target = $(event.target);
    var value = $target.val();
    var id = $target.attr('id');
    this.model.set(id, value);
  }

// ...
}

When you call the set() method on Backbone.View, at least two events are triggered: change and change:<fieldname>.We can use these events to update the necessary views:

var myModel = new Backbone.Model();

myModel.on('change:foo', event => {
  console.log('foo changed to', event.changed.foo);
});
myModel.on('change', event => {
  var changedKeys = _.keys(event.changed);

  changedKeys.forEach(key => {
    console.log(key, 'changed to', event.changed[key]);
  });
});

myModel.set('foo', 'bar');
myModel.set('baz', 'xyz');
myModel.set({
  foo: 'stuff',
  baz: 'zxy'
});

You can see the output of the preceding snippet in the following figure:

Two-way binding

Figure 3.2 Output of change events

We can use these events to update the view when necessary. Indeed, the code we already have is enough to keep the ContactForm and ContactPreview views in sync.

this.model.on('change', this.render, this);

ContactPreview is listening for every change in the model and re-rendering the view when something changes. However, re-rendering the whole view each time is a heavy process; it would be better if we made the changes only when necessary.

First, you will need to identify each field with an identifier:

<script id="preview-template" type="text/template">
<h3 id="name"><%= name %></h3>
<ul>
<li id="phone"><%= phone %></li>
<li id="email"><%= email %></li>
</ul>
</script>

And the change event handler will update only the contents of the identified elements:

class ContactPreview extends ModelView {
  constructor(options) {
    //...

    // Re-render the view if something in the model
    // changes
    this.model.on('change', this.handleChange, this);
  }

  handleChange(event) {
    var changedKeys = _.keys(event.changed);

    changedKeys.forEach(key => {
      let $target = this.$('#' + key);
      if ($target) {
        $target.html(event.changed[key]);
      }
    });
  }
}

Despite the result of the two-way data binding it should be used with caution; some people don't think that two-way data binding is a good idea and consider it as an anti-pattern.

References

Refer to the following URLs for more information:

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.51.153