Skip to content

Latest commit

 

History

History
93 lines (72 loc) · 3.35 KB

README.md

File metadata and controls

93 lines (72 loc) · 3.35 KB

Backbone.DeepModelBinder

Extension of Backbone.ModelBinder to support binding of nested/related Backbone models/collections. Meant to be API compatible with Backbone.ModelBinder so it can be used as a drop in replacements. Mirrored methods are:

bind(model, rootEl, attributeBindings, modelSetOptions)
bindCustomTriggers(model, rootEl, triggers, attributeBindings, modelSetOptions)
unbind()

Examples

Pertinent calls:

binder.bind(model, el, {'post.title': '.post-title'});
binder.bind(model, el, {'post.comments[0].author.name': '.first-comment-author'});
binder.bind(model, el, {'post.comments[-1].author.email': '.last-comment-author-email'});

Full example:

var model = constructNestedBackboneModel();
var binder = new Backbone.DeepModelBinder();
var el = $('#some-el');

binder.bind(model, el, {'post.title': '.post-title'});
binder.bind(model, el, {'post.comments[0].author.name': '.first-comment-author'});
binder.bind(model, el, {'post.comments[-1].author.email': '.last-comment-author-email'});

var modelBinderTriggers = {
  '': 'change hiddenchange',
  '[contenteditable]': 'blur'
};
binder.bindCustomTriggers(model, el, modelBinderTriggers, {'post.title': '.post-title'});

var constructNestedBackboneModel = function() {
  model = new Backbone.Model({type: 'blog_post', publish_date: new Date()});
  post = new Backbone.Model({title: 'My Awesome Blog Post'});
  comments = new Backbone.Collection();
  comment = new Backbone.Model({text: 'some silly comment'});
  author = new Backbone.Model({name: 'ralfthewise', email: '[email protected]'});

  //setup the nesting
  comment.set({author: author});
  comments.add(comment);
  post.set({comments: comments});
  model.set({post: post});

  return model;
}

Internals

When you call:

bindings = {
  'post.comments[0].author.name': '.first-comment-author',
  'post.comments[0].author.email': '.first-comment-email'
};
binder.bind(blogPostModel, el, bindings);

internally the deep binder is creating a chain of backbone objects to watch for changes. After the above call, the internal structure will look like:

bindingChains: {
  'post.comments[0].author': {
    'chainSteps': {
      'post': <BlogPost Backbone.Model (top level model)>,
      'post.comments': <Post Backbone.Model>,
      'post.comments[0]': <Comments Backbone.Collection>,
      'post.comments[0].author': <Comment Backbone.Model that was at [0]>
    },
    
    model: <Author Backbone.Model>,
    modelBinder: <Backbone.ModelBinder used to bind the end/leaf of the chain ('name' and 'email' attributes)>,

    'bindings': {
      'name': '.first-comment-author',
      'email': '.first-comment-email'
    }
  }
}

As part of creating this structure, the deep model binder binds to 'change' (for Backbone.Models) or 'add remove sort reset' (for Backbone.Collections) events and when those events are triggered it rebuilds the chain and re-binds the leaf ModelBinder.

Known Issues

  • Doesn't handle the case where the end of the chain is an actual model and not an attribute: 'post.comments[0]'
  • Doesn't provide a way to bind to the collection length