4

When developing Backbone applications, I often find myself instantiating models in views when dealing with nested data. Here's some example data:

{
  name: Alfred,
  age 27,
  skills: [
    {
      name: 'Web development',
      level: 'Mediocre'
    },
    {
      name: 'Eating pizza',
      level: 'Expert'
  ]
}

Say I have some view PersonView that takes a person object PersonModel as its model (where Alfred would be an instance). And let's say I want to render the person's skills as subviews. At this point I create a new view and a new model for dealing with the nested skills data. And this is where I suspect I'm doing something wrong, or at least something suboptimal.

var SkillModel = Backbone.Model.extend();

var SkillView = Backbone.View.extend({
  tagName: 'li',
  initialize: function() {
    this.render();
  },
  render: function() {
    this.$el.html(someTemplate(this.model));
  }
});

var PersonView = Backbone.View.extend({
  el: ...
  initialize: ...
  renderSkills: function() {
    _.each(this.model.get('skills'), function(skill) {
      var skillModel = new SkillModel(skill);
      var skillView = new SkillView({ model: skillModel });

      self.$el.append(skillView.el);
    })
  }
});

Should I really be doing this or is there some better pattern, that does not involve third-party libraries, that solves the problem? I feel like I'm not really decoupling the views and models in the best way. If you want a concrete question it's: Is this an anti-pattern?

zvisofer
  • 1,346
  • 18
  • 41

1 Answers1

1

Views are for view logic, Models for data logic.

Consider to have a Model like this:

var SkillModel = Backbone.Model.extend({
});
var SkillsCollection = Backbone.Collection.extend({
    model : SkillModel
});
var PersonModel = Backbone.Model.extend({

    /*
    ** Your current model stuff
    */

    //new functionality 

    skills : null,

    initialize : function(){
        //set a property into the model that stores a collection for the skills
        this.skills = new SkillsCollection;
        //listen to the model attribute skills to change
        this.on('change:skills', this.setSkills);
    },

    setSkills : function(){
        //set the skills of the model into skills collection
        this.skills.reset( this.get('skills') );
    }

});

So in your view renderSkills would be something like this:

var PersonView = Backbone.View.extend({
  renderSkills: function() {
     //this.model.skills <- collection of skills
    _.each(this.model.skills, function(skill) {
      var skillView = new SkillView({ model: skillModel });

      self.$el.append(skillView.el);
    })
  }
});

I did my pseudo code trying to adapt to your sample code. But if you get the point, basically you could have a nested model or collection into your Model, so in the view there is not needed data interaction / set, everything is in the model. Also I answer as your requested, handling things without a third party. However don't mind taking a look to the source of this kind of plugins also: https://github.com/afeld/backbone-nested

Update: per comments I provided a setup of the Model example:

var m = new PersonModel();
//nested collection is empty
console.log(m.skills.length);
m.set({skills : [{skill1:true}, {skill1:true}]});
//nested collection now contains two children
console.log(m.skills.length);

As you can see the Model usage is as "always" just a set of attributes, and the model is the one handling it. Your view should use skills as how views use any other collection. You could do a .each iterations or worth better to listen for events like add, reset, etc. on that collection(but that is other topic out of the scope of this question).

Example: http://jsfiddle.net/9nF7R/24/

Daniel Aranda
  • 6,426
  • 2
  • 21
  • 28
  • Interesting. I'm trying to wrap my head around it. But in `PersonModel`, when is `change:skills` going to get fired? –  Feb 05 '14 at 20:41
  • when the attribute change. yourPersonInstances.set({skills : skills}); other way could be to create in the model a method named setSkills, and in that method set the skills into the nested collection. @trevorDashDash – Daniel Aranda Feb 05 '14 at 21:03
  • Ah I see! Just a final question (and, I suppose, the thing I don't really understand). Where do you actually instantiate the skillsModel in your setup? –  Feb 05 '14 at 21:17
  • @trevorDashDash I updated the answer with a "setup" example. I believe that the usage is still as any other model. :) – Daniel Aranda Feb 05 '14 at 21:25
  • Sorry, but my question was *where* you instantiate the skillsModel. Aren't you also forced to instantiate it in the view? –  Feb 05 '14 at 21:40
  • @trevorDashDash probably a running example could guide you with my idea. http://jsfiddle.net/9nF7R/24/ – Daniel Aranda Feb 05 '14 at 22:30
  • Splendid! I've got some refactoring to do :) –  Feb 05 '14 at 22:41