2
votes

I am trying to get to grips with Backbone and Require JS using marionette for some of its excellent features. However I am finding a few issues with the app being available to views:

main.js

require(['application'], function(app){
app.start();
});

application.js

define([
'marionette',
'router'
], function(marionette, Router){

"use strict";

var app = new marionette.Application();

app.addRegions({
    header : 'header',
    main   : '#main'
});

app.addInitializer(function(){
    this.router = new Router();
});

return app;

});

dashboard.js

define([
'application',
'underscore',
'backbone', 
], function(app, _, Backbone) {
var DashboardView = Backbone.View.extend({

initialize: function() {
    console.log(app);
    $('a').click(function(e){
        e.preventDefault();
        app.router.navigate("claims", {
           trigger: true 
        });
    });
},

});
return DashboardView;
});

I am receiving undefined in the console log? Should the application be using requirejs modules instead?

EDIT: Update with require

require.config({

paths: {
    'jquery'        : '../vendors/jquery-1.8.2',
    'underscore'    : '../vendors/underscore',
    'text'          : '../vendors/text',
    'json2'         : '../vendors/json2',
    'backbone'      : '../vendors/backbone',
    'marionette'    : '../vendors/plugins/backbone.marionette',
    'paginator'     : '../vendors/plugins/backbone.paginator',
    'relational'    : '../vendors/plugins/backbone.relational',
    'moment'        : '../vendors/moment',
    'bootstrap'     : '../vendors/bootstrap',
    'datepicker'    : '../vendors/plugins/bootstrap.datepicker',
    'templates'     : 'templates/'
},

shim: {

    backbone: {
        deps: ['underscore', 'jquery'],
        exports: 'Backbone'
    },
    marionette : {
        exports : 'Backbone.Marionette',
        deps : ['backbone']
    },
    paginator: {
        deps: [
        'backbone',
        'underscore',
        'jquery'
        ],
        exports: 'Backbone.Paginator'
    },
    relational: ['backbone'],
    underscore: {
        'exports': '_'
    },
    bootstrap: {
        deps: ['jquery'],
        exports: "bootstrap"
    },
    datepicker: {
        deps: ['jquery','bootstrap'],
        exports: "datepicker"
    },
    moment: {
        exports: 'moment'
    }
}

});

require(['application'], function(app){
    app.start();
});

router

define([
  'views/claims/PaginatedList',
  'views/dashboard/Dashboard'
  ], function(PaginatedClaimListView, DashboardView){

var Router = Backbone.Router.extend({

    routes: {

        "": "index",
        "claims": "claims"

    },

    initialize: function(){
        Backbone.history.start({
            pushState: true,
            root: '/app_dev.php/hera'
        });
    },

    index: function(){
        var dashboard = new DashboardView();
    },

    claims: function(){
        var claimListView = new PaginatedClaimListView();
    }

});

return Router;

});
3
How did you configure require.js? Can you report your shim and paths configs?Ingro
@Ingro I have added this in my original question now, thanks.Hard-Boiled Wonderland
Well I don't see anything strange, I'm using a similar setup (I just don't proxy marionette and use the full syntax when i need it, like 'new Backbone.Marionette.Application()') and everything works fine. How did you call dashboard.js? It's app defined inside your main function? What's inside 'vent'?Ingro
I removed vent and I have added the Router so you can see how Dashboard is called. App is defined at the bottom of main.js I have updated so it is clearer. (Bottom of require js)Hard-Boiled Wonderland

3 Answers

2
votes

I think I've figured it out, even if I'm not 100% sure why.

The problem lies in your Router definition. Putting there your views with a reference to Application makes the router start before app.start() is called in main.js. In fact if you put a console.log(app) in your main.js you'll notice that it gets called after the one in dashboard.js.

So here's how I've resolved it:

define(['backbone'], function(Backbone){

var Router = Backbone.Router.extend({

    routes: {
        "": "index",
        "claims": "claims"
    },

    initialize: function(){
        Backbone.history.start({
            pushState: true,
            root: '/app_dev.php/hera'
        });
    },

    index: function(){
        require(['views/dashboard/Dashboard'],function(DashboardView){
            var dashboard = new DashboardView();
        });
    },

    claims: function(){
        require(['views/claims/PaginatedList'],function(PaginatedClaimListView){
             var claimListView = new PaginatedClaimListView();
        });
    }

});

return Router;

});

I'm not sure if there's a better solution that keeps your views defined in your router, anyway this works and will make your router lighter, especially if your views grown in number...

0
votes

I've found that usually whenever I have Require.js modules set to undefined when they shouldn't be, it's because of a circular dependency. For instance, let's say "vent" depended on "dashboard"; you'd have: vent needs dashboard needs application needs vent ...

When this happens Require's response is essentially just to just pick one of the files and make it work, but any of the other files involved in the circular dependency come in as undefined.

You can test this theory by removing imports to see if that fixes your undefined. Alternatively the Require folks have a tool you can download called xrayquire which assists in finding circular dependencies.

0
votes

Do you have to access the router via app.router? Can you not just have dashboard require router on its own, and access it directly? As long as application has already executed (which it should have, at that point), then you don't need to access it via app. I think your problem is definitely a complicated circular dependency.