1
votes

First off - I am a MarionetteJS noob.

I am having trouble making an ItemView display a loading message or throbber while it is being fetched. This is especially problematic when this ItemView is being displayed from a deep link in Backbone's history (i.e. the ItemView is the first page of the app being displayed since the user linked directly to it). I want to indicate that the page is loading (fetching), preferably with a simple view, and then show the real templated view with the fetched model.

I have seen other answers on SO like Marionette.async, (which has been deprecated) and changing the template during ItemView.initalize().

Anybody (Derrick?) got any suggestions or best practices here?

UPDATE:

I am getting the model from the collection using collection.get(id), not using model.fetch() directly.

After thinking about this, the real question is where should this be implemented:

  • I could change my controller to see if the model exists in the collection (and if the collection is loaded) and decide which view to show accordingly. this seems like a lot of boilerplate everywhere since this could happen with any ItemView and any controller.

  • I could change my ItemView initialize to test for existence of the model (and a loaded collection), but same comment here: every ItemView could have this problem.

    UPDATE 2: This is what I ended up with, in case anybody else want this solution:

    app.ModelLayout = Backbone.Marionette.Layout.extend({  
        constructor: function(){  
            var args = Array.prototype.slice.apply(arguments);    
            Backbone.Marionette.Layout.prototype.constructor.apply(this, args);    
            // we want to know when the collection is loaded or changed significantly    
            this.listenTo(this.collection, "reset sync", this.resetHandler);    
        },    
        resetHandler: function () {    
            //  whenever the collection is reset/sync'ed, we try to render the selected model    
            if (this.collection.length) {    
                // when there is no model, check to see if the collection is loaded and then try to get the    
                // specified id to render into this view    
                this.model = this.collection.get(this.id);    
            }    
            this.render();    
        },    
        getTemplate: function(){    
            // getTemplate will be called during render() processing.  
            // return a template based on state of collection, and state of model    
            if (this.model){    
                // normal case: we have a valid model, return the normal template    
                return this.template;    
            } else if (this.collection && this.collection.isSyncing) {    
                // collection is still syncing, tell the user that it is Loading    
                return this.loadingView;    
            } else {    
                // we're not syncing and we don't have a model, therefore, not found    
                return this.emptyView;    
            }    
        }    
    });    
    

    And here is how to use it:

    // display a single model on a page
    app.Access.Layout.CardLayout = app.ModelLayout.extend({
        regions: {
            detailsRegion:"#detailsRegion",
            eventsRegion:"#eventsRegion"
        },
        template:"CardLayout",  // this is the normal template with a loaded model
        loadingView:"LoadingView",  // this is a template to show while loading the collection
        emptyView:"PageNotFoundView",  // this is a template to show when the model is not found
        onRender : function() {  
            this.detailsRegion.show( blah );  
            this.eventsRegion.show( blah );  
        }
    });
    

    thanks!

  • 2

    2 Answers

    0
    votes

    I'd suggest using jQuery deferreds:

    1. Start fetching your data, and store the return value (which is a jQuery promise)
    2. Instanciate your content view
    3. Show your loading view
    4. When the promise is done, show the view containing the content

    I've talked about implementing this technique on my blog:

    The issue with the solution linked by Rayweb_on, is that your loading view will be displayed any time your collection is empty (i.e. not just when it's being fetched). Besides, the ItemView doesn't have an emptyView attribute, so it won't be applicable to your case anyway.

    Update:

    Based on your updated question, you should still be able to apply the concept by dynamically specifying which template to use: https://github.com/marionettejs/backbone.marionette/blob/master/docs/marionette.view.md#change-which-template-is-rendered-for-a-view

    Then, when/if the data has been fetched successfully, trigger a rerender in the view.

    1
    votes

    For the ItemView

    I think you can add a spinner in your initialize function, I really like spin.js http://fgnass.github.io/spin.js/ because its pretty easy and simple to use, and you can hide the spinner in the onRender function of the Itemview

    For The CollectionView

    in the CollectionView you could handle it like this....

    Take a look at the solution that Derick posted.. https://github.com/marionettejs/backbone.marionette/wiki/Displaying-A-%22loading-...%22-Message-For-A-Collection-Or-Composite-View