3

Scenario:

I have a requirement to show a list of products, where a customer can quickly add them to the basket. This list can contain simple or configurable products, and if a configurable is listed, it must provide all the options required to add right to the basket without redirecting to the details page. Essentially what is possible with swatches, but for all product options.

Problem:

I have created a view which lists out the products, the problem I'm having is understanding how to init a "mage.priceBox" widget against each configurable with its own spConfig. So when a customer changes the options, the price against the product is recalculated.

I've looked at how Magento does this for detail pages, it requires "Magento_ConfigurableProduct/js/configurable" and triggers this using x-magento-init

<script type="text/x-magento-init">
    {
        "#product_addtocart_form": {
            "configurable": {
                "spConfig": <?php echo $block->getJsonConfig() ?>
            }
        }
    }
</script>

But looking at core Magento, this is always based around having a single product form, referenced by its id in the x-magento-init.

Has anyone seen this done before or have any tips on having multiple configurable products add to cart forms on a single page?

4
  • what about move all content product detail main to category page ???
    – HoangHieu
    Commented Oct 21, 2016 at 9:34
  • Can you explain how that might help? Magento by default doesnt seem to provide this functionality out of the box, so custom page vs category page wouldn't change much.
    – Stanley
    Commented Oct 21, 2016 at 9:37
  • If product detail has a lot of options, category page will slow down performance. are you sure to do it ??
    – HoangHieu
    Commented Oct 21, 2016 at 9:42
  • This is besides the point, my problem isnt about the performance of this page, but the implementation.
    – Stanley
    Commented Oct 21, 2016 at 9:46

2 Answers 2

1

I have tried a similar approach, but in the end for some reason when I changed the attribute for one product the price would also change for the other products. The way I did it was to add the product id to the form id. Then add the below code within the foreach loop.

<script type="text/x-magento-init">
{
"#product_addtocart_form<?php echo $_product->getId();?>": {
"configurable": {
"spConfig": <?php /* @escapeNotVerified */ echo $matrixConfigurableBlock->getJsonConfigForProdList($_product, $configProdOptFrontend) ?>,
"onlyMainImg": <?php /* @escapeNotVerified */ echo $matrixConfigurableBlock->getVar('change_only_base_image', 'Magento_ConfigurableProduct') ?: 'false'; ?>
                                                    }
                                                }
                                            }
</script>

Basically the product_id is passed through to the getJsonConfigForProdList function along with a flag to state whether we are in grid or list mode. The getJsonConfigForProdList is below.

public function getJsonConfigForProdList($product, $configProdOptFrontend)
  {

    $store = $this->getCurrentStore();
    $currentProduct = $product;
    $regularPrice = $currentProduct->getPriceInfo()->getPrice('regular_price');
    $finalPrice = $currentProduct->getPriceInfo()->getPrice('final_price');
    if($configProdOptFrontend == 'matrix'){
        $options = $this->helper->getOptionsForMatrix($currentProduct, $this->getAllowProductsForProdList($product));
    }else{
         $options = $this->helper->getOptions($currentProduct, $this->getAllowProductsForProdList($product));
    }
    $attributesData = $this->configurableAttributeData->getAttributesData($currentProduct, $options);

    $config = [
        'attributes' => $attributesData['attributes'],
        'template' => str_replace('%s', '<%- data.price %>', $store->getCurrentCurrency()->getOutputFormat()),
        'optionPrices' => $this->getOptionPrices(),
        'currencySymbol'=>str_replace('%s','', $store->getCurrentCurrency()->getOutputFormat()),
        'prices' => [
            'oldPrice' => [
                'amount' => $this->_registerJsPrice($regularPrice->getAmount()->getValue()),
            ],
            'basePrice' => [
                'amount' => $this->_registerJsPrice(
                    $finalPrice->getAmount()->getBaseAmount()
                ),
            ],
            'finalPrice' => [
                'amount' => $this->_registerJsPrice($finalPrice->getAmount()->getValue()),
            ],
        ],
        'productId' => $currentProduct->getId(),
        'chooseText' => __('Choose an Option ...'),
        'images' => isset($options['images']) ? $options['images'] : [],
        'index' => isset($options['index']) ? $options['index'] : [],
    ];

    if ($currentProduct->hasPreconfiguredValues() && !empty($attributesData['defaultValues'])) {
        $config['defaultValues'] = $attributesData['defaultValues'];
    }

    $config = array_merge($config, $this->_getAdditionalConfig());

    return $this->jsonEncoder->encode($config);
}

You will also need to create a few other functions, one of which for the price

public function getPriceJsonConfigProdList($currentProduct)
    {
        /* @var $product \Magento\Catalog\Model\Product */
        $product = $currentProduct;
    if (!$this->hasOptions()) {
        $config = [
            'productId' => $product->getId(),
            'priceFormat' => $this->_localeFormat->getPriceFormat()
            ];
        return $this->_jsonEncoder->encode($config);
    }

    $tierPrices = [];
    $tierPricesList = $product->getPriceInfo()->getPrice('tier_price')->getTierPriceList();
    foreach ($tierPricesList as $tierPrice) {
        $tierPrices[] = $this->priceCurrency->convert($tierPrice['price']->getValue());
    }
    $config = [
        'productId' => $product->getId(),
        'priceFormat' => $this->_localeFormat->getPriceFormat(),
        'prices' => [
            'oldPrice' => [
                'amount' => $this->priceCurrency->convert(
                    $product->getPriceInfo()->getPrice('regular_price')->getAmount()->getValue()
                ),
                'adjustments' => []
            ],
            'basePrice' => [
                'amount' => $this->priceCurrency->convert(
                    $product->getPriceInfo()->getPrice('final_price')->getAmount()->getBaseAmount()
                ),
                'adjustments' => []
            ],
            'finalPrice' => [
                'amount' => $this->priceCurrency->convert(
                    $product->getPriceInfo()->getPrice('final_price')->getAmount()->getValue()
                ),
                'adjustments' => []
            ]
        ],
        'idSuffix' => '_clone',
        'tierPrices' => $tierPrices
    ];

    $responseObject = new \Magento\Framework\DataObject();
    $this->_eventManager->dispatch('catalog_product_view_config', ['response_object' => $responseObject]);
    if (is_array($responseObject->getAdditionalOptions())) {
        foreach ($responseObject->getAdditionalOptions() as $option => $value) {
            $config[$option] = $value;
        }
    }

    return $this->_jsonEncoder->encode($config);
}`
0

First: You need to overwrite some file to do that.

1: Magento_Catalog/product/list.phtml

2: Magento_Swatch/js/configurable.js

3: Magento_Catalog/view/layout/catalog_category_view.xml

Step by step: It didn't test you must check and correct error.

In file: catalog_category_view.xml create child block in block name category.products, content like

<block class="Magento\Catalog\Block\Product\View" name="product.info" template="product/view/form.phtml">
<container name="product.info.form.content" as="product_info_form_content">
    <block class="Magento\Catalog\Block\Product\View" name="product.info.addtocart" as="addtocart" template="product/view/addtocart.phtml"/>
</container>
<block class="Magento\Framework\View\Element\Template" name="product.info.form.options" as="options_container">
    <block class="Magento\Catalog\Block\Product\View" name="product.info.options.wrapper" as="product_options_wrapper" template="product/view/options/wrapper.phtml">
        <block class="Magento\Catalog\Block\Product\View\Options" name="product.info.options" as="product_options" template="product/view/options.phtml">
            <block class="Magento\Catalog\Block\Product\View\Options\Type\DefaultType" as="default" template="product/view/options/type/default.phtml"/>
            <block class="Magento\Catalog\Block\Product\View\Options\Type\Text" as="text" template="product/view/options/type/text.phtml"/>
            <block class="Magento\Catalog\Block\Product\View\Options\Type\File" as="file" template="product/view/options/type/file.phtml"/>
            <block class="Magento\Catalog\Block\Product\View\Options\Type\Select" as="select" template="product/view/options/type/select.phtml"/>
            <block class="Magento\Catalog\Block\Product\View\Options\Type\Date" as="date" template="product/view/options/type/date.phtml"/>
        </block>
        <block class="Magento\Framework\View\Element\Html\Calendar" name="html_calendar" as="html_calendar" template="Magento_Theme::js/calendar.phtml"/>
    </block>
    <block class="Magento\Catalog\Block\Product\View" name="product.info.options.wrapper.bottom" as="product_options_wrapper_bottom" template="product/view/options/wrapper/bottom.phtml">
        <block class="Magento\Catalog\Block\Product\View" name="product.info.addtocart.additional" as="product.info.addtocart" template="product/view/addtocart.phtml"/>
    </block>
</block>
</block>

2: After that go to Magento_Catalog/templates/product/list.phtml

at line 79 you can find a line isSaleable()): ?> remove tocart-form in this file them add this code.

$productForm = $block->getLayout()->getBlock('product.info');
if($productForm){
      $productForm->setProductId($_product->getId()); //Registry here
      echo $productForm->toHtml();
}

to optimate this code you can registry $_product and product.info can access this :) don't need load product again.

3: With configuable.js wasn't built for multi product-detail form :). when load it maybe throw some error, you must re-build it for multi product-detail form :).

I hope this will help you

2
  • This doesn't work for me, the block is only rendered for one product, then it repeats the options from the first product. Did you find a solution to that?
    – Silvan
    Commented Mar 30, 2017 at 18:34
  • That means block Magento\Catalog\Block\Product\View\Options was registry that product, I think you can unregistry after render options_container
    – HoangHieu
    Commented Sep 5, 2017 at 5:26

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.