user846062
user846062

Reputation:

Backbone View render: fetch-first versus render-first, both approaches have faults?

This is more of a conceptual/architectural question than anything; the typical/popular approach to constructing and instantiating Backbone Views seems to be to only render the View AFTER successfully fetching necessary Model/Collection data from the server (in a success() or done() callback).

This is all well and good, but what if you have some sort of loading indicator or UI element within the View's template that needs to be displayed before/during the fetch? By not rendering the View until the call finishes, you effectively are unable to display such notifications to the user.

Conversely, if you render the View BEFORE making the fetch, you're now able to display such UI elements, but you now run the risk of displaying a mostly-empty template, since your Model/Collection data hasn't been retrieved yet, and this can look rather weird.

What if you need both things: UI notifications before/during the fetch, AND not to render a mostly-empty template pre-fetch? What might be some good approaches towards accomplishing this goal?

Upvotes: 0

Views: 587

Answers (3)

kinakuta
kinakuta

Reputation: 9037

I use a separate loading view to handle the loading gif that listens for relevant events I trigger from the model/collection fetching results. The view responsible for rendering does so without being concerned about the loading gif. The loading gif essentially obscures the results which are "revealed" when the loading gif is removed:

var MyView = Backbone.View.extend({

    initialize: function () {

        this.listenTo(this.collection, 'reset', this.render);
    },

    render: function () {
        // do whatever you need to here
    }
});

var LoadingView = Backbone.View.extend({

    el: '#loading',

    initialize: function () {

        this.listenTo(this.collection, 'fetchStarted', this.showLoader);
        this.listenTo(this.collection, 'fetchFinished', this.hideLoader);
    },

    showLoader: function () {

        var self = this;

        if (this.waitHandle) {
            clearTimeout(this.waitHandle);
            delete this.waitHandle;
        }
        this.waitHandle = setTimeout(function () {
             self.$el.removeClass('hide');
        }, 300);
     },

     hideLoader: function () {

         clearTimeout(this.waitHandle);
         delete waitHandle;
         this.$el.addClass('hide');
    }
});

var MyCollection = Backbone.Collection.extend({

     getResults: function () {

         var self = this;

         this.fetch({
             beforeSend: function () {
                 self.trigger('fetchStarted');
             },
             complete: function () {
                 self.trigger('fetchFinished');
             }
          });
      }
});

I use the timeouts in the loading view to delay showing the loading gif in case they aren't necessary.

Upvotes: 3

David Sulc
David Sulc

Reputation: 25994

I wrote a blog post with a solution that could interest you (http://davidsulc.com/blog/2013/04/01/using-jquery-promises-to-render-backbone-views-after-fetching-data/). Basically, one approach to the problem:

  1. Store the return value of the fetch call (which is a jQuery promise)
  2. Render the loading view (with MyApp.myRegion.show(loadingView);)
  3. If you want, you can already instantiate your view (that requires data) at this point
  4. When the promise is fulfilled (i.e. the fetch is done), display your data-dependent view within the region (as above)

Upvotes: 0

Alex Mcp
Alex Mcp

Reputation: 19315

I usually have an application-level view that will show a loader when it knows one of the app's main collections is being fetched. In face I usually do an ajax before filter to always pop the loader, and hide on completion application wide. It blocks the whole UI, but that's fine unless I'm doing a more complex dashboard-type app where there are clear modular concerns.

Upvotes: 1

Related Questions