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.