Last night, I was able to work up a rather nice Backbone.js collection caching solution for my calendar application:
The solution is built on sub-collections that do the work of loading a month's worth of calendar appointments. Once successful, the main Backbone appointment collection adds the appointments from the sub-collection to its own internal store and triggers events that render the appointments on the calendar. For good measure, a successful fetch of a month also triggers pre-fetching of the previous and next month's appointments.
The solution felt fairly strong because each layer had a definite purpose. Methods were small. The only "magic" came by a reliance on Backbone's conventions (in particular the
success()
callback). But reliance on convention isn't magic—it is good coding.Anyhow, tonight I have a look through some of the dusty corners to see if I have a robust solution. The first bit that I know to be wonky is loading the default (non-route) page. Currently, it loads a blank October:
The routes already do everything that I want this view to do—tell the calendar to draw the correct month and point the appointments collection to the same month:
var Routes = Backbone.Router.extend({
routes: {
"month/:date": "setMonth"
},
setMonth: function(date) {
console.log("[setMonth] %s", date);
draw_calendar(date);
appointments.setDate(date);
}
});
So maybe I can simply call that method directly?
// Setup the appointments collection
var year_and_month = Helpers.to_iso8601(new Date()).substr(0,7),
appointments = new Collections.Appointments(undefined, {date: year_and_month});
// Setup the views
new Views.Application({collection: appointments});
// Default route
Routes.prototype.setMonth(year_and_month);
// Setup routing
routes = new Routes();
Backbone.history.start();
It turns out, I can. After reloading the page, I now see all of my October appointments (as well as pre-fetched September and November ones):And the Javascript console in Chrome confirms that this is behaving as expected:
Easy enough. I cannot say with certainty that this is the ideal solution for a default route in a Backbone application, but I cannot argue with the results.
The other problem may prove a bit more challenging. When I navigate to the next month, I should see two appointments: one to get started on the final copy of Recipes with Backbone on the November 1 and one to deliver the final version of the book on November 30.
I see neither of the expected appointments, but do see several ghost October appointments:
The Javascript console indicates that the collection remains intact (and December is pre-fetched):
So this must be a problem with triggering the appointment views. Since this bug is only seen on the second page, I eschew the debugger for fear of the F8 (continue) key being worn out on my keyboard. Instead, I add logging to the render method:
var Appointment = Backbone.View.extend({
// ...
render: function() {
console.log(this.model.toJSON());
console.log(this.el);
console.log(this.container);
$(this.el).html(this.template(this.model.toJSON()));
this.container.append($(this.el));
return this;
},
// ...
});
Checking the Javascript console for the first November appointment, I see that it thinks the containing element is December 6!To resolve, I need to make the container element a method to be calculated each time:
var Appointment = Backbone.View.extend({
// ...
render: function() {
$(this.el).html(this.template(this.model.toJSON()));
this.container().append($(this.el));
return this;
},
container: function() {
return $('#' + this.model.get('startDate'));
},
// ...
});
With that, I can navigate between months:Nice! Yesterday's caching solution caused neither of these two bugs. I only needed to call a default route and to use a dynamic container method for the appointment views. Maybe my caching solution is... not bad? I will find out tomorrow... when I attempt cache invalidation.
Day #175
No comments:
Post a Comment