Skip to main content

Recently I tried to customize mage.SwatchRenderer widget, and was unpleasantly surprised by the frustrating lack of documentation on this topic, and by some changes, mage by Magento2 developers in Magento_Swatches module .

I am a newbie in Magento2, and now, when I finally managed to make all needed customizations, I want to share my experience with other newbies.

I will not explain how to create module – there are plenty of articles about module creation, for example, this simple one.

I assume that reader already knows how to work with modules, but have trouble with extending widget functionality.

At first I read this good and simple Inchoo’s article, that explains how to extend menu widget functionality (you must read that article in order to understand what I will be talking about).
In theory, widget extending is a simple process. At first, you must tell Magento2 to use your custom script instead of the standard one. In above-mentioned Inchoo’s article, following statement tells Magento2 to use menu-custom.js whenever “menu” ID is used:

var config = {
    "map": {
        "*": {
            "menu": "js/menu-custom"
        }
    }
};

What is this, the script ID?

This is a sort of “alias” that is used to reference the script.

 

Why should we use script IDs?

All core modules define script IDs to reference their inner scripts. All templates (even those in core modules) should use script IDs to reference scripts. So, when some custom module redefines some script ID, it makes all Magento modules to use new (redefined) version of this script.

Let’s get back, to example with “menu” ID.
This ID firstly defined in vendor/magento/module-theme/view/frontend/requirejs-config.js

var config = {
    map: {
        "*": {
            "rowBuilder": "Magento_Theme/js/row-builder",
            "toggleAdvanced": "mage/toggle",
            "translateInline": "mage/translate-inline",
            "sticky": "mage/sticky",
            "tabs": "mage/tabs",
            "zoom": "mage/zoom",
            "collapsible": "mage/collapsible",
            "dropdownDialog": "mage/dropdown",
            "dropdown": "mage/dropdowns",
            "accordion": "mage/accordion",
            "loader": "mage/loader",
            "tooltip": "mage/tooltip",
            "deletableItem": "mage/deletable-item",
            "itemTable": "mage/item-table",
            "fieldsetControls": "mage/fieldset-controls",
            "fieldsetResetControl": "mage/fieldset-controls",
            "redirectUrl": "mage/redirect-url",
            "loaderAjax": "mage/loader",
            "menu": "mage/menu",
...

We can see, that “menu” ID points at the real file “mage/menu” (yes, Magento2 sees “mage/menu” as a real file vendor/magento/magento2-base/lib/web/mage/menu.js).

Inchoo’s author redefine “menu” ID, so now it points at the “js/menu-custom.js” file inside theme. In this file author extends mage.menu widget functionality, using widget factory:

define([
        'jquery',
        'jquery/ui',
        'mage/menu'
],
    function($){
        $.widget('inchoo.menu', $.mage.menu, {
            _init: function () {
                alert("I'm Inchoo");
            },
            toggle: function () {
                alert("I'm Inchoo");
            }
        });
        return $.inchoo.menu;
    });

In the “define” directive we again see ‘mage/menu’ reference, that leads to primordial vendor/magento/magento2-base/lib/web/mage/menu.js file. So, widget factory without difficulties extends primordial widget by replacing “init” method. This all work like a charm.

Now we have to extend the mage.SwatchRenderer widget. We will closely follow Inchoo’s article recommendations.
Let’s create the brand-new module Magenmagic_SwatchExtend (you can read about creating modules here )
Now, when our empty module is ready, we will try to extend mage.SwatchRenderer widget.

mage.SwatchRenderer widget is a part of the Magento_Swatches module. We can find this widget in the vendor/magento/module-swatches/view/frontend/web/js/swatch-renderer.js file. What id points to this file? Let’s look at the vendor/magento/module-swatches/view/frontend/requirejs-config.js file. Oops! This file is missing!

In previous versions of Magento (before the CE-2.1.0) this requirejs-config.js file looked like this:

var config = {
    map: {
        '*': {
            'SwatchRenderer': 'Magento_Swatches/js/swatch-renderer'
        }
    }
}

We can clearly see the “SwatchRenderer” ID, pointing at the real file ‘Magento_Swatches/js/swatch-renderer‘ (do not forget that Magento sees ‘Magento_Swatches/js/swatch-renderer‘ as a real file vendor/magento/module-swatches/view/frontend/web/js/swatch-renderer.js )

But now this requirejs-config.js file is missing, and now we can not redirect “SwatchRenderer” ID to our own version of the swatch-renderer.js.

But we can create this “SwatchRenderer” ID all of our own. May be this will help somehow?

Let’s create folder app/code/Magenmagic/SwatchExtend/view/frontend, and put there file “requirejs-config.js” with the following content:

var config = {
    map: {
        '*': {
            'SwatchRenderer': 'Magenmagic_SwatchExtend/js/swatch-renderer'
        }
    }
};

We defined “SwatchRenderer“ ID, telling Magento2, that script, referenced by this ID, resides in the “Magenmagic_SwatchExtend/js/swatch-renderer” file (Magento2 sees it as a real file app/code/Magenmagic/SwatchExtend/view/frontend/web/js/swatch-renderer.js)

Now let’s create app/code/Magenmagic/SwatchExtend/view/frontend/web/js/swatch-renderer.js file, and put there the following content:

define([
    'jquery',
    'jquery/ui',
    'Magento_Swatches/js/swatch-renderer'
], function($){
    $.widget('mage.SwatchRenderer', $.mage.SwatchRenderer, {
        _init: function () {
            console.log('Magenmagic SwatchRenderer: _init');
            if (this.options.jsonConfig !== '' && this.options.jsonSwatchConfig !== '') {
                this._sortAttributes();
                this._RenderControls();
            } else {
                console.log('SwatchRenderer: No input data received');
            }
        },
    });
    return $.mage.SwatchRenderer;
});

Everything is almost exactly like in the Inchoo’s article. We just extending mage.SwatchRenderer wiget, adding “console.log” statement to the _init method.

But, alas, this will not work!
Whatever you do, you will never see new message in the browser’s console.

Why?

Because Magento2 developers NEVER USE “SwatchRenderer” ID in their templates. If ID is not using anywhere, our script will not be executed, and widget mage.SwatchRenderer will not be extended.

But how Magento developers made standard script executed?
Let’s look at one of the templates from the Magento_Swatches module. In file vendor/magento/module-swatches/view/frontend/templates/product/listing/renderer.phtml we can see the following:

require([
    "jquery",
    "jquery/ui",
    "Magento_Swatches/js/swatch-renderer"], function ($) {

Developers chose not to use “SwatchRenderer” ID, but instead use a pointer to the real file “Magento_Swatches/js/swatch-renderer“.

To rectify this mistake, we must override all the templates where “Magento_Swatches/js/swatch-renderer” is used, and change all instances of “Magento_Swatches/js/swatch-renderer” to our ID “SwatchRenderer”.

Vladimir Repalo

Vladimir Repalo

Magento Developer at Mobecls, 8+ years of experience. Feel free to ask me anything about this post in the comments below.