Wednesday, October 19, 2011

Limiting Backbone.js Redraws with View Signatures

‹prev | My Chain | next›

Up tonight, a bit of research for Recipes with Backbone. As Nick Gauthier continues to work through new recipes, I am working through some of our existing chapters to beef them up. Tonight, I am going to get started on the View Signature recipe.

The idea behind the view signature is to retain information from previous renders to decide if a model change requires an re-render. In my calendar app:


...the easiest way to trigger a change event is to navigate to a later month, change an appointment in a separate window, then navigate back to the same month. The collection caching solution that I have implemented will display the original view immediately, then fetch any changes from the server, forcing a re-render.

Currently, updates are implemented by binding the View's update() method to the model's change event:
        var Appointment = Backbone.View.extend({
          initialize: function(options) {
            // ...
            options.model.bind('change', this.update, this);
          },
          // ...
          update: function() {
            console.log("[Appointment#update]");
            $(this.el).html(this.template(this.model.toJSON()));
            return this;
          },
          // ...
        });
But, this will cause re-renders even if minor details change. Really, the only reason that I would want to update the Appointment view is if the appointment's title changes.

For that, I build up a view signature that is tied only to the title. If this title-bound signature changes after a change event, only then will I redraw the appointment:
        var Appointment = Backbone.View.extend({
          // ...
          update: function() {
            console.log("[Appointment#update]");

            var signature = $.param({
              title: this.model.get('title')
            });

            if (this.signature === signature) return this;

            console.log("[Appointment#update] sig change: " + this.signature + " -> " + signature);

            this.signature = signature;

            $(this.el).html(this.template(this.model.toJSON()));

            return this;
          },
          // ...
        });
Now, if I update the title of an appointment in a separate window, force a collection cache refresh in my current window, I see the signature has changed in Chrome's Javascript console:


More importantly, the appointment in the calendar is updated.

But, if I repeat the exercise, only updating the description of the appointment (and not the title):


Then a collection cache refresh omits the redraw since the view's signature has not changed:


With a view signature implementation in place, I call it a night here. Up tomorrow: taking this up a notch.

Day #179

3 comments:

  1. I'm sure that's an appropriate time to use a signature, but in your example, why not just bind to 'change:title'. This way, your view will only be notified when the title changes.

    options.model.bind('change:title', this.update, this);

    ReplyDelete
  2. Oops, sorry for the typo. My last comment should have said "I'm sure there's an appropriate time...".

    ReplyDelete
  3. Hahaha. Good point. I really should have made my example dependent on title and description, then added a bunch of unrelated, but oft-changing additional attributes.

    I was more concerned with playing with my new toy and not concerned enough with setting a good context. Thanks for the feedback -- much appreciated!

    ReplyDelete