I'm doing a lot of experimenting with Backbone.js for the team at Sheepdog these days. We're working on compiling a front end stack to build our internal and client projects that require complex front end interactions.

One of our "wants" for Backbone is to easily bind a Backbone.View containing many form elements directly to a Backbone.Model instance. I wanted to implement this in the most declarative way possible, similar to the way a view is structured. The solution involved creating a subclass of Backbone.View called FormView.

Before I start, I'd just like to show an example of the git diff stat from the commit where I changed one of our View implementations to use FormView. They say that you can calculate the value of your work by the lines of code that it shrinks while maintaining functionality.

Moving On To explain fieldMap, I'll go through a simple example of creating a simple Model and View to lay it all out. I'll start by creating a model for the User with some fields fullName, foo, and bar. model.coffee 1 2 3 4 5 class User extends Backbone . Model defaults: fullName: '' foo: '' bar: '' Then create a View to display this new User class. view.coffee 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 class UserForm extends Backbone . View tagName: 'div' events: 'click #test_reset' : 'resetModel' resetModel: -> @model . set foo: 'bang' bar: 'baz' fullName: 'Cool Guy' render: -> $ ( @el ). html """ <input type=" text " id=" test_foo " /> <input type=" text " id=" test_bar " /> <input type=" text " id=" test_first_name " /> <input type=" text " id=" test_last_name " /> <input type=" submit " id=" test_reset " value=" reset " /> """ @ Now, you pass a User object into the view when you create it, and now the goal is to have the model update each time the form is changed as well as propagate model changes back into the form when they happen. This is where things start to get a little ugly and WET (!DRY). You'll have to add a refresh method to your View, and bind it with @model.bind 'change', => do @refresh and to sync in the other direction you will need to have 'change input': 'toModel' declaration within your events object for the View. This is where you'll end up writing some ugly code like the following which implements naive refresh and toModel methods for the example. view.coffee 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 class UserForm extends Backbone . View tagName: 'div' events: 'change input' : 'toModel' 'click #test_reset' : 'resetModel' initialize: -> @model . bind "change" , => do @refresh resetModel: -> @model . set foo: 'bang' bar: 'baz' fullName: 'Cool Guy' toModel: -> @model . set foo: $ ( '#test_foo' ). val () bar: $ ( '#test_bar' ). val () fullName: "#{@$('#test_first_name').val()} #{@$('#test_last_name').val()}" refresh: -> $ ( '#test_foo' ). val @model . get "foo" $ ( '#test_bar' ). val @model . get "bar" names = @model . get ( 'fullName' ). split " " $ ( '#test_first_name' ). val names [ 0 ] $ ( '#test_last_name' ). val names [ 1 ] render: -> $ ( @el ). html """ <input type=" text " id=" test_foo " /> <input type=" text " id=" test_bar " /> <input type=" text " id=" test_first_name " /> <input type=" text " id=" test_last_name " /> <input type=" submit " id=" test_reset " value=" reset " /> """ @ There are many problems with the code above for keeping the form and model in sync with each other. To name a few: It's unclear which Model fields are bound to which form elements

Lots of repetitive code

Refactoring becomes a nightmare, jQuery selectors all over the code

What if you don't want to propagate ALL changes on a model? only certain fields? the boiler plate for this starts growing very quickly. Not only does the code contain the aforementioned issues, but it doesn't really follow the backbone paradigm of defining selectors for events during the View's declaration.

Syncing Solution - FieldMap The solution we (read Honza and I) came up with involves creating a "fieldMap" of input ids to model field names, and have them sync in both directions automatically. For the most simple case this looks similar to the events declaration at the top of your View class; No additional legwork needed. You can see that the fullName syncing isn't in the below example, I'll show how that can be done just as simply leveraging a slightly more complex fieldMap declaration. view.coffee 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 class UserForm extends FormView tagName: 'div' fieldMap: '#test_foo' : 'foo' # Binds #test_foo to User.foo '#test_bar' : 'bar' # Binds #test_bar to User.bar events: 'click #test_reset' : 'resetModel' resetModel: -> @model . set foo: 'bang' bar: 'baz' fullName: 'Cool Guy' render: -> $ ( @el ). html """ <input type=" text " id=" test_foo " /> <input type=" text " id=" test_bar " /> <input type=" text " id=" test_first_name " /> <input type=" text " id=" test_last_name " /> <input type=" submit " id=" test_reset " value=" reset " /> """ @ Much nicer, isn't it? When the user presses the "reset" button which is generated by render , the resetModel method will fire to set the model's fields and the syncing will happen immediately to those form elements that the fields are bound to. Also, when a user changes the content of either textbox the foo and bar values will get set on the View's model instance. Now how about the complex case where the form input doesn't necessarily match up exactly to the data going into the Model? or if the Model's field is separated over multiple inputs, just like the fullName field on the User Model?