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"
})
})
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();
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');
}
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With