Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Upsell product prices missing in Magento 2 when block loaded with AJAX

I am working on a Magento 2 module that uses AJAX to load the upsell products. The upsell products can be different per customer so AJAX is used to load the block to allow for cache busting.

For this I have a custom module where my block extends the \Magento\Catalog\Block\Product\ProductList\Upsell. In the modules layout for catalog_product_view.xml I have the following -

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="product.info.upsell" remove="true" />
        <referenceContainer name="content.aside">
            <block class="MyCompany\MyModule\Block\Product\ProductList\Upsell"
                   name="personalised.product.upsell"
                   template="MyCompany_MyModule::upsell.phtml" />
        </referenceContainer>
    </body>
</page>

In my upsell.phtml -

<div id="personalised-upsells-container" data-role="personalised-upsells"></div>
<script type="text/x-magento-init">
    {
        "*": {
            "MyCompany_MyModule/js/upsell": {
                "upsellAjaxUrl": "<?php echo $block->getUpsellAjaxUrl(); ?>"
            }
        }
    }
</script>

The getUpsellAjaxUrl() generates http://magento2.dev/personalised/products/upsellAjax/id/6

My upsell.js -

define([
    'jquery',
    'upsellProducts'
], function($) {

    function getUpsellContent(url) {
        $.ajax({
            url: url,
            dataType: 'html'
        }).done(function (data) {
            $('#personalised-upsells-container').html(data).promise().done(function(){
                $('.upsell').upsellProducts();
            });
        });
    }

    return function (config, element) {
        getUpsellContent(config.upsellAjaxUrl);
    };
});

My controller (upsellAjax) -

class UpsellAjax extends ProductController
{
    public function execute()
    {
        $productId = (int) $this->getRequest()->getParam('id');
        $product = $this->loadProduct($productId);
        if (!$product) {
            return false;
        }
        /** @var \Magento\Framework\View\Result\Layout $resultLayout */
        $resultLayout = $this->resultFactory->create(ResultFactory::TYPE_LAYOUT);
        return $resultLayout;
    }
}

My personalised_products_upsellajax.xml -

<?xml version="1.0"?>
<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
    <container name="root">
        <block class="MyCompany\MyModule\Block\Product\ProductList\Upsell" name="product.info.personalised.upsell" template="Magento_Catalog::product/list/items.phtml">
            <arguments>
                <argument name="type" xsi:type="string">upsell</argument>
            </arguments>
        </block>
    </container>
</layout>

As you would expect this correctly loads the product upsell block via ajax, pushes the HTML into my container then initialises the upsellProducts widget on the page. My upsell products show as expected but without prices.

I have tried a couple of things to debug the situation but as far as I can see it fails to load the priceRender on line 428 of \Magento\Catalog\Block\Product\AbstractProduct inside the getProductPriceHtml() method. The line $priceRender = $this->getLayout()->getBlock('product.price.render.default'); always returns false when the block is loaded via AJAX.

This is also the situation when I replace using my block for the default block within the layout (personalised_products_upsellajax.xml) e.g.

<?xml version="1.0"?>
<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
    <container name="root">
        <block class="Magento\Catalog\Block\Product\ProductList\Upsell" name="product.info.upsell" template="Magento_Catalog::product/list/items.phtml">
            <arguments>
                <argument name="type" xsi:type="string">upsell</argument>
            </arguments>
        </block>
    </container>
</layout>

I was thinking it might have something to do with the removal of the upsell block first in my layout i.e <referenceBlock name="product.info.upsell" remove="true" /> I decided to comment this line out which results in two upsell blocks appearing, one is the default loaded block and the other is my AJAX block. Same results where the default block show correct information however my AJAX block is still missing prices.

Any help would be greatly appreciated.

like image 444
Steven Avatar asked Jan 06 '16 08:01

Steven


1 Answers

I had the same problem: the block product.price.render.default needs to be available in the layout, as well as some of its child blocks, to render prices. The idea to the answer is simple: since this block is loaded for the default handle, make sure this handle is available in the layout, by adding it early in the request:

public function __construct(Context $context, LayoutInterface $layout)
{
    parent::__construct($context);

    $this->layout = $layout;
    $this->layout->getUpdate()->addHandle('default');
}
like image 190
Patrick van Bergen Avatar answered Oct 10 '22 18:10

Patrick van Bergen