-
-
Save wesleytodd/3948155 to your computer and use it in GitHub Desktop.
wl.ready(function() { | |
// Backbone setup within scope | |
var Image = Backbone.Model.extend({ | |
defaults: { | |
'file_tn': 'undefined', | |
'file_lg': 'undefined' | |
}, | |
initialize: function() { | |
this.on('all', function(e) { wl.log('Image event: ' + this.get('file_tn') + ': ' + e); }); | |
} | |
}); | |
var Gallery = Backbone.Collection.extend({ | |
model: Image, | |
url: '/backbone-gallery/filterImages', | |
initialize: function() { | |
this.on('all', function(e) { wl.log('Gallery event: ' + e); }); | |
} | |
}); | |
var GalleryContainer = Backbone.View.extend({ | |
events: { | |
'change .filter': 'filterImages' | |
}, | |
initialize:function () { | |
this.on('all', function(e) { wl.log('GalleryView event: ' + e); }); | |
this.collection.on('reset', this.render, this); | |
}, | |
filterImages: function(e) { | |
e.preventDefault(); | |
this.collection.fetch({ | |
type: 'POST', | |
data: { | |
filters: { | |
track: $('#filters\\[track\\]').val(), | |
team: $('#filters\\[team\\]').val(), | |
driver: $('#filters\\[driver\\]').val() | |
} | |
} | |
}); | |
}, | |
render:function() { | |
this.$('#gallery_images').empty(); | |
_.each(this.collection.models, function (model) { | |
this.$('#gallery_images').append(_.template($('#gallery_image_template').html(), { image:model.attributes })); | |
}, this); | |
return this; | |
} | |
}); | |
var gallery_images = new Gallery(); | |
var gallery_container = new GalleryContainer({ el: $('#gallery_container'), collection: gallery_images }); | |
}); |
oops, I thought I could make line specific comments, but apparently I cannot. So here is the list:
Line 1: This call defers the setup of all the code inside until the page is ready. Because of that it can make the page render seem slower because it has so much to do. In a simple example like this that is not such a big deal, but if it was crunching more data or doing more DOM manipulation that could be worse. And the change is so simple it makes it a no-brainier to just do:
// Everything else here...
wl.ready(function(){
var gallery_images = new Gallery();
var gallery_container = new GalleryContainer({ el: $('#gallery_container'), collection: gallery_images });
});
One other thing this enables you to do is move the parts of the Backbone app into separate files for ease of use when the project gets bigger.
Line 33: One thing that I have found is that you often want to run the fetch
method from many places. When you do you end up having to write the type: 'POST'
and data: ...
things over and over. To keep things dry you can move that into the fetch
method of the controller:
var Gallery = Backbone.Collection.extend({
fetch : function(options){
if (!_.isUndefined(options)) options = {};
if (!_.isUndefined(options.data)) options.data = {};
options.data = _.extend({
track: $('#filters\\[track\\]').val(),
team: $('#filters\\[team\\]').val(),
driver: $('#filters\\[driver\\]').val()
}, options.data);
var args = _.extend({
type: 'POST'
}, options);
Backbone.Collection.prototype.fetch.call(this, args);
}
});
Unfortunately JavaScript is a bit verbose when you want to make things flexible, but it is what it is. So basically now this will let you just call fetch
wherever you need to and it will grab the filter values automatically.
Line 45: For performance reasons you always want to keep the DOM manipulation to a minimum. So running append
for every new image is not the best route. Also, the _.template
makes it so you can compile and re-use the templates. So to get both of these fixed I would recommend something more like this:
render:function() {
// Setup a new element to add your images to
var
// pre-compile the template. tmpl is a function.
tmpl = _.template($('#gallery_image_template').html());
// the 'each' function is built into the collection, so you can call it directly
this.collection.each(function (model) {
// two things here: the toJSON method is the recommended way to access a model for rendering
// the output of the tmpl function is appended to the new element to prevent re-draws
$el.append( tmpl({ image: model.toJSON() }) );
}, this);
// replace with so that there is only one re-draw triggered by DOM manipulation
this.$('#gallery_images').replaceWith($el);
return this;
}
I think that rendered the "div" as html...here is the full code snippet
render:function() {
// Setup a new element to add your images to
var $el = $('<div id="gallery_images" />'),
// pre-compile the template. tmpl is a function.
tmpl = _.template($('#gallery_image_template').html());
// the 'each' function is built into the collection, so you can call it directly
this.collection.each(function (model) {
// two things here: the toJSON method is the recommended way to access a model for rendering
// the output of the tmpl function is appended to the new element to prevent re-draws
$el.append( tmpl({ image: model.toJSON() }) );
}, this);
// replace with so that there is only one re-draw triggered by DOM manipulation
this.$('#gallery_images').replaceWith($el);
return this;
}
Swank. Thanks Wes!
I'll post back again when I get your suggestions in place. They all make sense, and it would have taken me a while to figure out the best way to implement them.
One question though about moving the backbone structure out of the wl.ready() function. What's the best way to prevent conflicts with other variables that may be referenced in the same scope? Gallery and Image seem pretty common. Would you use some kind of prefix convention?
Other than some different variable naming here and there, I've got your suggestions in place. I've separated out the backbone structure from the wl.ready() function. I've also removed the event logging code I had in place for each initialize method. I'm liking this stuff. Thanks again Wes!
Hey! Sorry for my slow response, I thought I should have gotten an email on this but never did. Anyway, the best way to move it out of the wl.ready()
is to use the module pattern:
var dulaney_gallery = (function(){
// All your code
// return the parts you want to expose to the world
return {
Gallery : Gallery,
GalleryContainer : GalleryContainer
};
})();
wl.ready(function(){
var gallery_images = new dulaney_gallery.Gallery();
var gallery_container = new dulaney_gallery.GalleryContainer({
el: $('#gallery_container'),
collection: gallery_images
});
});
For more on the module pattern you can look at this: Learning JavaScript Design Patterns
Cool. I'll implement those mods and check out that link when I get some free time again. Thanks again for the great advice!
Hey Wes!
Overall it looks great!! The backbone portions of this are exactly how I would have set them up. I only have a few small things that I would have changed.