Multiple configurable products with options on one page in Magento

Posted in Magento tips & tricks, Magento Experts on February 24, 2012 0 comment(s)  

When you code with products configurable in Magento, maybe, you will have problems between configurable products and options . so, To display 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  you 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.

-------------------------------------------------------------------

1
2
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;
}
}
}
}

Enjoy coding!!

- From inchoo.net -


This post was posted in Magento tips & tricks, Magento Experts

Leave a comments
Your email address will not published. Required fields are marked *

What Our Clients Say?

  • Oscar Soto MANAGER
    ArrowHiTech has always impressed me! They are professionals and are quick to response to my needs. I have used
    them in the past and will continue to use them in the future. Thanks Martin and all the Staff at ArrowHiTech!!!
  • The guys of AHT solutions did a perfect job and are real profesionals. Definitely recommend them and will use them again!
  • Jonatas Leonel MANAGER
    Martin has a great team of Magento experts, I really liked the work he did for me. I asked him to customize my template following a couple of jpg files that I got from a designer and he worked on that template without the psd's - that was awesome, the result I got was really good.
  • I really enjoy working with ahtsolutions. Quick response to our questions, delivery the project in time and excellent skills. I hope to work with them soon
Request us now! Yahoo:gravitational_boy Email:info@mage-shop.com Skype:hyun_yoo_it +84437955813