Magic grid: Don't show view before loading all images
Im displaying a CompositeView in which each model has a property that is the url for the image. If I just show the view in the area, the images haven't finished loading, which doesn't look very sexy (they are displayed when they are loaded).
I would like to wait for all the images to be loaded before I render the view. Right now, I'm trying to get it to work by postponing the load on the image, but this is probably not the most correct way (maybe it should be done on the model?).
applications/list_view.js
App.module("ApplicationsApp.List", function(List, App, Backbone, Marionette, $, _){
List.Application = Marionette.ItemView.extend({
tagName: 'li',
template: Templates.applications.application
});
List.Applications = Marionette.CompositeView.extend({
className: "applications",
template: Templates.applications.applications,
childView: List.Application,
childViewContainer: "ul"
});
});
applications/list_controller.js
App.module("ApplicationsApp.List", function(List, App, Backbone, Marionette, $, _) {
List.Controller = {
listApplications: function() {
// Set layout
App.trigger("set:layout:authenticated");
// Fetch the applications
var fetchingApplications = App.request('application:entities');
$.when(fetchingApplications).done(function(applications) {
var applicationsListView = new List.Applications({
collection: applications
});
var deferreds = [];
applications.each(function(application) {
deferreds.push(function() {
var loader = new Image();
loader.onload = function() {
console.log(application.get("image_path"));
};
loader.src = application.get("image_path");
});
});
$.when.apply($, deferreds).then(function() {
App.layout.mainRegion.show(applicationsListView);
});
});
}
};
});
source to share
I think your approach is correct, I will try to encapsulate the data / image loading into one function using a repository, probably
$.when(applicationsRepository.getAll()).then(function (apps) {
var appsView = new App.Views.ApplicationListView({ collection: apps});
App.layout.mainRegion.show(appsView );
});
So this code will be part of your controller, as you already know it, when the repository finishes downloading, the controller just displays the data on the screen, something similar to server side MVC with repositories.
getAll () will contain the code you have on the controller to load data and images, the repository will be an additional module in your puppet app.
So your getAll method would be something like
var getAll = function () {
var deferred = $.Deferred();
_getAll(function (apps) {
deferred.resolve(apps);
});
return deferred.promise();
};
var _getAll = function (callback) {
//code to create the app collection
apps.on("reset", function(apps){
//load images
//callback(apps);
});
apps.fetch({ reset: true });
};
To identify and perform some action (callback) after the last image is loaded, I will probably use a counter set on the total number of images (from the collection) and decrement it every time the load handler is executed. $ (window) .load () may be and the option will fire after the entire page has loaded.
Thank.
source to share
I ended up doing this (inspired by Wait for the image to load before proceeding ):
applications/list_controller.js
App.module("ApplicationsApp.List", function(List, App, Backbone, Marionette, $, _) {
List.Controller = {
listApplications: function() {
self = this;
// Set layout
App.trigger("set:layout:authenticated");
// Fetch the applications
var fetchingApplications = App.request('application:entities');
// Perform actions when applications have been fetched
$.when(fetchingApplications).done(function(applications) {
// Load all images in applications
var imgLoaders = [];
applications.each(function(application) {
var src = application.get("image_path");
imgLoaders.push(self.loadImage(src));
});
// Perform actions when images have been loaded
$.when.apply($, imgLoaders).done(function() {
var applicationsListView = new List.Applications({
collection: applications
});
App.layout.mainRegion.show(applicationsListView);
});
});
},
loadImage: function(src) {
var deferred = $.Deferred();
var img = new Image();
img.onload = function() {
deferred.resolve();
};
img.src = src;
return deferred.promise();
}
};
});
source to share