Magento – Magento 2, List of configurable products with attribute options & add to cart

configurable-productknockoutjsmagento2requirejsswatches

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?

Best Answer

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);
}`
Related Topic