Dmytro Vyprichenko
Dmytro Vyprichenko

Reputation: 4924

Webpack dependency from a module which waits for DOM loading

Is there a way to delay resolving dependencies from a module while it waits for some events?

For instance, there is a module globals.js which takes application user ID from the data-attribute, creates User (from User.js) object and stores it in window to be widely used across application.

globals.js:

define('globals', ['User'], function (User) {
    document.addEventListener('DOMContentLoaded', function () {
        var id = document.body.getAttribute('data-user-id')
        window.appUser = new User(id);
    });
});

User.js (just to illustrate that I want the code in globals.js to be web-packed because it has dependencies):

define('User', function () {
    return class User {
        constructor(id) {
            this.id = parseInt(id);
        }
    }
});

And app.js requires globals.js:

require(['globals'], function () {
    //window.appUser will throw ReferenceError: appUser is not defined
    //Add DOMContentLoaded listener here again? :(
});

It would be nice to avoid declaring DOMContentLoaded event listener in each module which is dependent on globals.js How can I manage this?

Upvotes: 1

Views: 473

Answers (1)

jAndy
jAndy

Reputation: 235962

You cannot really solve that "problem" with Webpack. It's your architecture which has the real problem here. Webpack correctly calls your callback method when your globals module was transfered and evaluated, but at that point, it cannot know that the module actually waits for an asynchronous event (DOMContentLoaded).

If you ask me, this is a prime example for a Promise object. Create a Promise in your globals module and return that Promise immediately. You then can grab that returned Promise in any module you're including globals and just put anything you want into the .then() handlers.

define('globals', ['User'], function (User) {
    return new Promise(function( res, rej ) {
        document.addEventListener('DOMContentLoaded', function () {
            var id = document.body.getAttribute('data-user-id')
            //window.appUser = new User(id);
            res( new User( id ) );
        });
    });
});

...and then go like

require(['globals'], function ( getUser ) {
    getUser.then(function( user ) {
        // do whatever with user object
    });
});

I'm not too familiar with AMD module syntax, actually using CommonJS and/or ES7 modules, but this should do the trick. It is in general a bad choice to clobber the global object with data for various reasons, so we can kill two problems at once here.

Caution: Your architecture is flawed in more ways here. We're just assuming that the DOMContentLoaded even has not fired because you're including the globals module. It would be wise to check for document.readyState within globals and either resolve the Promise directly or listen for the DCL event if necessary.

Upvotes: 2

Related Questions