5

enter image description here

How to Change the Configurable products name dynamically when i click configurable swatches, currently price change when click swatches .I need to change name too. please suggest better way! Thank You,

1
  • Add attribute of the name, add a name in that attribute and in the product page change the product name with Ajax on click event. Jul 6, 2019 at 7:01

4 Answers 4

8

You must create a module that will override the "ConfigurableProduct" and "Swatches" modules of Magento.

I made you something based based on a solution from an author of this forum (As soon as I find his name, I will tag him) that will dynamically change the product name, Sku and description. You can make anything you want dynamic with this module. :

First, create your base file in : /app/code/VendorName/ModuleName

In each of the indications below, VendorName and Module/name are to be modified by what you want.

registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'VendorName_ModuleName',
    __DIR__
);

Create directory /app/code/VendorName/ModuleName/etc

Put this file in it : 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="VendorName_ModuleName" setup_version="1.0.0">
        <sequence>
            <module name="Magento_ConfigurableProduct"/>
            <module name="Magento_Swatches"/>
        </sequence>
    </module>
</config>

Now create directory /app/code/VendorName/ModuleName/etc/frontend/

Put this file in it : di.xml

<?xml version="1.0" ?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\ConfigurableProduct\Block\Product\View\Type\Configurable">
        <plugin disabled="false" name="VendorName_ModuleName_Plugin_Magento_ConfigurableProduct_Block_Product_View_Type_Configurable" sortOrder="10" type="VendorName\ModuleName\Plugin\Magento\ConfigurableProduct\Block\Product\View\Type\Configurable"/>
    </type>
    <type name="Magento\Swatches\Block\Product\Renderer\Configurable">
        <plugin disabled="false" name="VendorName_ModuleName_Plugin_Magento_Swatches_Block_Product_Renderer_Configurable" sortOrder="10" type="VendorName\ModuleName\Plugin\Magento\Swatches\Block\Product\Renderer\Configurable"/>
    </type>
</config>

Create directories /app/code/VendorName/ModuleName/Plugin/Magento/ConfigurableProduct/Block/Product/View/Type/ /app/code/VendorName/ModuleName/Plugin/Magento/Swatches/Block/Product/Renderer/

In /app/code/VendorName/ModuleName/Plugin/Magento/ConfigurableProduct/Block/Product/View/Type/

Put this file : Configurable.php

<?php
namespace VendorName\ModuleName\Plugin\Magento\ConfigurableProduct\Block\Product\View\Type;

class Configurable
{
    public function afterGetJsonConfig(\Magento\ConfigurableProduct\Block\Product\View\Type\Configurable $subject, $result) {
        $jsonResult = json_decode($result, true);
        foreach ($subject->getAllowProducts() as $simpleProduct) {
            $id = $simpleProduct->getId();
            foreach($simpleProduct->getAttributes() as $attribute) {
                if(($attribute->getIsVisible() && $attribute->getIsVisibleOnFront()) || in_array($attribute->getAttributeCode(), ['sku','description','name']) ) { // <= Here you can put any attribute you want to see dynamic
                    $code = $attribute->getAttributeCode();
                    $value = (string)$attribute->getFrontend()->getValue($simpleProduct);
                    $jsonResult['dynamic'][$code][$id] = [
                        'value' => $value
                    ];
                }
            }
        }
        $result = json_encode($jsonResult);
        return $result;
    }
}

In /app/code/VendorName/ModuleName/Plugin/Magento/Swatches/Block/Product/Renderer/

<?php

namespace VendorName\ModuleName\Plugin\Magento\Swatches\Block\Product\Renderer;
class Configurable
{
    public function afterGetJsonConfig(\Magento\Swatches\Block\Product\Renderer\Configurable $subject, $result) {
        $jsonResult = json_decode($result, true);
        foreach ($subject->getAllowProducts() as $simpleProduct) {
            $id = $simpleProduct->getId();
            foreach($simpleProduct->getAttributes() as $attribute) {
                if(($attribute->getIsVisible() && $attribute->getIsVisibleOnFront()) || in_array($attribute->getAttributeCode(), ['sku','description','name']) ) { // <= Here you can put any attribute you want to see dynamic
                    $code = $attribute->getAttributeCode();
                    $value = (string)$attribute->getFrontend()->getValue($simpleProduct);
                    $jsonResult['dynamic'][$code][$id] = [
                        'value' => $value
                    ];
                }
            }
        }
        $result = json_encode($jsonResult);
        return $result;
    }
}

Now, we enter in the LAST STEP :

Create Directories : /app/code/VendorName/ModuleName/view/frontend/web/js/model/

In this folder "model", add these files:

attswitch.js

define([
    'jquery',
    'mage/utils/wrapper'
], function ($, wrapper) {
    'use strict';
    return function(targetModule){

        $('h1 span').attr("data-dynamic", "name");

        var reloadPrice = targetModule.prototype._reloadPrice;
        targetModule.prototype.dynamic = {};

        $('[data-dynamic]').each(function(){
            var code = $(this).data('dynamic');
            var value = $(this).html();

            targetModule.prototype.dynamic[code] = value;
        });

        var reloadPriceWrapper = wrapper.wrap(reloadPrice, function(original){
            var dynamic = this.options.spConfig.dynamic;
            console.log(dynamic);
            for (var code in dynamic){
                if (dynamic.hasOwnProperty(code)) {
                    var value = "";
                    var $placeholder = $('[data-dynamic='+code+']');

                    if(!$placeholder.length) {
                        continue;
                    }

                    if(this.simpleProduct){
                        value = this.options.spConfig.dynamic[code][this.simpleProduct].value;
                    } else {
                        value = this.dynamic[code];
                    }

                    $placeholder.html(value);
                }
            }

            return original();
        });

        targetModule.prototype._reloadPrice = reloadPriceWrapper;
        return targetModule;
    };
});

swatch-attswitch.js

/*jshint browser:true jquery:true*/
/*global alert*/
define([
    'jquery',
    'mage/utils/wrapper'
], function ($, wrapper) {
    'use strict';

    return function(targetModule){

        $('h1 span').attr("data-dynamic", "name");

        var updatePrice = targetModule.prototype._UpdatePrice;
        targetModule.prototype.dynamic = {};

        $('[data-dynamic]').each(function(){
            var code = $(this).data('dynamic');
            var value = $(this).html();

            targetModule.prototype.dynamic[code] = value;
        });

        var updatePriceWrapper = wrapper.wrap(updatePrice, function(original){
            var dynamic = this.options.jsonConfig.dynamic;
            console.log(dynamic);
            for (var code in dynamic){
                if (dynamic.hasOwnProperty(code)) {

                    var value = "";
                    var $placeholder = $('[data-dynamic='+code+']');
                    var allSelected = true;

                    if(!$placeholder.length) {
                        continue;
                    }

                    for(var i = 0; i<this.options.jsonConfig.attributes.length;i++){
                        if (!$('div.product-info-main .product-options-wrapper .swatch-attribute.' + this.options.jsonConfig.attributes[i].code).attr('option-selected')){
                            allSelected = false;
                        }
                    }

                    if(allSelected){
                        var products = this._CalcProducts();
                        value = this.options.jsonConfig.dynamic[code][products.slice().shift()].value;
                    } else {
                        value = this.dynamic[code];
                    }

                    $placeholder.html(value);
                }
            }

            return original();
        });

        targetModule.prototype._UpdatePrice = updatePriceWrapper;
        return targetModule;

    };
});

And finally in /app/code/VendorName/ModuleName/view/frontend/

Add this file : requirejs-config.js

var config = {
    config: {
        mixins: {
            'Magento_ConfigurableProduct/js/configurable': {
                'VendorName_ModuleName/js/model/attswitch': true
            },
            'Magento_Swatches/js/swatch-renderer': {
                'VendorName_ModuleName/js/model/swatch-attswitch': true
            }
        }
    }
};

You have now a fully functionnal module that will change dynamically all information you want on product page.

Just pass habitual CLI commands and well done, it work.

Regards,

11
  • Hi, @Jimmy Labuche Thank you for this detailed solution Jul 6, 2019 at 10:48
  • With pleasure! ! Feel free to pass your subject to solved if it is :) Jul 6, 2019 at 10:52
  • But an issue in related product case, if any products have related product at that case last related product name comes in the actual product name section ! first load. when swatches click its clear, but initial loading show wrong name .how to solve this Bug? Jul 6, 2019 at 11:08
  • I checked, I cannot reproduce your problem. Can you tell me more about it ? Have you a link ? Jul 6, 2019 at 11:13
  • please check updated question :) Thank you Jul 6, 2019 at 11:20
8

Module Method Working For me in Magento 2.1 / 2.2 /2.3 :: The below Code is to change Name & Description both. For Magento 2.4 add mixin


1. app/code/[VendorName]/[ModuleName]/registration.php

<?php
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    '[VendorName_ModuleName]',
    __DIR__
);

2. app/code/[VendorName]/[ModuleName]/etc/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="[VendorName_ModuleName]" setup_version="1.0.0" />
</config>

3. app/code/[VendorName]/[ModuleName]/etc/di.xml

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\ConfigurableProduct\Block\Product\View\Type\Configurable">
        <plugin name="[VendorName_ModuleName]-product-block" type="[VendorName]\[ModuleName]\Plugin\Product\View\Type\ConfigurablePlugin" sortOrder="1" />
    </type>
    <type name="Magento\ConfigurableProduct\Model\Product\Type\Configurable">
        <plugin name="[VendorName_ModuleName]-product-model" type="[VendorName]\[ModuleName]\Plugin\Product\Type\ConfigurablePlugin" sortOrder="1" />
    </type>
</config>

4. app/code/[VendorName]/[ModuleName]/Plugin/Product/Type/ConfigurablePlugin.php

<?php 
namespace [VendorName]\[ModuleName]\Plugin\Product\Type;

    class ConfigurablePlugin
    {
        public function afterGetUsedProductCollection(\Magento\ConfigurableProduct\Model\Product\Type\Configurable $subject, $result)
        {
            $result->addAttributeToSelect('name');
            $result->addAttributeToSelect('description');
            return $result;
        }
    }

5. app/code/[VendorName]/[ModuleName]/Plugin/Product/View/Type/ConfigurablePlugin.php

<?php 

namespace [VendorName]\[ModuleName]\Plugin\Product\View\Type;

class ConfigurablePlugin
{
    protected $jsonEncoder;
    protected $jsonDecoder;

    public function __construct(
        \Magento\Framework\Json\DecoderInterface $jsonDecoder,
        \Magento\Framework\Json\EncoderInterface $jsonEncoder
    ){
        $this->jsonEncoder = $jsonEncoder;
        $this->jsonDecoder = $jsonDecoder;
    }

    public function afterGetJsonConfig(\Magento\ConfigurableProduct\Block\Product\View\Type\Configurable $subject, $result)
    {
        $result = $this->jsonDecoder->decode($result);
        $currentProduct = $subject->getProduct();
        
        if ($currentProduct->getName()) {
            $result['productName'] = $currentProduct->getName();
        }
        if ($currentProduct->getDescription()) {
            $result['productDescription'] = $currentProduct->getDescription();
        }

        foreach ($subject->getAllowProducts() as $product) {
            $result['names'][$product->getId()][] =
                [
                    'name' => $product->getData('name'),
                ];
            $result['descriptions'][$product->getId()][] =
                [
                    'description' => $product->getData('description'),
                ];
        }
        return $this->jsonEncoder->encode($result);
    }
}

6. app/code/[VendorName]/[ModuleName]/view/frontend/requirejs-config.js

var config = {
    map: {
        '*': {
            'Magento_Swatches/js/swatch-renderer':'[VendorName]_[ModuleName]/js/swatch-renderer'
        }
    }
};

7. app/code/[VendorName]/[ModuleName]/view/frontend/web/js/swatch-renderer.js

Copy original JS file(vendor/magento/module-swatches/view/frontend/web/js/swatch-renderer.js & Replace _OnClick function with below code (Line No Approx: 712)

_OnClick: function ($this, $widget, eventName) {
            var $parent = $this.parents('.' + $widget.options.classes.attributeClass),
                $wrapper = $this.parents('.' + $widget.options.classes.attributeOptionsWrapper),
                $label = $parent.find('.' + $widget.options.classes.attributeSelectedOptionLabelClass),
                attributeId = $parent.attr('attribute-id'),
                $input = $parent.find('.' + $widget.options.classes.attributeInput);

            if ($widget.inProductList) {
                $input = $widget.productForm.find(
                    '.' + $widget.options.classes.attributeInput + '[name="super_attribute[' + attributeId + ']"]'
                );
            }

            if ($this.hasClass('disabled')) {
                return;
            }

            if ($this.hasClass('selected')) {
                $parent.removeAttr('option-selected').find('.selected').removeClass('selected');
                $input.val('');
                $label.text('');
                $this.attr('aria-checked', false);
            } else {
                $parent.attr('option-selected', $this.attr('option-id')).find('.selected').removeClass('selected');
                $label.text($this.attr('option-label'));
                $input.val($this.attr('option-id'));
                $input.attr('data-attr-name', this._getAttributeCodeById(attributeId));
                $this.addClass('selected');
                $widget._toggleCheckedAttributes($this, $wrapper);

                /* CUSTOM CODE START */
                if (jQuery('[data-ui-id="page-title-wrapper"]').length && this.options.jsonConfig.names) {
                    if (this.getProduct()) {
                        var iname = this.options.jsonConfig.names[this.getProduct()][0].name
                        if (iname != '') {
                            jQuery('[data-ui-id="page-title-wrapper"]').html(iname);
                        }
                    }
                    else {
                        var productName = this.options.jsonConfig.productName
                        if (productName) {
                            jQuery('[data-ui-id="page-title-wrapper"]').html(productName);
                        }
                    }
                }
                if (jQuery('.description > div.value').length && this.options.jsonConfig.descriptions) {
                    if (this.getProduct()) {
                        var description = this.options.jsonConfig.descriptions[this.getProduct()][0].description
                        if (description) {
                            jQuery('.description > div.value').html(description);
                        }
                    } else {
                        var productDescription = this.options.jsonConfig.productDescription
                        if (productDescription) {
                            jQuery('.description > div.value').html(productDescription);
                        }
                    }
                }
                /* CUSTOM CODE END */

                $widget._Rebuild();

                if ($widget.element.parents($widget.options.selectorProduct)
                    .find(this.options.selectorProductPrice).is(':data(mage-priceBox)')
                ) {
                    $widget._UpdatePrice();
                }

                $widget._loadMedia(eventName);
                $input.trigger('change');
            }
        },

For Magento 2.4

For mixins you have to Add or Update app/code/[VendorName]/[ModuleName]/view/frontend/requirejs-config.js

var config = {
    config: {
        mixins: {
            "Magento_Swatches/js/swatch-renderer" : {
                "[VendorName]_[ModuleName]/js/swatch-renderer": true
            }
        }
    }
};

For mixins you have to Add or Update app/code/[VendorName]/[ModuleName]/view/frontend/web/js/swatch-renderer.js

define([
    'jquery',
    'underscore',
    'jquery/ui',
    'jquery/jquery.parsequery'
], function ($, _) {
    'use strict';
    return function (widget) {

        $.widget('mage.SwatchRenderer', widget, {

            /**
             * Event for swatch options
             *
             * @param {Object} $this
             * @param {Object} $widget
             * @private
             */
            _OnClick: function ($this, $widget) {

                $widget._super($this, $widget);

                $widget._UpdateDetailProduct();

            },

            /**
             * Event for select
             *
             * @param {Object} $this
             * @param {Object} $widget
             * @private
             */
            _OnChange: function ($this, $widget) {

                $widget._super($this, $widget);

                $widget._UpdateDetailProduct();

            },

            _UpdateDetailProduct: function () {
                var $widget = this,
                    index = '',
                    options = _.object(_.keys($widget.optionsMap), {}),
                    childProductName = this.options.jsonConfig.names,
                    childProductDescription = this.options.jsonConfig.descriptions;

                $widget.element.find('.' + $widget.options.classes.attributeClass + '[data-option-selected]').each(function () {
                    var attributeId = $(this).attr('data-attribute-id');
                    options[attributeId] = $(this).attr('data-option-selected');
                });

                /* CUSTOM CODE START */
                if (jQuery('[data-ui-id="page-title-wrapper"]').length && this.options.jsonConfig.names) {
                    //console.log("In "names);
                    console.log(this.getProduct());
                    if (this.getProduct()) {
                        var iname = this.options.jsonConfig.names[this.getProduct()][0].name
                        if (iname != '') {
                            jQuery('[data-ui-id="page-title-wrapper"]').html(iname);
                        }
                    }
                    else {
                        var productName = this.options.jsonConfig.productName
                        if (productName) {
                            jQuery('[data-ui-id="page-title-wrapper"]').html(productName);
                        }
                    }
                }
                if (jQuery('.description > div.value').length && this.options.jsonConfig.descriptions) {
                    if (this.getProduct()) {
                        var description = this.options.jsonConfig.descriptions[this.getProduct()][0].description
                        if (description) {
                            jQuery('.description > div.value').html(description);
                        }
                    } else {
                        var productDescription = this.options.jsonConfig.productDescription
                        if (productDescription) {
                            jQuery('.description > div.value').html(productDescription);
                        }
                    }
                }
                /* CUSTOM CODE END */
            },
        });
        return $.mage.SwatchRenderer;
    }
});

** Output::** enter image description here

8
  • Can you change the SKU also?
    – paj
    Nov 8, 2019 at 12:03
  • @paj Yes, even you can do this also using the above code. change in 5. app/code/[VendorName]/[ModuleName]/Plugin/Product/View/Type/ConfigurablePlugin.php & 7. app/code/[VendorName]/[ModuleName]/view/frontend/web/js/swatch-renderer.js, else remain same. Nov 8, 2019 at 12:15
  • ok, but overriding swatch-renderer.js can cause problems with other image modules. So perhaps first answer is a better method.
    – paj
    Nov 8, 2019 at 15:59
  • @Rahul I have one small query i.e for parent level product doesn't have a description than can we display the simple product description at the description tab? Dec 26, 2019 at 8:52
  • Hi, I tried it on magento 2.3.3 and not work.. I would need this small extension for other attributes too. Can I get help for this?
    – Joso
    Dec 29, 2019 at 15:49
0

Out of the box, when choosing options, the images and the price will change. But not product name.

More information about which elements on the page change when options are selected can be found here: Magento\ConfigurableProduct\Block\Product\View\Type\Configurable::getJsonConfig().

In order to display the Product name of the selected product, you can create a Plugin (Interceptor) for Magento\ConfigurableProduct\Block\Product\View\Type\Configurable::getJsonConfig() where you need to add missing Product name for each simple product.

Switching Product name when selecting an option can be implemented in vendor/magento/module-swatches/view/base/web/js/swatch-renderer.js and vendor/magento/module-configurable-product/view/frontend/web/js/configurable.js depending on the type of attribute.


I made VCT Product Info Switcher module on Magento Marketplace that solves this problem.

0

For shops, who are using a Hyvä Theme, our module might be an option: https://commercemarketplace.adobe.com/customgento-module-child-product-data.html This updates not only the name, but also the following attributes quick and easy:

  • SKU
  • Short Description
  • Description
  • “More Information” Block
  • Related Products (performantly via AJAX)
  • Upsell Products (performantly via AJAX)

You only need to enter the fitting identifiers of your Hyvä theme in the system configuration, and the module will do the rest.

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.