Awesome Model Binding For Backbone.js

A few weeks ago, Brandon Satrom introduced me to Knockout.js by pointing me to a video by Steve Sanderson. I haven’t had a chance to try KO, yet, but I have to say I was blown away by the data-binding capabilties. Then, Brandon puts up this blog post and talks about how he made KO even more awesome by eliminating the data-* attributes that KO needs, from his HTML markup.

Needless to say, I was envious. KO’s data binding is impressive – especially compared to what we get out of the box with Backbone. Then this last week, Neive pointed me to a post by Brad Phelan that talks about adding better model binding to Backbone.

With all of this gong on, I was inspired! So I set out to build not just a manual configuration model binder, but a convention based binder that I can use in my apps to reduce the amount of view logic and code I need.

Introducing Backbone.ModelBinding

I wanted to get rid of code that binds my model’s change events to the form input, and form input change event to the model attribute. And after a handful of hours hacking away, this weekend, I produced a Backbone plugin that does what I want.

Backbone.ModelBinding: Awesome Model Binding For Backbone.js

Head over to the link to check out the detail on how to get started, what prerequisites you’ll need, etc.

An Example

In one of my previous posts, I talked about a form that uses backbone to manage the add/edit process for patient medications. Since I already had this code in my head and there was a fair amount of backbone view code to manage all of the model binding between the form elements and model, I decided that this would be the first place I use my new plugin – this is what drove out the initial requirements and implementation.

Here is the screen shot from the previous post, showing a list of medications with an add/edit form above that.

NewImage

There are 7 input fields and one select box on this form. To code to bind these fields to my model previously looked like this:

AddEditView = Backbone.View.extend({

  events: {
    "change input": "fieldChanged",
    "change select": "selectionChanged",
  },

  selectionChanged: function(e){
    var field = $(e.currentTarget);
    var value = $("option:selected", field).val();
    var data = {};
    data[field.attr('id')] = value;
    this.model.set(data);
  },

  fieldChanged: function(e){
    var field = $(e.currentTarget);
    var data = {};
    data[field.attr('id')] = field.val();
    this.model.set(data);
  },

  render: function(){
    // various jquery template things, here
  }
});

Given the number of inputs on this form, this code isn’t too bad. I was able to use some javascript and jQuery magic to reduce the amount of code I needed for each of the input boxes. However, I ended up with code to handle input fields and select fields, separately.

Now let’s look at this code with Backbone.ModelBinding in place.

AddEditView = Backbone.View.extend({
  render: function(){
    // various jquery template things, here
    Backbone.ModelBinding.call(this);
  }
});

Notice a small difference in the amount of code needed to bind all 8 of those fields (7 inputs, 1 select)? What this comparison doesn’t show is the code that would have been needed to bind a model change back out to the form’s input fields. The one line of code in the render function, though, to call the Backbone.ModelBinding plugin handles this as well.

Now I can not only get my form bound to my models with a simple convention, but I can also have any other code on the page update my model and if the model happens to be bound to the form, the form will be updated as well.

A Simiple Convention, With Unconventional Support

The Backbone.ModelBinding convention to make this work is very simple. The #id of the html form element is used as the model’s attribute and vice-versa. For example, in the above screen shot my “Trade name” field has an id of #trade_name. The model, then, has an attribute called trade_name.

If you don’t have your fields and model attributes aligned to this convention, though, Backbone.ModelBinding still supports you! But you have to drop back to manual configuration of the binding.

AddEditView = Backbone.View.extend({
  formBindings: {
    "change #someInput": "modelAttribute"
  }
});

Still, this is pretty simple. All you need to do is specify which form element is being bound to which model attribute and Backbone.ModelBinding takes care of the rest for you. The only requirement is that the html element you specify must fire a ‘change’ event that jQuery can bind to, and must be able to have it’s value set via jQuery’s ‘.val’ method.

What Are You Waiting For?

Well? What are you waiting for? Go grab a copy of Backbone.ModelBinding and try it out! It’s still verfdy early in it’s development, so it only supports input and select elements, right now. But! It’s open source – MIT License – and hosted on Github. If it doesn’t do what you need or does something you don’t want it to do, I’m always open to receiving pull requests.


Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Derick Bailey

Derick Bailey is an entrepreneur, problem solver (and creator? :P ), software developer, screecaster, writer, blogger, speaker and technology leader in central Texas (north of Austin). He runs SignalLeaf.com - the amazingly awesome podcast audio hosting service that everyone should be using, and WatchMeCode.net where he throws down the JavaScript gauntlets to get you up to speed. He has been a professional software developer since the late 90's, and has been writing code since the late 80's. Find me on twitter: @derickbailey, @mutedsolutions, @backbonejsclass Find me on the web: SignalLeaf, WatchMeCode, Kendo UI blog, MarionetteJS, My Github profile, On Google+.
This entry was posted in Backbone, Backbone.ModelBinding, Javascript, JQuery, Model-View-Controller. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • Bradphelan

    Hi Derick, Thanks for the link to my site but the name is “Brad Phelan” not “Brian Phelan”. Glad you liked the idea.

    • http://mutedsolutions.com Derick Bailey

      D’oh! sorry about that. fixed :)

  • http://murrayon.net/ Mike Murray

    One advantage to the Knockout data-bind attributes is that a particular value doesn’t have to be tied directly to only one DOM element. Imagine if I wanted a value to be displayed in more than once place on the screen. You obviously couldn’t have the same ID for both elements.

    • http://mutedsolutions.com Derick Bailey

      true. the focus of what i’ve written, so far, is to get data moved between form inputs and the model, easily. i’m not sure where this will head, if anywhere, in regards to binding other parts of the html page. i think jquery templates and the general re-render paradigm of backbone views covers this end pretty well.

  • http://www.register-domainname.in Register domain names

    I think that, you have got a good chance to say. You have described very clearly about this information. Thanks for sharing.

  • Jace Bennett

    I’m struggling with some of the same issues here, but I think I might have to go another way… some thoughts, and a question:

    - In this paradigm, I can’t bind a view that has more that one model.
    - I have a need to support highly interruptable usage for long editing sessions (complex document), which means I’m more apt to be listening and updating the model on keyup than on save click, which means rerendering is just a nonstarter (I would lose focus).

    I also have the issue (because of listening to keyup) that some portion of the events I get from the model will be due to edits made in this control, which is still focused and likely under active change. I can’t just poke a new value into it without screwing the user up. Did you run into the issue of sourcing the model events to filter them?

    I’ll be looking at these issues independently, and I might like to contribute, but the scopes might be so different as to be divergent. What do you think? 

    • http://mutedsolutions.com Derick Bailey

      Hi Jace – those are scenarios that I haven’t run into yet, but I can see why you would need functionality like that … i don’t have a direct answer for you, at the moment. I’m interested in seeing how I would build functionality like this, though, and whether or not my plugin would support these needs. I’ll have to do a bit of testing to find out, and I’ll let you know what I find.

      Thanks for posting such a great scenario to tackle. :)

  • http://dinhe.net/~aredridel Aria Stewart

    Any reason you chose the ‘id’ attribute rather than ‘name’? Since id must be document-unique, I can’t have views that use the same field names, or mix models that use that without overriding formBindings.

    • http://mutedsolutions.com Derick Bailey

      based on my own needs… but it’s configurable. see the documentation on the wiki. you can use any attribute you want. it only defaults to id.

  • Nikolai Aleksandrenko

    IT will be great to not couple so much the models and the view. For more complex projects with many templates and models at some point you will just go search for a name in different files, that’s no good.

    Configurable list of selector – event – model.value is just a must. Model names and template id’s will not be coupled and more flexibility will be possible.

    i see it somewhat like this (event like syntax):

    …,

    bindings : {
            ‘#password’ : password,
            ‘.user-name’ : username’
    },

    where password and username are properties in a model.

    Is someone want to implement it … :) call me ;)

    • Aleksandrenko

      I’m currently working on a plugin that make something like this possible:

      binder : [
         {
           model: App.Models.User,
          attribute: "username",
          selector: "input#username"
         }
      ]

      anyone interested to take a look :) and give a hand?

      • Kailasvilaskore

        Could please tell me where can I find entire code of your example.

  • Ryan

    Hi Derick, I am pretty familiar with Knockout and am used to that paradigm but new to Backbone and was looking for something like your ModelBinding plugin. So thanks for your work on this.

    My question is, and maybe I am missing something obvious, but how do you set the changes only upon clicking a save button in your ui? In your screenshot, you have a “close” and “save” button.
    ModelBinding sets the value of the changed form element to the model whenever the user edits a form field.
    But what if I want to hold all those changes and only update my model when the user actually clicks “Save”? And then if they decide to click “Cancel” no changes to the model get applied?

    I am using version 0.4.3.

    Thanks for your help.

    • http://mutedsolutions.com Derick Bailey

      right now, there’s no way to do this. it’s something I want to add, as I need the same functionality, though. I’m not sure what the time frame for getting this done is, because the code is currently not set up to support this and will need a major overhaul (which I’m planning to do, because the code is getting out of hand)

      • Ryan

        Oh ok. I actually just recently implemented your Memento plugin and it’s working great.

  • Aleksandrenko

    i can’t make it work with manual configuration. Please provide a working example.

    here is my NOT working code:
        var mymodel= {};
        var myview = {};

        $(document).ready(function(e) {
            var model = Backbone.Model.extend({});
            var view = Backbone.View.extend({
        el: ‘body’,
        modelBindings: {
            ”change #domid”: “attr” //#domid is an input
        }
            });

    mymodel = new model({ “attr”: ‘def’ });
    myview = new view( { model: mymodel } );});