Patterns for ES6 JavaScript Modules - Part 2
If you have read Part 1 of this series on ES6 module patterns you will recognize this next pattern as a variation of the first pattern, the simple export. In this pattern, instead of exporting a single structure that holds whatever we want returned by the import statement, we export a single anonymous function. In this function, we group together whatever code we want to execute from the module. For example, I like to use this pattern when writing applications for the altseven JavaScript framework. Specifically, I use this pattern in conjunction with the publish/subscribe event system altseven employs to pass event notifications anywhere in the application.
If you are familiar with pub/sub systems, the altseven system will not seem unusual. You simply subscribe to an event, with corresponding code for what should happen when that event fires, then publish the event where it needs to fire. Simple, but in anything more than a trivial application, you might be quickly overwhelmed by the number of subscribed events. While you can subscribe to an event anywhere in the application, I use a centralized ./events folder to hold a set of JavaScript files that organizes events logically by section of the application. For instance, in the LacesIDE in browser editor, there is an auth.js file that contains all the subscriptions to the auth events in the application.
The code is structurally like below. I have included one of the pub/sub events for clarity, you can see all of them in the source code on GitHub.
import {a7} from '/lib/altseven/dist/a7.js';
import {ui} from '/js/app.ui.js';
import {auth} from '/js/app.auth.js';
export var authEvents = function (){
a7.events.subscribe( "auth.failed", function( obj ){
let lf = a7.ui.getView( "loginForm" );
let message = `Login failed. <a name="forgot" data-onclick="forgotPassword"> Forgot your password?</a>`;
lf.setState( Object.assign( lf.getState(), { message: message } ) );
a7.router.open( "/auth/showlogin" );
});
};
So what exactly is happening here?
In short, when this module is imported into an application, nothing happens at first, not until you run the imported function:
import {authEvents} from '/events/auth.js';
authEvents();
When you run this function, all of the code in the module function is executed. In the case of these event modules, all of the event subscriptions in these modules are created and will respond to the events being fired in the application. I import and run these functions in the entry point of the application so they are present from the start of the application. Structurally, I can keep all of the subscriptions organized and easy to follow. It is always tempting to subscribe to an event inline in a view component that might need to do something based on that event firing, but I don’t like to litter the code with event subscriptions because it becomes difficult to track where things are when you need to debug an application of any significant size. Debugging SPAs is already complex enough without adding this wrinkle to the mix.
In Part 3 of this series I will show you a pattern that combines the previous patterns and creates a variation that offers some useful features not in these first two patterns.