I have two views one being a ListView and one being a ListRow view. The ListView is responsible for showing the UI and handling the events on the search box. The search method is triggered on change and with the assistance of Backbone.search() I am getting a collection of matching models.
However when my ListRow views are rendered (after search) the events are not bound to them. I have tried to call view.delegateEvents() in the ListView processSearch method inside of the filtered_events.each block just after the view is instantiated but that did no seem to help at all. Below is the source code for my views.
ListVIew.js
CheckinApp.Views.ListView = Backbone.View.extend({
properties: {
list_title: '',
event_id: null,
list_type: ''
},
list_element: '.chk_list_main',
template: null,
collection: null,
el: '.content',
events: {
"keyup #search": "processSearch",
"change #search": "processSearch"
},
"processSearch": function(e) {
var html = '';
var search_string = $(e.currentTarget).val().toLowerCase();
var filtered_events = this.collection.search(search_string, 'name');
if(search_string == '') filtered_events = this.collection;
console.log('searching tickets for \'' + search_string + '\' and found ' + filtered_events.length + ' matching tickets');
filtered_events.each(function(model) {
var list_template = (this.properties.list_type == 'event') ? '#list_row_event' : '#list_row_ticket';
var view = new CheckinApp.Views.ListRow({"model": model, list_template: list_template});
var rendered_view = view.render().el;
html += '<div class=\'chk_list\'>' + $(rendered_view).html() + '</div>';
}, this);
if(html == '') {
$(this.list_element).html($('#search_no_results').html());
} else {
$(this.list_element).html(html);
}
return this;
},
initialize: function(data) {
if(this.properties.list_type == 'ticket' && data.event_id == null) {
Backbone.history.navigate("#", true);
}
this.properties.list_title = data.list_title;
this.properties.event_id = data.event_id;
this.properties.list_type = data.list_type;
if(data.list_type == 'event') {
this.collection = new CheckinApp.Collections.Events();
} else {
this.collection = new CheckinApp.Collections.Tickets({"event_id": data.event_id});
}
this.template = _.template($('#listview').html())(this.properties);
this.listenTo(this.collection, 'add', this.add_list_row);
this.listenTo(this.collection, 'reset', this.add_all);
this.collection.fetch();
},
render: function() {
var logo = $('body').find('logo');
var navigation = $('body').find('.navig');
if(this.properties.list_type == 'event') {
navigation.remove();
logo.text(CheckinApp.session.user.brand);
} else {
logo.text(CheckinApp.session.user.current_venue);
}
$(this.el).html(this.template);
return this;
},
add_list_row: function(model) {
var list_template = (this.properties.list_type == 'event') ? '#list_row_event' : '#list_row_ticket';
var view = new CheckinApp.Views.ListRow({"model": model, list_template: list_template});
view.delegateEvents();
var rendered_view = view.render().el;
$(".chk_list_main").append(rendered_view);
},
add_all: function() {
this.collection.each(this.add_list_row, this);
}
});
ListRow.js
CheckinApp.Views.ListRow = Backbone.View.extend({
tagName: "div",
className: "chk_list",
template: null,
events: {
"click .checkd_in": "toggleCheckin",
"click .undo_checkd_in": "toggleCheckin",
"click .select_event, .txt_chk_text": "viewListsForEvent"
},
initialize: function(data) {
this.model = data.model;
this.template = _.template($(data.list_template).html());
this.listenTo(this.model, 'change', this.render);
this.listenTo(this.model, 'destroy', this.remove);
},
render: function() {
(this.model instanceof CheckinApp.Models.Event) ? this.renderEventRow() : this.renderTicketRow();
return this;
},
renderEventRow: function() {
this.$el.html(this.template(this.model.attributes));
},
renderTicketRow: function() {
if(this.model.get('checked_in') == 1) $(this.el).addClass('chk_undo_list');
else $(this.el).removeClass('chk_undo_list');
this.$el.html(this.template(this.model.attributes));
},
toggleCheckin: function(e) {
e.preventDefault();
this.model.save({
"checked_in": (this.model.get('checked_in') == 0) ? "1" : "0"
});
},
viewListsForEvent: function(e) {
e.preventDefault();
CheckinApp.session.user.current_venue = this.model.get("venue_name");
CheckinApp.session.user.current_event = $(e.currentTarget).find('a').data('event');
Backbone.history.navigate('guestlist', true);
}
});
I was stringifying the views in the processSearch method, changing it to the following fixed the event binding issue.
"processSearch": function(e) {
var search_string = $(e.currentTarget).val().toLowerCase();
var filtered_events = this.collection.search(search_string, 'name');
if(search_string == '') filtered_events = this.collection;
var results = [];
filtered_events.each(function(model) {
var item_row = new CheckinApp.Views.ListRow({"model": model, list_template: this.properties.list_template});
results.push(item_row.render().el);
}, this);
if(results.length == 0) {
$(this.list_element).html($('#search_no_results').html());
} else {
$(this.list_element).html(results);
}
return this;
},