Categories


Archives


Recent Posts


Categories


Using Non-Function Objects with x-magento-init Scripts

astorm

Frustrated by Magento? Then you’ll love Commerce Bug, the must have debugging extension for anyone using Magento. Whether you’re just starting out or you’re a seasoned pro, Commerce Bug will save you and your team hours everyday. Grab a copy and start working with Magento instead of against it.

Updated for Magento 2! No Frills Magento Layout is the only Magento front end book you'll ever need. Get your copy today!

So, over the summer I covered how to invoke a function returning RequireJS modules via an x-magento-init script. Today I discovered there’s a syntax for doing this with an object returning RequireJS module. If you’ve got an x-magento-init that looks like this

<script type="text/x-magento-init">
    {
        "*": {
            "Package_Namespace/js/example": {/* ... config object ... */}
        }
    }            
</script>    

just define a module that looks like this

#File: app/code/Package/Namespace/view/web/frontend/js/example.js

define([], function(){
    return {
        /* ... other methods and properties */,            
        'Package_Namespace/js/example':function(configObject){
            //magento will call this function
        }
    }
});

That is – if your RequireJS module returns a non-function, and that non-function is an object with a key whose name matches the module name, Magento will treat the function in that key as the x-magento-init function.

Why

You’d do this if you wanted to create a module that takes in some server side rendered data, but that other modules would also later include to access functionality. You can see this in the Magento_Customer/js/customer-data module. Here’s the x-magento-init function

#File: vendor/magento/module-customer/view/frontend/web/js/customer-data.js
/*...*/
'Magento_Customer/js/customer-data': function (settings) {
    options = settings;
    invalidateCacheBySessionTimeOut(settings);
    invalidateCacheByCloseCookieSession();
    customerData.init();
}
/*...*/

All pages in Magento’s front end add the following x-magento-init

<script type="text/x-magento-init">
    {
        "*": {
            "Magento_Customer/js/customer-data": {
                "sectionLoadUrl": "http://magento-2-1-3.dev/customer/section/load/",
                "cookieLifeTime": "3600",
                "updateSessionUrl": "http://magento-2-1-3.dev/customer/account/updateSession/"
            }
        }
    }            
</script>    

When Magento parses the x-magento-init scripts, it will call the 'Magento_Customer/js/customer-data': function (settings) { function, which sets a bunch of state in the module. However, Magento also uses the Magento_Customer/js/customer-data module all over the place

$ find vendor/magento/ -name '*.js' | xargs ack 'Magento_Customer/js/customer-data'
vendor/magento/module-catalog/view/frontend/web/js/view/compare-products.js
7:    'Magento_Customer/js/customer-data'

vendor/magento/module-checkout/view/frontend/web/js/checkout-data.js
12:    'Magento_Customer/js/customer-data'

vendor/magento/module-checkout/view/frontend/web/js/model/address-converter.js
9:        'Magento_Customer/js/customer-data',

vendor/magento/module-checkout/view/frontend/web/js/proceed-to-checkout.js
9:        'Magento_Customer/js/customer-data'

vendor/magento/module-checkout/view/frontend/web/js/sidebar.js
10:    'Magento_Customer/js/customer-data',

//...

Assuming these invocations happen after the initial x-magento-init parsing, they’ll have access to values set in the 'Magento_Customer/js/customer-data': function (settings) { function.

Where x-magento-init Parsing Happens

For the really curious, the “call the x-magento-init function” part of the parsing happens here

#File: lib/web/mage/apply/main.js
function init(el, config, component) {
    require([component], function (fn) {

        //this block calls the self-named object 
        //property we've been talking about
        if (typeof fn === 'object') {
            fn = fn[component].bind(fn);
        }

        if (_.isFunction(fn)) {
            //this happens if our module return a function object
            fn(config, el);
        } else if ($(el)[component]) {
            //and this seems to indicate its possible to 
            //attach a function to a dom element for the
            //`data-mage-init` and/or non-* `x-magento-init`s
            $(el)[component](config);
        }
    });
}    

This code gets called by one of the RequireJS deps modules – i.e., Magento calls it on every page load. Useful code point if you’re wondering why you’re x-magento-init scripts never get called.

Copyright © Alan Storm 1975 – 2019 All Rights Reserved

Originally Posted: 4th February 2017