5

Consider the below code. If I create a change event handler in Select View, What would be the cleanest and best way to get the selected model of the option view without assigning data-cid attribute in the option view. Im trying to keep the truth out of the dom and do this Backbone way:

var ItemView = Backbone.View.extend({
tagName: 'option',
initialize:function(){        
    this.template= _.template($('#menu_item_view').html());    
},    
render:function(){        
    this.$el.html(this.template(this.model.toJSON()));        
    return this;        
}
});

var CollectionView = Backbone.View.extend({
  tagName: 'select',
  initialize:function(){        
    this.collection = new ItemCollection();            
    this.collection.on('sync',this.render,this);            
    this.collection.fetch();
 },    
 render:function(){        
    this.$el.html(this.collection.map(function( item ){            
        return new ItemView ({model:item}).render().el;        
    },this);      
    return this;        
 }
});
Alex Shilman
  • 1,547
  • 1
  • 11
  • 15
  • Here is how I would go about doing it: http://stackoverflow.com/questions/24703561/backbone-model-ids-and-on-click-events/24704706#24704706 – Toli Aug 09 '14 at 23:37
  • @AnatoliyZaslavskiy, I would agree with you if the event was taking place at the option level. However, the change event only happens at the Select level, not at the Option level. So the collection View, which is a Select view listens on the change event. How would I know which model was selected? Im assigning data-cid to all the options and getting the model by cid, however I dont like that. It defeats the purpose of using a MV framework like Backbone. I dont want to use the dom at all, and stick to model. – Alex Shilman Aug 10 '14 at 01:06
  • When the select event happens on the ItemView you reference the model: `events: { – Toli Aug 10 '14 at 01:56
  • When the select event happens in the DOM the ItemView subscribes to it and triggers a `selected` event or sets a `selected` attribute on the associated model `this.model` which you passed in when creating the view. No need for CIDs. If you set an an attribute you can come back at any time and do a `findWhere` on the collection. If you do a trigger you can do a listenTo like Daniel suggested. – Toli Aug 10 '14 at 02:02
  • Yes, but The click event would never fire in the option model view. The change event only fires in Select collection view. http://jsfiddle.net/7cfjznjn/ – Alex Shilman Aug 10 '14 at 02:22
  • Aaah sorry! I misunderstood. Didn't know you were actually using a select element ;) I assumed you were using views to represent each option. – Toli Aug 11 '14 at 12:53

1 Answers1

6

You are right, you should NOT use anything in the DOM.

The idea to solve this is easy, at ItemView listen for the click event, then in the listener just do something like it:

this.model.trigger('selected', this.model);

This trigger an event in the model and pass as argument the model as itself(to know wish has been selected). Events triggered in models are propagated to its collection.

Then in CollectionView listen for:

this.collection.on('selected', this.selectedHandler, this); 

SelectedHandler is going to receive the selected model as argument, as you passed it in the trigger.

Update: adding sample code. Basically as the DOM element option did not trigger as itself a DOM event, we add a "plugin" in the select DOM element to do it.

var ItemView = Backbone.View.extend({
  tagName: 'option',
  events : {
    'option_changed' : 'optionChangedHandler'
  },
  initialize:function(){        
    this.template= _.template($('#menu_item_view').html());    
  },    
  render:function(){        
    this.$el.html(this.template(this.model.toJSON()));        
    return this;        
  },
  optionChangedHandler : function(){
    this.model.trigger('selected', this.model);
  }
});

var CollectionView = Backbone.View.extend({
  tagName: 'select',
  events : {
    'change' : 'changeHandler'
  },
  initialize:function(){        
    this.collection = new ItemCollection();            
    this.collection.on('sync',this.render,this);    
    this.collection.on('selected',this.selectedModel, this);            
    this.collection.fetch();
 },    
 render:function(){        
    this.$el.html(this.collection.map(function( item ){            
        return new ItemView ({model:item}).render().el;        
    },this);      
    return this;        
 },
 selectedModel : function(model){
    console.log('it is magic: ', model);
 },
 changeHandler : function(){
   this.$("option:selected").trigger('option_changed'); 
 }
});
Daniel Aranda
  • 6,426
  • 2
  • 21
  • 28
  • In the select list, I dont believe option would fire a click event. http://jsfiddle.net/7cfjznjn/ The select would fire a change event. Option view is the model view, and select is the collection view – Alex Shilman Aug 10 '14 at 02:19
  • In the collection view, add a listener to the change event for the select element, and there do this: this.$("option:selected").trigger('option_changed'); in ItemView listen for "option_changed", I'm going to update my answer with complete code example. – Daniel Aranda Aug 10 '14 at 03:02
  • You're right that would work. That's cleaner than using the dom. I'll accept your answer. Thanks. – Alex Shilman Aug 10 '14 at 03:07