I'm new to Marionette and I just can't get routes to work.
I'm using Marionette's 2.4.1 version, and trying to do it the simplest way possible so it'll just work.
This code works for old version of Marionette, v1.0.2, which was included in one of the yeoman's generator. I know there's a huge gap between those two versoins but for every post, blog, official documentation and even books written for this framework code stays the same.
There are no errors in console, but the 'home' method just won't start.
Am I missing something here?
application.js (Application body):
define(['backbone', 'marionette'],
function (Backbone, Marionette) {
'use strict';
var App = new Marionette.Application();
App.Router = Marionette.AppRouter.extend({
appRoutes: {
"home": "home"
}
});
var myController = {
"home": function() {
console.log("This thing just won't work.");
}
};
/* Add initializers here */
App.addInitializer(function () {
console.log('App initialized');
new App.Router({
controller: myController
});
});
App.on("initialize:after", function () {
if (Backbone.history) {
Backbone.history.start();
}
});
return App;
});
main.js (Starts our app defined in application.js):
require(['marionette', 'application'],
function ( Marionette, App ) {
'use strict';
App.start();
});
config.js (Config for require.js)
require.config({
baseUrl: "/scripts",
/* starting point for application */
deps: ['marionette', 'main'],
shim: {
backbone: {
deps: [
'underscore',
'jquery'
],
exports: 'Backbone'
},
marionette: {
deps: ['backbone'],
exports: 'Marionette'
}
},
paths: {
backbone: '../bower_components/backbone/backbone',
jquery: '../bower_components/jquery/dist/jquery',
underscore: '../bower_components/underscore/underscore',
/* alias all marionette libs */
'marionette': '../bower_components/marionette/lib/core/backbone.marionette',
'backbone.wreqr': '../bower_components/backbone.wreqr/lib/backbone.wreqr',
'backbone.babysitter': '../bower_components/backbone.babysitter/lib/backbone.babysitter'
}
});
It looks like you're missing a reference to the router's controller.
Try updating the Router to include a reference to myController:
App.Router = Marionette.AppRouter.extend({
controller: myController,
appRoutes: {
"home": "home"
}
});
See AppRouter docs for more infomation:
http://marionettejs.com/docs/v2.4.1/marionette.approuter.html
It seems that initialize:after in:
App.on("initialize:after", function () {
if (Backbone.history) {
Backbone.history.start();
}
});
isn't supported in newest versions of Marionette and I was supposed start instead:
App.on("start", function () {
if (Backbone.history) {
Backbone.history.start();
}
});
Most posts about Marionette seems to be rather outdated. Those are still really helpful but be sure to verify it with the official framework's documentation.
I should've done that in the first place..
Related
I am trying to convert an existing Backbone.js project into Require.js.
As recommended I am modularising my components. My router is the starting point in the application, thus my main.js require file looks like this:
requirejs.config({
'baseUrl': '/',
'paths': {
'app': 'app',
// define vendor paths
'jquery' : 'bower_components/jquery/jquery',
'underscore' : 'bower_components/underscore/underscore',
'backbone' : 'bower_components/backbone/backbone',
'handlebars' : 'bower_components/handlebars/handlebars',
'nprogress' : 'bower_components/nprogress/nprogress',
},
'shim':{
'jquery': {
'exports': '$'
},
'underscore': {
'exports': '_'
},
'backbone': {
'deps': ['jquery', 'underscore'],
'exports': 'Backbone'
},
'handlebars': {
'exports': 'Handlebars'
}
}
});
require(['app/js/routes/router'], function(Router) {
// Fire up the quattro
});
My router then looks like this
define(['backbone', '/app/js/views/HomepageView'], function(Backbone) {
var AppRouter = Backbone.Router.extend({
routes: {
"": "showHomepage",
"categories/:sofa": "showCategoryList",
"range/:categoryName/:sofaName": "showProductRange",
"customersearch/:customerName": "showCustomerSearch"
}
});
// Initialise our router
var router = new AppRouter;
router.on("route:showHomepage", function(param){
localStorage.removeItem('lastProduct');
var homepageview = new HomepageView({ el: $('#content') });
})
// Start the router
Backbone.history.start();
});
However when I navigate to my route '/' for HomepageView to initialise, I get the following error no matter what I try and I cannot seem to find the solution..
Uncaught ReferenceError: Backbone is not defined HomepageView.js:1
changing the first line to the following would be a start.
define(['backbone', '/app/js/views/HomepageView'], function(Backbone, HomePageView) {
I'm going a bit crazy here. I'm trying to use Grunt to go through a large RequireJS-based project and combine and minify the results during the deployment process. Here is my grunt process (using grunt-contrib-requirejs):
requirejs: {
compile: {
options: {
baseUrl: "public/js",
mainConfigFile: "public/js/config.js",
name: 'config',
out: "public/js/optimized/script.min.js",
preserveLicenseComments: false
}
}
}
Initially, I was taking the outputted script and placing it in the HTML -- but this lead to the 'define is undefined' error that means that RequireJS wasn't evoked. So instead, I'm putting in the HTML like this:
<script data-main="js/optimized/script.min" src="js/vendor/require.js"></script>
However, now I'm only getting a blank page.
The closest thing I can find out there that sounds like this is here, but's not being super helpful right now. For reference, I was using this as a starting point of my project -- however, when I run it, everything seems to be working for them but I can't find the differences.
Here is my config.js file:
require.config({
//Define the base url where our javascript files live
baseUrl: "js",
//Increase the timeout time so if the server is insanely slow the client won't burst
waitSeconds: 200,
//Set up paths to our libraries and plugins
paths: {
'jquery': 'vendor/jquery-2.0.3.min',
'underscore': 'vendor/underscore.min',
'backbone': 'vendor/backbone.min',
'marionette': 'vendor/backbone.marionette',
'mustache': 'vendor/mustache.min',
'bootstrap': 'vendor/bootstrap.min',
'bootbox': 'vendor/bootbox.min',
'jquery-ui': 'vendor/jquery-ui-1.10.2',
'app-ajax': '../conf/app-ajax',
'common': 'common',
'moment': 'vendor/moment.min'
},
//Set up shims for non-AMD style libaries
shim: {
'underscore': {
exports: '_'
},
'backbone': {
deps: ['underscore', 'jquery'],
exports: 'Backbone'
},
'marionette': {
deps: ['backbone', 'underscore', 'jquery'],
exports: 'Marionette'
},
'mustache': {
exports: 'mustache'
},
'bootstrap': {
deps: ['jquery']
},
'bootbox': {
deps: ['jquery', 'bootstrap'],
exports: 'bootbox'
},
'jquery-ui': {
deps: ['jquery']
},
'jquery-layout': {
deps: ['jquery', 'jquery-ui']
}
}
});
//Initalize the App right after setting up the configuration
define([
'jquery',
'backbone',
'marionette',
'common',
'mustache',
'bootbox',
'controllers/GlobalController',
'routers/GlobalRouter'
],
function ($, Backbone, Marionette, Common, Mustache, bootbox) {
//Declare ECMAScript5 Strict Mode first and foremost
'use strict';
//Define the App Namespace before anything else
var App = Common.app_namespace || {};
//Create the Marionette Application
App.Application = new Marionette.Application();
//Add wrapper region, so we can easily swap all of our views in the controller in and out of this constant
App.Application.addRegions({
wrapper: '#wrapper'
});
// Set up Initalizer (this will run as soon as the app is started)
App.Application.addInitializer(function () {
//Reach into Marionette and switch out templating system to Mustache
Backbone.Marionette.TemplateCache.prototype.compileTemplate = function (rawTemplate) {
return Mustache.compile(rawTemplate);
};
var globalController = new App.Controllers.GlobalController();
var globalRouter = new App.Routers.GlobalRouter({
controller: globalController
});
//Start Backbone History
Backbone.history.start();
});
//Start Application
App.Application.start();
}
);
Okay, so this is the crazy simple fix:
In the module that's declared after the require.config, use 'require' instead of 'define' when declaring the module.
If you use 'define', it added 'config' as a dependency of that module, which broke the whole thing. Craziness!
I got following router.js:
define(['jquery', 'underscore', 'backbone', 'views/settings/index'], function($, _, Backbone, SettingsView) {
var Router = Backbone.Router.extend({
container: $('div.main'),
routes: {
"settings": "settings"
, "settings/:query": "settings"
},
settings: function(query) {
this.container.html(SettingsView.render().el);
}
});
if (Router._instance) return Router._instance;
Router._instance = new Router();
return Router._instance;
});
The router gets called in the main.js filed which is marked as first dependency in the requirejs config:
define(['jquery', 'underscore', 'backbone', 'router'], function($, _, Backbone, router) {
// ajax settings (sent cors cookies)
$.ajaxSetup({ xhrFields: { withCredentials: true } });
// register master router
window.router = router;
// start backbone history
Backbone.history.start({ root: "/" });
});
In some view I now want to do:
define(['jquery', 'underscore', 'backbone', 'router'], function($, _, Backbone, router) {
var View = Backbone.View.extend({
initialize: function() {
router.on('route:settings', this.change, this);
},
change: function(query) {
// load some new content for example
}
});
return View;
});
Than I am getting this error message:
Uncaught TypeError: Cannot call method 'on' of undefined
UPDATE:
I added a console.log to each module. So I know in which order the modules are executed. I mentioned that requirejs has the wrong order:
view.js -> main.js -> router.js
instead of:
router.js -> main.js -> view.js
Let see how I can solve this
UPDATE2:
I now wrapped the router event binding part into a setTimeout loop:
setTimeout(function() {
window.router.on('route:settings', this.select, this);
}.bind(this), 20);
And see there it works!
This truly just is a fix but nevertheless a fix :)
Why are you trying to create a singleton instance on your router?
Requirejs resolves every module only once as far as I understand and
as far as I would expect from any decent module loader.
#router.js
define(['jquery', 'underscore', 'backbone', 'views/settings/index'],
function($, _, Backbone, SettingsView) {
//your code omitted ...
//no need to do this
//if (Router._instance) return Router._instance;
//Router._instance = new Router();
//return Router._instance;
//this should work as well and give you a "singleton" instance
return new Router();
});
please check if the names/path you have given to requirejs.config() is correct. I have hit the wall hard a few times because I had a slight typo. requirejs doesnt return any errors on such cases but sadly only undefined.
If you are dead sure that there is no typo I'd try to include main.js as an explicit dependency on the definition of your view.
I think you need to return a reference to the Router object at the end of the first define.
define(['jquery', 'underscore', 'backbone', 'views/settings/index'], function($, _, Backbone, SettingsView) {
var Router = Backbone.Router.extend({
container: $('div.main'),
routes: {
"settings": "settings"
, "settings/:query": "settings"
},
settings: function(query) {
this.container.html(SettingsView.render().el);
}
});
var Singletonizer = function(Singleton) {
if (Singleton._instance) return Singleton._instance;
Singleton._instance = new Singleton();
Singletonizer(Singleton);
};
return Singletonizer(Router);
});
I'm new to RequireJS but seem to be hitting a brick wall.
The trouble starts with my "app" module. I'm not sure how to tell RequireJS to load my leaf modules - packages that depend on "app".
I think I understand why - since nothing in the system depends on them and they aren't registered anywhere, but I need to deal with this case.
How can I let RequireJS know about these modules and to load them appropriately?
Cheers
//index.html
....
<script data-main="app/config" src="/assets/js/libs/require.js"></script>
....
//config.js
require.config({
deps: [ "app" ],
paths: {
libs: "../assets/js/libs",
plugins: "../assets/js/plugins",
jquery: "../assets/js/libs/jquery",
underscore: "../assets/js/libs/underscore",
backbone: "../assets/js/libs/backbone",
marionette: "../assets/js/libs/backbone.marionette"
}
});
//app.js
require(
[ "jquery", "underscore", "backbone", "marionette" ],
function ( $, _, Backbone, Marionette ) {
//....
}
);
//app.view.js
require(
[ "jquery", "underscore", "backbone", "marionette", "app" ],
function ( $, _, Backbone, Marionette, App ) {
//....
}
);
//app.route.js
require(
[ "backbone", "app" ],
function ( Backbone, App ) {
//....
}
);
Hence:
app.js depends on "jquery", "underscore", "backbone", "marionette"
app.view.js depends on "jquery", "underscore", "backbone",
"marionette", "app" app.route.js depends on "backbone", "app"
As stated in the docs ->
http://requirejs.org/docs/api.html#config
dependencies are defined in the deps array. They are the first thing that's loaded when require.js is run, it's really mostly used when you have to define dependencies before you load require.js.
this is what your structure should look like
//config.js
require.config({
paths: {
libs: "../assets/js/libs",
plugins: "../assets/js/plugins",
jquery: "../assets/js/libs/jquery",
underscore: "../assets/js/libs/underscore",
backbone: "../assets/js/libs/backbone",
marionette: "../assets/js/libs/backbone.marionette"
}
});
require(['app']);
//app.js
define(
[ "jquery", "underscore", "backbone", "marionette" ],
function ( $, _, Backbone, Marionette ) {
//....
}
);
//app.view.js
define(
[ "jquery", "underscore", "backbone", "marionette", "app" ],
function ( $, _, Backbone, Marionette, App ) {
//....
}
);
//app.route.js
define(
[ "backbone", "app" ],
function ( Backbone, App ) {
//....
}
);
Bear in mind that all your libraries and modules need to be AMD compliant and if you want to use app as a path like in app.view.js then you need to define it as one. The same with egis, because you can't load modules like so [ "Backbone", "App" ] if they are not defined in require.config as paths.
This is how I startup:
// main.js
define(["jquery", "app", "router"], function ($, App) {
"use strict";
// domReady plugin maybe best used here?
$(function() {
App.start();
});
});
// app.js
define(["backbone", "marionette"], function (Backbone) {
"use strict";
var app = new Backbone.Marionette.Application();
app.on("initialize:after", function(options){
if (Backbone.history){
Backbone.history.start();
}
});
return app;
});
// router.js
define(["backbone", "controller", "marionette"], function(Backbone, controller) {
"use strict";
var Router = Backbone.Marionette.AppRouter.extend({
appRoutes: {
"": "index"
}
});
return new Router({
controller: controller
});
});
// controller.js
define(["view"], function(View) {
return {
"index": {
new View(); // Do what you like hereā¦
}
}
});
// view.js
define(["backbone"], function(Backbone) {
// view here
});
I assume that the dependency to router.js could be put on app.js but basically the call to Backbone.history.start() needs routers to be loaded.
The router has a dependency on the controller. It's the controller that has all the dependencies to the views etc that is used by it. There could be models and collections etc.
Hope that helps.
I have my bootstrap file which defines the require.js paths, and loads the app and config modules.
// Filename: bootstrap
// Require.js allows us to configure shortcut alias
// There usage will become more apparent futher along in the tutorial.
require.config({
paths: {
bfwd: 'com/bfwd',
plugins: 'jquery/plugins',
ui: 'jquery/ui',
jquery: 'jquery/jquery.min',
'jquery-ui': 'jquery/jquery-ui.min',
backbone: 'core/backbone.min',
underscore: 'core/underscore.min'
}
});
console.log('loading bootstrap');
require([
// Load our app module and pass it to our definition function
'app',
'config'
], function(App){
// The "app" dependency is passed in as "App"
// Again, the other dependencies passed in are not "AMD" therefore don't pass a parameter to this function
console.log('initializing app');
App.initialize();
});
app.js is loaded like it should, and it's dependencies are loaded. it's define callback is called, with all the correct dependencies passed as arguments. No error is thrown. HOWEVER, in the bootstrap's callback, App is undefined! no arguments are passed. What can be causing this? Here's my app file ( modified for space)
// Filename: app.js
define(
'app',
[
'jquery',
'underscore',
'backbone',
'jquery-ui',
'bfwd/core',
'plugins/jquery.VistaProgressBar-0.6'
],
function($, _, Backbone){
var initialize = function()
{
//initialize code here
}
return
{
initialize: initialize
};
}
);
As far as I am aware you should probably just drop the 'app' string in your app.js define method.
// Filename: app.js
define([
'jquery',
'underscore',
'backbone',
'jquery-ui',
'bfwd/core',
'plugins/jquery.VistaProgressBar-0.6'
], function($, _, Backbone){
...
);
Ok I had the same problem, the key is the jquery path alias you define. It turns out that RequireJS has some special handling for jquery. If you use the jquery module name it will do a little bit of magic there.
Depending on what you have in jquery.min.js it may cause some problems, also the jquery plugin you have there may be a problem. Here are the relevant lines of code from the RequireJS source:
if (fullName) {
//If module already defined for context, or already loaded,
//then leave. Also leave if jQuery is registering but it does
//not match the desired version number in the config.
if (fullName in defined || loaded[id] === true ||
(fullName === "jquery" && config.jQuery &&
config.jQuery !== callback().fn.jquery)) {
return;
}
//Set specified/loaded here for modules that are also loaded
//as part of a layer, where onScriptLoad is not fired
//for those cases. Do this after the inline define and
//dependency tracing is done.
specified[id] = true;
loaded[id] = true;
//If module is jQuery set up delaying its dom ready listeners.
if (fullName === "jquery" && callback) {
jQueryCheck(callback());
}
}
For me I have it setup such that I have a file called /libs/jquery/jquery.js which returns the jquery object (just a wrapper for RequireJS). What I ended up doing was simply changing the path alias from jquery to $jquery. This helps avoid the undesired magic behavior.
In the original tutorial I read they use jQuery which also works.
This is a simple example that might help get you started:
I've created a very simple module:
https://gist.github.com/c556b6c759b1a41dd99d
define([], function () {
function my_alert (msg) {
alert(msg);
}
return {
"alert": my_alert
};
});
And used it in this fiddle, with only jQuery as an extra dependency:
http://jsfiddle.net/NjTgm/
<script src="http://requirejs.org/docs/release/1.0.7/minified/require.js"></script>
<script type="text/javascript">
require.config({
paths: {
"jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min",
"app": "https://gist.github.com/raw/c556b6c759b1a41dd99d/20d0084c9e767835446b46072536103bd5aa8c6b/gistfile1.js"
},
waitSeconds: 40
});
</script>
<div id="message">hello</div>
<script type="text/javascript">
require( ["jquery", "app"],
function ($, app) {
alert($.fn.jquery + "\n" + $("#message").text());
app.alert("hello from app");
}
);
</script>
This is how I do it with requirejs and backbone:
first, define main or bootstrap file with config:
// bootstrap.js
require.config({
paths: {
text: 'lib/text',
jQuery: 'lib/jquery-1.7.2.min',
jqueryui: 'lib/jquery-ui-1.8.22.custom.min',
Underscore: 'lib/underscore-1.3.3',
Backbone: 'lib/backbone-0.9.2'
},
shim: {
'Underscore': {
exports: '_'
},
'jQuery': {
exports: 'jQuery'
},
'jqueryui': {
exports: 'jqueryui'
},
'Zepto': {
exports: '$'
},
'Backbone': {
deps: ['Underscore', 'Zepto'],
exports: 'Backbone'
}
});
define(function (require) {
'use strict';
var RootView = require('src/RootView');
new RootView();
});
Then, I use this syntax to load my scripts. I find it easier than the array notation to just define my depencies via var declarations.
// rootview.js
define(function (require) {
'use strict';
var $ = require('Zepto'),
Backbone = require('Backbone'),
LoginView = require('./LoginView'),
ApplicationView = require('./ApplicationView'),
jQuery = require('jQuery').noConflict();
return Backbone.View.extend({
// append the view to the already created container
el: $('.application-container'),
initialize: function () {
/* .... */
},
render: function () {
/* .... */
}
});
});
Hope it helps!
This is a bit late, but I just had this problem. My solution can be found here:
https://stackoverflow.com/questions/27644844/can-a-return-statement-be-broken-across-multiple-lines-in-javascript
I posted that question for a different reason, to ask why my fix worked in the first place. Elclanrs provided the perfect answer. To make a long story short, the undefined is probably appearing due to javascript's automatic semicolon insertion: Automatic semicolon insertion & return statements
If you try changing the position of the curly bracket from underneath to directly after the return statement, I think your problem will disappear.
// Filename: app.js
define(
.
.
.
function($, _, Backbone){
var initialize = function()
{
//initialize code here
}
return {
initialize: initialize
};
}
);