Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone Marionette: using emptyView for loading

I am using Backbone Marionettes CollectionView. I am trying to indicate that the collection is loading, todo so I am using the emptyView to display a loader.

However this is bad logic, because sometimes collections are empty, and therefore it becomes a broken loader.

I tried using this script but it didnt work: https://github.com/surevine/marionette-loadingview-plugin

Does anyone have a better solution? Heres my current code:

//loadingview
define(["marionette", "text!app/templates/loading.html"], function(Marionette, Template) {
  "use strict";
  return Backbone.Marionette.ItemView.extend({
    template: Template
  });
})

//collection
define(["marionette", "text!app/templates/events/collection.html", "app/collections/events", "app/views/events/item", 'app/views/loading'], function (Marionette, Template, Collection, Row, LoadingView) {
  "use strict"
  return Backbone.Marionette.CompositeView.extend({
    template: Template,
    itemView: Row,
    itemViewContainer: "ul",
    emptyView: LoadingView,
    initialize: function () {
      this.collection = new Collection()
      return this.collection.fetch()
    }
  })
})

//item
define(["marionette", "text!app/templates/events/item.html"], function(Marionette, Template) {
  "use strict";
  return Backbone.Marionette.ItemView.extend({
    template: Template,
    tagName: "li"
  })
})
like image 386
azz0r Avatar asked Sep 27 '13 14:09

azz0r


2 Answers

You can simply supply some static HTML in the element where you plan to show your view. The static content will be displayed on page load and when the model successfully returns data, you will show the view and thereby overwrite the static content.

Check out this jsFiddle which demonstrates it. The static content just says "Loading..." but of course you can make it as fancy as you like with spinner gif's and all. http://jsfiddle.net/tonicboy/5dMjD/6/

HTML:

<header>
     <h1>A Marionette Playground</h1>
</header>
<article id="main">LOADING...</article>
<script type="text/html" id="sample-template">
    Store name: <span> <%= venue.name %> </span>.
</script>

Code:

// Define the app and a region to show content
// -------------------------------------------

var App = new Marionette.Application();

App.addRegions({
    "mainRegion": "#main"
});

// Create a module to contain some functionality
// ---------------------------------------------

App.module("SampleModule", function (Mod, App, Backbone, Marionette, $, _) {

    // Define a view to show
    // ---------------------

    var MainView = Marionette.ItemView.extend({
        template: "#sample-template"
    });

    // Move this to outside the Controller
    // as this gives access to other Views
    // Otherwise you would have to declare a New Model inside every controller
    var Book = Backbone.Model.extend({
        url: 'https://api.foursquare.com/v2/venues/4afc4d3bf964a520512122e3?oauth_token=EWTYUCTSZDBOVTYZQ3Z01E54HMDYEPZMWOC0AKLVFRBIEXV4&v=20130808',
        toJSON: function () {
            return _.clone(this.attributes.response);
        }
    })

 // Define a controller to run this module
    // --------------------------------------
    var Controller = Marionette.Controller.extend({

        initialize: function (options) {
            this.region = options.region;
            this.model = options.model;
            // Listen to the change event and trigger the method
            // I would prefer this as this is a cleaner way of binding and
            // handling events
            this.listenTo(this.model, 'change', this.renderRegion);
        },
        show: function () {
            this.model.fetch();
        },
        renderRegion: function () {
            var view = new MainView({
                el: $("#main"),
                model: this.model
            });
            this.region.attachView(view);
            this.region.show(view);
        }
    });


    // Initialize this module when the app starts
    // ------------------------------------------

    Mod.addInitializer(function () {
        // I would create the model here and pass it to the controller
        var myBook = new Book();
        Mod.controller = new Controller({
            region: App.mainRegion,
            model: myBook
        });
        Mod.controller.show();
    });
});

// Start the app
// -------------

App.start();
like image 39
T Nguyen Avatar answered Oct 30 '22 05:10

T Nguyen


What I usually do is listen to the 'request' and 'sync' events of the Collection/Model. Something like this:

var View = Backbone.Marionette.CompositeView.extend({
  template: Template,
  itemView: Row,
  itemViewContainer: "ul",

  collectionEvents: {
    'request': 'showLoading',
    'sync': 'hideLoading'
  },

  initialize: function () {
    this.collection = new Collection();
    return this.collection.fetch();
  }

  showLoading: function() {
    this.$el.addClass('loading');
  },

  hideLoading: function() {
    this.$el.removeClass('loading');
  }
});
like image 69
gbsice Avatar answered Oct 30 '22 06:10

gbsice