Reputation: 16726
I'm trying to update only a part of a template used by a Backbone view when a collection is updated. The code below successfully executes the search()
and render_list()
methods. Furthermore, console.log(html)
shows the full template's html. But when I execute the replaceWith
, it replaces the selector with empty. If I replace $(selector,html)
with a string (ie: 'test'), it successfully replaces with 'test'.
So, for some reason, the $(selector, html)
selector isn't doing what it's meant to. What is further weird is that the images within the updated html selector are requested by the browser even though none of the updated html is inserted on into the document.
define([
'jquery',
'underscore',
'backbone',
'collections/tracks',
'collections/genres',
'text!templates/search_view_title.html',
'text!templates/search_view.html'
],function($,_,Backbone,Tracks_collection,Genres_collection,Search_view_title_template,Search_view_template){
var Search_view = Backbone.View.extend({
el: $('#app'),
events: {
'change #genre_select': function(){this.search('genre')},
'click #search_btn': function(){this.search('search')}
},
template: _.template(Search_view_template),
initialize: function(){
// SET SOME IMPORTANT LAYOUT SETTINGS
$('#pagetitle').html(_.template(Search_view_title_template));
$('body').css('padding-top','124px');
this.genre_collection = new Genres_collection();
this.listenTo(this.genre_collection,'update',this.render);
this.genre_collection.fetch();
this.collection = new Tracks_collection();
this.listenTo(this.collection,'update',this.render_list);
},
search: function(searchtype){
switch(searchtype){
case 'genre':
console.log('genre changed');
this.collection.fetch({
data: {limit: 30, type:'genre',genre_id:$('#genre_select').val()}
});
break;
case 'search':
console.log('search changed');
this.collection.fetch({
data: {limit: 30, type:'search',keyword:$('#keyword').val()}
});
break;
}
console.log(this.collection);
},
render_list: function(){
var that = this;
console.log('render list');
var html = that.template({genres: this.genre_collection.models,tracks: this.collection.models});
console.log(html);
var selector = '#tracklist';
console.log($(selector,html));
that.$el.find(selector).replaceWith($(selector,html));
return this;
},
render: function(){
// MAKE 'THIS' ACCESSIBLE
var that = this;
console.log('render');
that.$el.find('#container').html(that.template({genres: this.genre_collection.models}));
return this;
}
});
return Search_view;
});
Upvotes: 2
Views: 176
Reputation: 17430
Without the HTML templates in hand, I can just assume things.
This is closer to how I would do it:
var Search_view = Backbone.View.extend({
el: $('#app'),
events: {
'change #genre_select': 'onGenreChange',
'click #search_btn': 'onSearchClick'
},
template: _.template(Search_view_template),
initialize: function() {
// SET SOME IMPORTANT LAYOUT SETTINGS
$('#pagetitle').html(Search_view_title_template);
// Do this in css
$('body').css('padding-top', '124px');
this.genre_collection = new Genres_collection();
this.genre_collection.fetch();
this.collection = new Tracks_collection();
this.listenTo(this.genre_collection, 'update', this.render);
this.listenTo(this.collection, 'update', this.render_list);
},
render: function() {
console.log('render');
this.$('#container').html(this.template({
genres: this.genre_collection.models
}));
return this;
},
render_list: function() {
console.log('render list');
var html = this.template({
genres: this.genre_collection.models,
tracks: this.collection.models
});
console.log(html);
var $selector = this.$('#tracklist');
console.log($selector);
$selector.replaceWith($(html));
return this;
},
////////////
// events //
////////////
onSearchClick: function() {
console.log('search changed');
this.collection.fetch({
data: { limit: 30, type: 'search', keyword: $('#keyword').val() }
});
},
onGenreChange: function() {
console.log('genre changed');
this.collection.fetch({
data: { limit: 30, type: 'genre', genre_id: $('#genre_select').val() }
});
},
});
$('#pagetitle').html(_.template(Search_view_title_template));
The _.template
function returns a function, which itself returns the rendered template when called.
It can be confused with this.template
which often contains the result of _.template
and is ready to be called (this.template(data)
).
Split your callbacks, functions are cheap and unnecessary switch
are ugly.
I made your search
into onGenreChange
and onSearchClick
.
$('body').css('padding-top','124px');
Try to avoid that, it can be easily done with CSS, or even inline <style>
tag or inline style=""
attribute. If it's necessary for you as it's related to a dynamic behavior, create a class (e.g. search-class
) in a css file, then toggle the class with jQuery, moving the "design" responsability out of the js:
$('body').toggleClass('search-class');
var that = this;
This is only necessary when dealing with callbacks where the context (this
) is different in the callback. In Backbone, most of the time, it's avoidable as the context
option is often available and automatically set on most (like the events
callbacks).
this.$el.find(selector)
This is equivalent to this.$(selector)
. Just a little shortcut.
.replaceWith($(selector,html));
replaceWith
expects a htmlString or Element or Array or jQuery.
$(selector, html)
expects a selector and a context. You want $(html)
to transform your html string into a jQuery element.
Upvotes: 1