Search code examples
javascriptdesign-patternsrevealing-module-pattern

Compiling revealing module pattern


I'm writing an app that is using a revealing-ish module pattern. I'm using Gulp for compilation, so I can set the order of compilation. Aside from some jQuery, there aren't any other frameworks or libraries at play.

I'm trying to break the files up:

  • The site's main JS file,
  • one for a respective feature's general settings,
  • one for the feature's UI,
  • and another for the feature's data.

I'm trying to namespace as follows:

myApp
myApp.dashboard
myApp.dashboard.ui
myApp.dashboard.data

The problem is the namespace nesting because (for example) my dashboard.ui.js file is referenced in dashboard.js

I receive Uncaught TypeError: Cannot set property 'ui' of undefined when it tries to access the myApp.dashboard.ui.

I've tried every order of compilation I can think of. I'm guessing there's something wrong with my pattern but I'm not sure what. Here's an example of the code.

// app.js

let myApp = (function(){

    const $els = {
        menu: $('#menu')
        // etc.
    };

    const eventHandlers = () => {
        //site-wide click event handlers (for example)
    };

    const utils = {
        // utility functions
    };

    const init = () => {
        // set some site variables, etc.
        eventHandlers();
    };

    return {
        init,
        utils
    };
})(myApp || {});

//dashboard.js

myApp.dashboard = (function (parent){

    // ! need access to the UI
    let ui = parent.dashboard.ui;

    const eventHandlers = () => {
        ...
    };

    const init = () => {
        eventHandlers();
    };

    return {
        init
    };

})(myApp || {});

//dashboard.ui.js

myApp.dashboard.ui = (function (parent){

    // ! need access to the DATA
    let ui = parent.dashboard.data;

    const htmlTemplate = (content) => {
        return `<h1>${content}</h1>`;
    };

    const setValidationFlags = () => {
        ...
    };

    return {
        setValidationFlags
    };

})(myApp || {});

//dashboard.data.js

myApp.dashboard.data = (function (parent){

    const getData = (dataObject) => {
        // do XHR look up
        // call setter, etc.
    };

    const parseForm = () => {
        // collect form data.
    };

    return {
        parseForm
    };

})(myApp || {});

Can someone please provide some guidance on how to correct my code so that I can nest the UI and Data portions under the myApp.dashboard namespace and have the three accessible to one another?

If this pattern is way off base, pointers on how to improve it are also welcome.


Solution

  • dashboard requires ui requires data, so you need to load them in the reverse order. And have each module set up the whole namespace tree it needs when it is not yet available:

    // data.js
    var myApp;
    if (!myApp) myApp = {};
    if (!myApp.dashboard) myApp.dashboard = {};
    myApp.dashboard.data = (function(){
        …
        return { parseForm };
    })();
    
    // ui.js
    var myApp;
    if (!myApp) myApp = {};
    if (!myApp.dashboard) myApp.dashboard = {};
    myApp.dashboard.ui = (function(){
        // need access to the DATA
        const data = myApp.dashboard.data;
        …
        return {setValidationFlags};
    })();
    
    // dashboard.js
    var myApp;
    if (!myApp) myApp = {};
    if (!myApp.dashboard) myApp.dashboard = {};
    myApp.dashboard.init = (function(){
        // need access to the UI
        const ui = parent.dashboard.ui;
        …
        return () => {
            eventHandlers();
        };
    })();
    

    The alternative would be not to use references to the other modules inside the IIFE that is executed immediately, but to defer them until the init call which is when all modules should have been loaded.