Multiple configurable products with options on one page in Magento

Featured Image

Displaying configurable products with options on one page can be problem because Javascript in Magento is setup to work only with one configurable product.
You can write your own Javascript or U can reuse Magento code.

To accomplish that we need to modify product Javascript (js/varien/product.js) and configurable (catalog\product\view\type\options\configurable.phtml) select element template.

In configurable.phtml call our new JS class (we will create that class later in article) and add product id to configurable select element class.

configurable.phtml:

<?php
$_product    = $this->getProduct();
$_attributes = Mage::helper('core')->decorateArray($this->getAllowAttributes());
?>
<?php if ($_product->isSaleable() && count($_attributes)):?>
    <dl>
    <?php foreach($_attributes as $_attribute): ?>
        <dt><label class="required"><em>*</em><?php echo $_attribute->getLabel() ?></label></dt>
        <dd<?php if ($_attribute->decoratedIsLast){?> class="last"<?php }?>>
            <div class="input-box">
                <select name="super_attribute[<?php echo $_attribute->getAttributeId() ?>]" id="attribute<?php echo $_attribute->getAttributeId() ?>" class="required-entry super-attribute-select_<?php echo $_product->getId()?>  ">
                    <option><?php echo $this->__('Choose an Option...') ?></option>
                  </select>
              </div>
        </dd>
    <?php endforeach; ?>
    </dl>
    <script type="text/javascript">
        var spConfig_<?php echo $_product->getId()?> = new Inchoo_Product.Config(<?php echo $this->getJsonConfig() ?>);
    </script>
<?php endif;?>
 
 

Then create Javascript file and Javascript class.
Copy javascript code from js/varien/product.js for configurable product (use own namespace to avoid JS collision with Magento).

if(typeof Inchoo_Product =='undefined') {
    var Inchoo_Product  = {};
}
 
/**************************** CONFIGURABLE PRODUCT **************************/
Inchoo_Product.Config = Class.create();
Inchoo_Product.Config.prototype = {

Because we change selector name we have to modify our JS code to work with new selector.

var settingsClassToSelect = '.super-attribute-select_'+this.config.productId;
this.settings   = $$(settingsClassToSelect);

Magento code is stripping “attribute” string from class of configurable select element but we also have to strip product id (we added product id to class name in configurable.phtml).

        // fill state
        this.settings.each(function(element){
            var attributeId = element.id.replace(/[a-z]*/, '');
            attributeId = attributeId.replace(/_.*/, '');
 
            ........
 
        fillSelect: function(element){
        var attributeId = element.id.replace(/[a-z]*/, '');
        attributeId = attributeId.replace(/_.*/, '');

Now we can have multiple configurable products with options on same page.

Update, as requested complete JS file

product.js:

 
if(typeof Inchoo_Product =='undefined') {
    var Inchoo_Product  = {};
}
 
/**************************** CONFIGURABLE PRODUCT **************************/
Inchoo_Product.Config = Class.create();
Inchoo_Product.Config.prototype = {
    initialize: function(config){
        this.config     = config;
        this.taxConfig  = this.config.taxConfig;
        var settingsClassToSelect = '.super-attribute-select_'+this.config.productId;
        this.settings   = $$(settingsClassToSelect);
        this.state      = new Hash();
        this.priceTemplate = new Template(this.config.template);
        this.prices     = config.prices;
 
        this.settings.each(function(element){
            Event.observe(element, 'change', this.configure.bind(this))
        }.bind(this));
 
        // fill state
        this.settings.each(function(element){
            var attributeId = element.id.replace(/[a-z]*/, '');  
            attributeId = attributeId.replace(/_.*/, '');  
            if(attributeId && this.config.attributes[attributeId]) {
                element.config = this.config.attributes[attributeId];
                element.attributeId = attributeId;
                this.state[attributeId] = false;
            }
        }.bind(this))
 
        // Init settings dropdown
        var childSettings = [];
        for(var i=this.settings.length-1;i>=0;i--){
            var prevSetting = this.settings[i-1] ? this.settings[i-1] : false;
            var nextSetting = this.settings[i+1] ? this.settings[i+1] : false;
            if(i==0){
                this.fillSelect(this.settings[i])
            }
            else {
                this.settings[i].disabled=true;
            }
            $(this.settings[i]).childSettings = childSettings.clone();
            $(this.settings[i]).prevSetting   = prevSetting;
            $(this.settings[i]).nextSetting   = nextSetting;
            childSettings.push(this.settings[i]);
        }
 
        // Set default values - from config and overwrite them by url values
        if (config.defaultValues) {
            this.values = config.defaultValues;
        }
 
        var separatorIndex = window.location.href.indexOf('#');
        if (separatorIndex != -1) {
            var paramsStr = window.location.href.substr(separatorIndex+1);
            var urlValues = paramsStr.toQueryParams();
            if (!this.values) {
                this.values = {};
            }
            for (var i in urlValues) {
                this.values[i] = urlValues[i];
            }
        }
 
        this.configureForValues();
        document.observe("dom:loaded", this.configureForValues.bind(this));
    },
 
    configureForValues: function () {
        if (this.values) {
            this.settings.each(function(element){
                var attributeId = element.attributeId;
                element.value = (typeof(this.values[attributeId]) == 'undefined')? '' : this.values[attributeId];
                this.configureElement(element);
            }.bind(this));
        }
    },
 
    configure: function(event){
        var element = Event.element(event);
        this.configureElement(element);
    },
 
    configureElement : function(element) {
        this.reloadOptionLabels(element);
        if(element.value){ 
            this.state[element.config.id] = element.value;
            if(element.nextSetting){
                element.nextSetting.disabled = false;
                this.fillSelect(element.nextSetting);
                this.resetChildren(element.nextSetting);
            }
        }
        else {
            this.resetChildren(element);
        }
        //this.reloadPrice();
//      Calculator.updatePrice();
    },
 
    reloadOptionLabels: function(element){
        var selectedPrice;
        if(element.options[element.selectedIndex].config){
            selectedPrice = parseFloat(element.options[element.selectedIndex].config.price)
        }
        else{
            selectedPrice = 0;
        }
        for(var i=0;i<element.options.length;i++){
            if(element.options[i].config){
                element.options[i].text = this.getOptionLabel(element.options[i].config, element.options[i].config.price-selectedPrice);
            }
        }
    },
 
    resetChildren : function(element){
        if(element.childSettings) {
            for(var i=0;i<element.childSettings.length;i++){
                element.childSettings[i].selectedIndex = 0;
                element.childSettings[i].disabled = true;
                if(element.config){
                    this.state[element.config.id] = false;
                }
            }
        }
    },
 
    fillSelect: function(element){
        var attributeId = element.id.replace(/[a-z]*/, '');
        attributeId = attributeId.replace(/_.*/, '');  
        var options = this.getAttributeOptions(attributeId);
        this.clearSelect(element);
        element.options[0] = new Option(this.config.chooseText, '');
 
        var prevConfig = false;
        if(element.prevSetting){
            prevConfig = element.prevSetting.options[element.prevSetting.selectedIndex];
        }
 
        if(options) {
            var index = 1;
            for(var i=0;i<options.length;i++){
                var allowedProducts = [];
                if(prevConfig) {
                    for(var j=0;j<options[i].products.length;j++){
                        if(prevConfig.config.allowedProducts
                            && prevConfig.config.allowedProducts.indexOf(options[i].products[j])>-1){
                            allowedProducts.push(options[i].products[j]);
                        }
                    }
                } else {
                    allowedProducts = options[i].products.clone();
                }
 
                if(allowedProducts.size()>0){
                    options[i].allowedProducts = allowedProducts;
                    element.options[index] = new Option(this.getOptionLabel(options[i], options[i].price), options[i].id);
                    element.options[index].config = options[i];
                    index++;
                }
            }
        }
    },
 
    getOptionLabel: function(option, price){
        var price = parseFloat(price);
        if (this.taxConfig.includeTax) {
            var tax = price / (100 + this.taxConfig.defaultTax) * this.taxConfig.defaultTax;
            var excl = price - tax;
            var incl = excl*(1+(this.taxConfig.currentTax/100));
        } else {
            var tax = price * (this.taxConfig.currentTax / 100);
            var excl = price;
            var incl = excl + tax;
        }
 
        if (this.taxConfig.showIncludeTax || this.taxConfig.showBothPrices) {
            price = incl;
        } else {
            price = excl;
        }
 
        var str = option.label;
        if(price){
            if (this.taxConfig.showBothPrices) {
                str+= ' ' + this.formatPrice(excl, true) + ' (' + this.formatPrice(price, true) + ' ' + this.taxConfig.inclTaxTitle + ')';
            } else {
                str+= ' ' + this.formatPrice(price, true);
            }
        }
        return str;
    },
 
    formatPrice: function(price, showSign){
        var str = '';
        price = parseFloat(price);
        if(showSign){
            if(price<0){
                str+= '-';
                price = -price;
            }
            else{
                str+= '+';
            }
        }
 
        var roundedPrice = (Math.round(price*100)/100).toString();
 
        if (this.prices && this.prices[roundedPrice]) {
            str+= this.prices[roundedPrice];
        }
        else {
            str+= this.priceTemplate.evaluate({price:price.toFixed(2)});
        }
        return str;
    },
 
    clearSelect: function(element){
        for(var i=element.options.length-1;i>=0;i--){
            element.remove(i);
        }
    },
 
    getAttributeOptions: function(attributeId){
        if(this.config.attributes[attributeId]){
            return this.config.attributes[attributeId].options;
        }
    },
 
    reloadPrice: function(){
        var price    = 0;
        var oldPrice = 0;
        for(var i=this.settings.length-1;i>=0;i--){
            var selected = this.settings[i].options[this.settings[i].selectedIndex];
            if(selected.config){
                price    += parseFloat(selected.config.price);
                oldPrice += parseFloat(selected.config.oldPrice);
            }
        }
 
        optionsPrice.changePrice('config', {'price': price, 'oldPrice': oldPrice});
        optionsPrice.reload();
 
        return price;
 
        if($('product-price-'+this.config.productId)){
            $('product-price-'+this.config.productId).innerHTML = price;
        }
        this.reloadOldPrice();
    },
 
    reloadOldPrice: function(){
        if ($('old-price-'+this.config.productId)) {
 
            var price = parseFloat(this.config.oldPrice);
            for(var i=this.settings.length-1;i>=0;i--){
                var selected = this.settings[i].options[this.settings[i].selectedIndex];
                if(selected.config){
                    price+= parseFloat(selected.config.price);
                }
            }
            if (price < 0)
                price = 0;
            price = this.formatPrice(price);
 
            if($('old-price-'+this.config.productId)){
                $('old-price-'+this.config.productId).innerHTML = price;
            }
 
        }
    }
}

48 comments

  1. Is it possible to use this code to produce the same effect in the related products list on a product page, by using it in related.phtml somehow instead of configurable.phtml? This would be really helpful for a project I’m working on. What would I have to change to make it work? Any ideas?

    Also it would have to not interfere with the confirgurable options of the main product on the product page.

  2. I’m a little confused. This post is called “Multiple configurable products with options on one page in Magento” however, I cant seem to get multiple config products on one page. How do I relate one config product to another? In Magento admin?

  3. Hi i followed your instruction but drop down only displayed there is no value in drop down box how to call all the configuration product value

  4. How to Display more Grouped products in a single page in Magento

    Please check this demo link : http://www.linbide.co.nz/product.php?p=1.

    option 1) In the above link’s page “Straight Router Bits Two Flute” is a category and
    “STANDARD LENGTH” , “LONG REACH”, etc are Grouped products. or

    option 2) In the above link’s page “Straight Router Bits Two Flute” is a Grouped Product. If so, How i can give space or
    enter some text between Associated Simple products, as like in the page :

    “LONG REACH *
    1/4″ (6.4mm) Diameter x 1 1/4” (32mm) Long Shank

    I assume option 1) is the solution. So I have to display more than one grouped product in a single page.

    Can anyone give me the solution how i can achieve as in the above link in magento.

    I created grouped product using this code : http://pastebin.com/nJppZ05r

    Thanks in Advance.

  5. Hi,

    Thanks for the code, this helped me a lot. My product options disappeared after upgrade, but now it is working.

    However, the prices won’t reload. Coud you of someone please let me know how to fix this?

  6. Hi,

    We are new in magento. we want to know how to set configurable product out of stock if its associate products have gone out of stock.

    Thanks in Advance

  7. what all changes do i need to make on product.js and bundle.js if i have more than one bundle product in one page?

  8. oops. sorry.
    correct code:

    $_hardDiskSku  = $_product->getBundleHardDiskSku();
    $_hardDisk = '';
    $_hdIsGrouping = '';
    if(!empty($_hardDiskSku))
    {
        $_hdProduct = Mage::helper("my_module")->getProduct($_hardDiskSku, Mage::app()->getStore()->getId() ,'sku');
        if($_hdProduct)
        {
            $_hardDisk = $this->getBundleJsonConfig($_hdProduct, 'hd');
            $_hdIsGrouping = Mage::helper("my_module")->getOptionValue($_hdProduct,
                    'hard_disk_is_grouping',
                    $_hdProduct->getBundleHDIsGrouping());
        }
    }
  9. Hi Goran.
    Can you tell me if is possible to use jQuery to request bundle items information from my module instead of loading it in the template file? what I want is to avoid loading lots of bundle items when open the page and just load those options when customer wants to change something (in this sample the hard drive). The page for configurable products is taking around 28 long seconds to load all the items.

    $_hardDiskSku  = $_product->getBundleHardDiskSku();
    $_hardDisk = '';
    
    $_hdIsGrouping = '';
    if(!empty($_hardDiskSku))
    {    
        $_hdProduct = Mage::helper("my_module")->getProduct($_gripSku, Mage::app()->getStore()->getId() ,'sku');
        if($_hdProduct)
        {
        	$_hardDisk = $this->getBundleJsonConfig($_hdProduct, 'hd');    	
        	$_hdIsGrouping = Mage::helper("my_module")->getOptionValue($_hdProduct, 
    				'hard_disk_is_grouping',
    				$_hdProduct->getBundleHDIsGrouping());
        }
    }
  10. How can i skip the product view page for configurable products when add to cart?. Please help me…

  11. hi

    i really liked your document and its really helpful but can you pls explain same method for bundle product too. so it will be very helpful for me.

    Thanks

  12. Hi,

    Basically I’ve got your tutorial working in my site. But instead of assigning the correct simple product related to the attributes/options its assigning the attributes to the configurable product, which only has the standard/lowest price. Any thing you know to change it from assigning attributes to assigning the correct simple product like in the product

    Below is an example of a product added in the product page and one in the list. The top one is from the list, which wont add the new prices and the bottom is from the product which would add a different price depending on the options selected

    http://www.hillsidegroup.co.uk/lordwholesale-configurable-options.jpg.

    Any help would be greatly appreciated, thank you.

  13. Hi,
    very help full post.
    But can you explain how to define option prices for multiple products.

  14. This is an extremely helpful tutorial, but there is still work to do if you want to use VarienForm validation, which refers to an attribute id. If you’re just using the default attributeN format for the select box, and you have multiple products with attributeN, VarienForm validation will target the first select box with that id. Better to append the product id to the select box directly rather in Inchoo_Product. That way, both validation and options fill work properly.

  15. Hi,
    I’m new magento developer and implement this code but fetchning some proble Attribute name is display but attribute value not display in dropdown.
    please help me.

  16. Hi, can somepne please tell me how they got the different getJsonConfig() for the products.

    Ive used a config product page, and brough more config products in….but i cant get anything from the second config product’s getJsonConfig() method when called.

    Ive tried bypassing this….but just doesnt seem right…having to copy a quite bit of code into an extension to get this to work correctly.

    The main config product for the page is fine….just the included products that have issues here.

    Ive tried calling and using config-product-model object, tried instantiating the product-config block itself (but cant get it populated with all the data it needs i think to product a jsonconfig string.)

    How can i easily generate this string, or get the getJsonConfig function itself working?

    thx

  17. I am setting up an apparel store for a client, they have a selection of items and each item has a color selection, however, not all of the items come in the same colors. Originally I set up each item with its own color attribute with the colors specific to that item, however, when you do a seach, you then end up with multiple “Color” sections listed on the left that you can use to narrow your search. Is there any way to set one “Color” attribute with all of the options, and then specify which options a specific product has access to?

  18. Hi
    i m new to magento can you please help
    i want the configurable drop down in crosssell option (checkout option)
    please help….

  19. Hello,
    I develop a module to compose a grouped product with configurable product.

    With your solution a retrieve well production option, I already manage the recursive item for configurable product in grouped product.

    How can i do to add all configurable product to cart after selection? in the same form…

    1. Hello

      Can you please let me know how did you repload the price. I need to do the same. Please update as soon as possible.

  20. Hey!

    Thank you for your great solution!

    But i have one problem: How do you handle the price reload?
    On the product page is missing var optionsPrice = new Product.OptionsPrice(….
    => exception, no price update after attribute selection possible

    Did you have any idea?

  21. Where can i specify the second configurable product id in the code.

    Please help me on this.

  22. Hi ,
    I did steps like you explain. Code working – I can add in cart choose option , but can’t change image in list when made option ( maybe some I made wrong). Can see here http://www.modatadnes.com/shop/woman.html , but only my product 1856 is now configurable. Can you give me advice how can change image when change option configurable product in catalog page.
    Thanks again Pau and Goran

  23. Ok, I thinks it’s time to talk about viagra!!

    We finally got it!! We were calling the same block again and again, so it was always the same. So we decided to dynamically create the block directly from the template:
    $this->getLayout()->createBlock(‘catalog/product_view_type_configurable’, ”, array(‘template’=> ‘catalog/product/view/type/options/configurable.phtml’))->toHtml(); ?>

  24. OK, I have to merge all my answers…..

    The thing is that the register/unregister seems to not work well: it always gets the same id. How did you solve this?

    Thanks!

  25. Hi again!

    I’ve just used the:

    And then I could see the configurable options. But only the first configurable option is working. I think we have to change id’s and other things…

  26. Hi Goran,

    I’ve been trying to do this but I’ve run into a few problems. What I did was:

    1. Add to the catalog_product_layer on the catalog.xml file the block related to configurable product (so I can call the configurable block from the list.phtml file).

    2. The problem is that on the configurable block the first line:
    $this->getProduct();

    doesn’t get any product, and then it just can’t get anything.

    How have you solved this?

    Thanks!

  27. Hi, i found your tutorial and i’m really want it but i’m a bit of confuse on how to do it. can you show me what would be the final code in configurable.php and in the product.js?

  28. U can use grouped product with configurable but it is out of scope of this tutorial. This tutorial is more suited for situations in which you use configurable product not as part of grouped product. For example for widget where you display all configurable product with options in some category.

  29. How do you get multiple configurable products onto one page in the first place? I’m looking to create a grouped product which allows multiple configurable products to be associated to it. Any ideas how I can acheive this?

  30. This tutorial is for more than one different configurable product on same page.
    You have one configurable product with different color (multiple simple products). So U can use your configurable product url for your simple products with different colors.

  31. This sounds like something I might need. Not sure I quiet get what you mean though.

    If I have a configurable product in two different colors, will this make me able to show them both but to point to one single url?

    Please explain further! 🙂

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <blockquote cite=""> <code> <del datetime=""> <em> <s> <strike> <strong>. You may use following syntax for source code: <pre><code>$current = "Inchoo";</code></pre>.