10

Precondition

I have 2 custom widgets extending same parent widget.

  • Parent widget: Magento_ConfigurableProduct/js/configurable
  • First Custom Widget: Vendor_AModule/js/configurable
  • Second Custom Widget: Vendor_BModule/js/configurable

First Custom Widget require-config.js:

var config = {
    map: {
        '*': {
            configurable: 'Vendor_AModule/js/configurable'
        }
    }
};

First Custom Widget JS:

define([
    'jquery',
    'mage/translate',
    'Magento_ConfigurableProduct/js/configurable'
], function ($) {
    $.widget('vendor.configurable_awidget', $.mage.configurable, {
        /**
         * {@inheritDoc}
         */
        _configureElement: function (element) {
            this._super(element);
            alert('Custom widget A is triggered!');
        }
    });

    return $.vendor.configurable_awidget;
});

Second Custom Widget require-config.js:

var config = {
    map: {
        '*': {
            configurable: 'Vendor_BModule/js/configurable'
        }
    }
};

Second Custom Widget JS:

define([
    'jquery',
    'mage/translate',
    'Magento_ConfigurableProduct/js/configurable'
], function ($) {
    $.widget('vendor.configurable_bwidget', $.mage.configurable, {
        /**
         * {@inheritDoc}
         */
        _configureElement: function (element) {
            this._super(element);
            alert('Custom widget B is triggered!');
        }
    });

    return $.vendor.configurable_bwidget;
});

Steps to reproduce

I open a configurable product frontend page.

Expected Result

I see both Custom widget B is triggered! and Custom widget A is triggered! alert.

Actual Result

I only see Custom widget B is triggered! alert.

Question

How should the code be to make the configurable product frontend page displays alerts of both widgets?

2 Answers 2

14

Magento 2 has a lesser known feature called a require-js mixin that's useful for extending a js module from multiple places.

Your requirejs-config.js should look like:

var config = {
    'config': {
        'mixins': {
            'Magento_ConfigurableProduct/js/configurable': {
                'Vendor_AModule/js/configurable': true
            }
        }
    }
};

The js file would then be:

define([
    'jquery',
    'mage/translate'
], function ($) {

    return function (widget) {
        $.widget('vendor.configurable_awidget', widget, {
            /**
             * {@inheritDoc}
             */
            _configureElement: function (element) {
                this._super(element);
                alert('Custom widget A is triggered!');
            }
        });
        return $.vendor.configurable_awidget;
    };
});

This js returns a function which takes the target module as an argument and returns the extended version. This way you can extend the widget in different places without undesired overriding.

6
  • Great! Helpful info. Thx. I forgot mixin Commented Mar 2, 2017 at 3:43
  • I can only see AWidget in your code, how to apply BWidget? Commented Mar 2, 2017 at 3:52
  • 1
    BWidget would be implemented the same as AWidget. Commented Mar 2, 2017 at 4:19
  • Thank you sir, I have implemented your code and it works like what it should do. Commented Mar 2, 2017 at 4:41
  • @AaronAllen, +1 Nice info. Commented Mar 2, 2017 at 5:36
3

Make sure the custom Module loaded after others

<sequence> tag to ensure that needed files from other components are already loaded when your component loads

module.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
    <module name="Vendor_BModule" setup_version="1.0.1">
        <sequence>
            <module name="Vendor_AModule"/>
        </sequence>
    </module>
</config>

We can check in app/etc/config.php. Your custom module should be "lower level" than others.

<?php
return array (
  'modules' => 
  array (
    ......
    'Magento_ConfigurableProduct' => 1,
    ......
    'Vendor_AModule' => 1,
    ......
    'Vendor_BModule' => 1,
    ......
  ),
);

We can remove the custom module from setup_module table. And then, run setup upgrade command again to re-order the module sequence.

We need to make sure the custom js is "lower level" than others in requirejs-config.js.

pub/static/_requirejs/frontend/Magento/luma/en_US/requirejs-config.js

(function(require){

    ......

    (function() {

        var config = {
            map: {
                '*': {
                    configurable: 'Magento_ConfigurableProduct/js/configurable'
                }
            }
        };
        require.config(config);
    })();

    ......



    (function() {
        var config = {
            map: {
                '*': {
                    configurable: 'Vendor_AModule/js/configurable'
                }
            }
        };
        require.config(config);
    })();

    .....

    (function() {
        var config = {
            map: {
                '*': {
                    configurable : 'Vendor_BModule/js/configurable'
                }
            }
        };
        require.config(config);
    })();

    ......

})(require);

Declare the Module B

Because the A widget was "overridden" the default Magento widget already. So, in the Module B, we need to load the A widget and "override" it.

app/code/Vendor/BModule/view/frontend/requirejs-config.js

var config = {
    map: {
        '*': {
            configurable : 'Vendor_BModule/js/configurable'
        }
    }
};

app/code/Vendor/BModule/view/frontend/web/js/configurable.js

define([
    'jquery',
    'mage/translate',
    'Vendor_AModule/js/configurable' // Module A widget
], function ($) {
    $.widget('vendor.configurable_bwidget', $.vendor.configurable_awidget, {
        /**
         * {@inheritDoc}
         */
        _configureElement: function (element) {
            this._super(element);
            alert('Custom widget B is triggered!');
        }
    });

    return $.vendor.configurable_bwidget;
});

After all, we need to run static content deploy again.

We can read more here: https://learn.jquery.com/jquery-ui/widget-factory/extending-widgets/#using-_super-and-_superapply-to-access-parents

1
  • 1
    Thanks for your answer. I have implemented this method someday ago, and yes, it worked. But then, I find myself in a problem where AModule must be independent of BModule, so that when I disable AModule, BModule should still work, vice-versa. Here is where your answer unfortunately can't handle this problem. Commented Mar 1, 2017 at 10:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.