Skip to content

Instantly share code, notes, and snippets.

@chiplay
Created October 24, 2013 23:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save chiplay/7147176 to your computer and use it in GitHub Desktop.
Save chiplay/7147176 to your computer and use it in GitHub Desktop.
RequireJS + Backbone + Marionette + Modules
define([
'config',
'underscore',
'backbone',
'marionette',
'vent'
],
function (config, _, Backbone, Marionette, vent) {
var app = new Marionette.Application();
app.addRegions({
header: '#header',
main: '#main',
footer: '#footer'
});
app.addInitializer(function (options) {
app.config = {};
_.extend(app.config, config);
app.namespaces = options.namespaces;
app.router = new options.Router({
controller: options.controller
});
});
app.on('initialize:after', function () {
if (Backbone.history) {
Backbone.history.start({ pushState: true });
}
});
// Get global settings from config
vent.reqres.setHandler('config',function(optionName){
if (!optionName){ return; }
var value;
if (app.config.settings && (app.config.settings[optionName] !== undefined)){
value = app.config.settings[optionName];
}
return value;
});
return app;
});
define([
'app',
'underscore',
'backbone',
'marionette',
'vent'
], function (app, _, Backbone, Marionette, vent) {
var AppController = Marionette.Controller.extend({
index: function() {
if (app.config.settings.city) {
this.invokeSubRoute('home');
} else {
Backbone.history.navigate('welcome', { trigger: true });
}
},
invokeSubRoute: function(route, subroute){
if (!_.contains(app.namespaces,route)) {
var msg = 'Route is not a defined namespace ';
throw new Error(msg);
}
subroute = subroute || '';
// follows a /modules/product/product.js naming convention
require(['modules/' + route + '/' + route],
function(){
app[route].start();
});
}
});
return new AppController();
});
require.config({
"paths": {
"jquery": "libs/jquery/jquery",
"underscore": "libs/underscore/underscore",
"Handlebars": "libs/handlebars/handlebars",
"backbone": "libs/backbone/backbone",
"marionette": "libs/marionette/lib/core/amd/backbone.marionette",
"marionette.subapprouter": "libs/marionette.subapprouter/Marionette.SubAppRouter",
"backbone.wreqr": "libs/backbone.wreqr/lib/amd/backbone.wreqr",
"backbone.babysitter": "libs/backbone.babysitter/lib/amd/backbone.babysitter",
"text": "libs/text/text",
"hbar": "libs/requirejs-handlebars/hbars"
},
"shim": {
"underscore": {
"exports": "_"
},
"backbone": {
"deps": [
"jquery",
"underscore"
],
"exports": "Backbone"
},
"Handlebars": {
"exports": "Handlebars"
}
}
});
require(['app','router','controller'],function(app, router, controller){
var options = {
'controller': controller,
'Router': router,
'namespaces': [ 'welcome', 'home', 'product' ]
};
app.start(options);
});
// from https://github.com/relayfoods/Marionette.SubAppRouter
;define([
'underscore',
'backbone',
'marionette'
],
/**
* A module that defines and adds Marionette.SubAppRouter to the Marionette object
* @module marionette.subapprouter
* @requires module:backbone
* @requires module:marionette
* @param {Function} Backbone - Backbone.js
* @param {Function} Marionette - Marionette.js
* @exports marionette
*/
function(_, Backbone, Marionette) {
/**
* Creates an AppRouter with a particular prefix at the beginning of each route.
* @class Marionette.SubAppRouter
* @alias module:marionette.subapprouter
* @classdesc A router that prepends a specified prefix (passed on instantiation) onto each specified `route` or
* `appRoute`. Directly extends from Backbone.SubRoute, but also uses the additional logic provided by
* Marionette.AppRouter to enable controller objects linked to the `appRoutes` hash. Useful for sub applications
* that live in sub-directories under root and want to handle all of their own routing.
*/
Marionette.SubAppRouter = Marionette.AppRouter.extend({
/**
* @constructs Marionette.SubAppRouter
* @param {string} [prefix] - The prefix string to prepend to all routes, making them act as if relative. If
* blank, then it just acts like a regular Backbone.Router.
* @param {Object} [options] - The options object expected by Marionette.AppRouter.
* @param {Object} [options.controller] - An object with function properties corresponding to the hash values
* from `routes` and `appRoutes`.
*/
constructor: function(prefix, options) {
var controller,
appRoutes,
routes = {};
// each subapproute instance should have its own appRoutes hash
this.appRoutes = _.clone(this.appRoutes);
// Prefix is optional, set to empty string if not passed
this.prefix = prefix = prefix || "";
// SubRoute instances may be instantiated using a prefix with or without a trailing slash.
// If the prefix does *not* have a trailing slash, we need to insert a slash as a separator
// between the prefix and the sub-route path for each route that we register with Backbone.
this.separator = (prefix.slice(-1) === "/") ? "" : "/";
// if you want to match "books" and "books/" without creating separate routes, set this
// option to "true" and the sub-router will automatically create those routes for you.
var createTrailingSlashRoutes = options && options.createTrailingSlashRoutes;
if (this.appRoutes) {
if (options && options.controller) {
this.controller = options.controller;
}
_.each(this.appRoutes, function(callback, path) {
if (path) {
// Strip off any leading slashes in the sub-route path,
// since we already handle inserting them when needed.
if (path.substr(0) === "/") {
path = path.substr(1, path.length);
}
routes[prefix + this.separator + path] = callback;
if (createTrailingSlashRoutes) {
routes[prefix + this.separator + path + "/"] = callback;
}
} else {
// Default routes (those with a path equal to the empty string)
// are simply registered using the prefix as the route path.
routes[prefix] = callback;
if (createTrailingSlashRoutes) {
routes[prefix + "/"] = callback;
}
}
}, this);
// Override the local sub-routes with the fully-qualified routes that we just set up.
this.appRoutes = routes;
}
// Required to have Marionette AppRouter set up routes
Marionette.AppRouter.prototype.constructor.call(this, options);
// grab the full URL
var hash;
if (Backbone.history.fragment) {
hash = Backbone.history.getFragment();
} else {
hash = Backbone.history.getHash();
}
// Trigger the subroute immediately. this supports the case where
// a user directly navigates to a URL with a subroute on the first page load.
// Check every element, if one matches, break. Prevent multiple matches
_.every(this.appRoutes, function(key, route) {
// Use the Backbone parser to turn route into regex for matching
if (hash.match(Backbone.Router.prototype._routeToRegExp(route))) {
Backbone.history.loadUrl(hash);
return false;
}
return true;
}, this);
if (this.postInitialize) {
this.postInitialize(options);
}
}
});
return Marionette;
});
define([
'app',
'vent',
'jquery',
'underscore',
'backbone',
'marionette.subapprouter',
'common/HeaderView',
'common/FooterView',
'modules/product/Layout'
], function(app, vent, $, _, Backbone, Marionette, HeaderView, FooterView, LayoutView) {
var namespace = 'product';
// Using Marionette Modules (light) to allow for simple start and stop methods
// with built in initializers / finalizers / events for startup and breakdown
app.module(namespace, function(){
var Router = Marionette.SubAppRouter.extend({
appRoutes: {
'': 'index',
':id': 'showProduct'
}
});
var controller = {
index: function () {
this._setupLayout();
},
showProduct: function(id, model) {
var params = {};
params.Productid = id;
if (model) params.model = model;
this._setupLayout(params);
},
// ensure the layout is in place, and set it up so
// that the layout is removed when the layout is closed
_setupLayout: function(params){
if (this.layout){ return; }
params = params || {};
var _this = this;
this.layout = new LayoutView(params);
this.layout.on('close', function(){
delete that.layout;
});
app.main.show(this.layout);
}
};
this.addInitializer(function(options) {
this.router =
new Router(namespace, {
controller: controller,
createTrailingSlashRoutes: true
});
app.header.show( new HeaderView() );
app.footer.show( new FooterView() );
});
this.addFinalizer(function(options) {
// tear down, shut down and clean up the module, here
this.options = options || {};
});
this.on('before:start', function () {
vent.trigger(namespace+':starting', this);
});
this.on('start', function () {
vent.trigger(namespace+':started', this);
});
});
// Since app.module is immediately invoked and attached
// to the Marionette App, there is no need to return anything
});
define(['marionette'], function(Marionette) {
var AppRouter = Marionette.AppRouter.extend({
appRoutes: {
'': 'index',
':namespace(/*subroute)': 'invokeSubRoute'
}
});
return AppRouter;
});
define([
'backbone.wreqr',
'underscore'
],
function(Wreqr, _) {
var Vent = {};
Vent = Wreqr.EventAggregator.extend({
constructor: function(debug){
this.commands = new Wreqr.Commands();
this.reqres = new Wreqr.RequestResponse();
Wreqr.EventAggregator.prototype.constructor.apply(this, arguments);
}
});
_.extend(Vent.prototype, {
// Command execution, facilitated by Backbone.Wreqr.Commands
execute: function(){
var args = Array.prototype.slice.apply(arguments);
this.commands.execute.apply(this.commands, args);
},
// Request/response, facilitated by Backbone.Wreqr.RequestResponse
request: function(){
var args = Array.prototype.slice.apply(arguments);
return this.reqres.request.apply(this.reqres, args);
}
});
return new Vent();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment