Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Backbone Marionette and RequireJS Modules Confusion – module seems to be undefined

I just got started using Marionette and I'm reading and following along Marionette - A Gentle Introduction by David Sulc. It's a really good read and it's easy to follow along building the sample application Contact Manager with the companion repository.

However, I had previously set up a project using RequireJS and wanted to translate and integrate the ideas and concepts of that book into this project. I haven't actually gotten that far and I think I might be a little bit confused about the use of Marionette Modules in combination with AMD modules which leads to undefined objects.

To be more specific, let me list app.js, listView.js and listController.js which should be the RequireJS version of this commit of the sample repo.

app.js

/*global define*/
define([
    'marionette'
], function ( Marionette ) {
    'use strict';

    var ContactManager = new Marionette.Application();

    ContactManager.addRegions({
        mainRegion : '#main-region'
    });

    ContactManager.on( 'initialize:after', function() {

        ContactManager.ContactsApp.List.Controller.listContacts();
    });

    return ContactManager;
});

listView.js

/*global define*/
define([
    'app',
    'marionette',
    'handlebars',
    'text!templates/contact.hbs'
], function ( ContactManager, Marionette, Handlebars, contactTemplate ) {
    'use strict';

    var List = ContactManager.module( 'ContactsApp.List' );

    List.Contact = Marionette.ItemView.extend({

        tagName: 'li',
        template : Handlebars.compile( contactTemplate ),

    });

    List.Contacts = Marionette.CollectionView.extend({

        tagName: 'ul',
        itemView: List.Contact
    });

    return List;
});

listController.js

/*global define*/
define([
    'app'
], function ( ContactManager ) {
    'use strict';

    var List = ContactManager.module( 'ContactsApp.List');

    List.Controller = {

        listContacts: function() {

            var contacts = ContactManager.request('contact:entities');

            var contactsListView = new ContactManager.ContactsApp.List.Contacts({
                collection: contacts
            });

            ContactManager.mainRegion.show( contactsListView );
        }
    };

    return List.Controller;
});

So, the error I get is Uncaught TypeError: Cannot read property 'List' of undefined in app.js:15 which refers to this line:

ContactManager.ContactsApp.List.Controller.listContacts();

This means that the ContactsApp module is undefined and this is exactly what I don't understand.

To my understanding I attach the ContactsApp module and the List sub-module to the ContactManager inside either listView.js or listController.js (whichever is called first) with the line:

ContactManager.module( 'ContactsApp.List' );

Shouldn't the ContactsApp be defined inside app.js then?

This is the main.js file which includes the require.config and the entry point to the application:

require.config({
    baseUrl: './scripts',
    paths: {
        jquery     : '../bower_components/jquery/jquery',
        underscore : '../bower_components/underscore/underscore',
        backbone   : '../bower_components/backbone/backbone',
        marionette : '../bower_components/backbone.marionette/lib/backbone.marionette',
        bootstrap  : '../bower_components/sass-bootstrap/dist/js/bootstrap',
        text       : '../bower_components/requirejs-text/text',
        handlebars : '../bower_components/handlebars/handlebars',
        templates  : '../templates'
    },

    shim: {
        underscore : {
            exports : '_'
        },
        backbone : {
            deps : [ 'underscore', 'jquery' ],
            exports : 'Backbone'
        },
        marionette : {
            deps : [ 'backbone' ],
            exports : 'Backbone.Marionette'
        },
        bootstrap : {
            deps : [ 'jquery' ],
        },
        handlebars : {
            exports : 'Handlebars'
        }
    },
    deps : [ 'jquery', 'underscore' ]
});

require([
    'app',
    'bootstrap'
], function ( ContactManager ) {
    'use strict';

    ContactManager.start();
});
like image 477
dschwertfeger Avatar asked Sep 13 '13 10:09

dschwertfeger


1 Answers

RequireJS bascially works like this: declare all the dependencies a given module has, then use them within the call back function.

Here's the issue with your code: in app.js, you require only marionette so as far as RequireJS is concerned, nothing else should be loaded for the module's code to be functional. But then, in that same file, you call ContactManager.ContactsApp.List.Controller.listContacts(). Where does that come from? Nowhere: it isn't defined within the current module, and wasn't declared as a dependency. Therefore, it doesn't exist and you get the undefined issue.

You can't just refer to the module, thinking it's attached to the main application: it only actually gets attached when the Marionette module code gets executed. And for that to happen, it needs to be required as a dependency.

As an aside, you'll have a tough time adapting the book's code to use with RequireJS, because it isn't architected for RequireJS use (in addition to the issue you ran into, you'll have circular dependencies, etc.).

I'd suggest you just read along the book to get a good feel for Marionette on its own, then look into using it with RequireJS. Shameless plug, I also wrote a book on marionette and requirejs.

like image 108
David Sulc Avatar answered Sep 21 '22 10:09

David Sulc