Magento 2 Checkout – Add Cross Sell Block to Checkout Step

checkoutmagento2

I have added a checkout step to my store where i want cross sell products. I have this block that is on the cart page for cross sell products which i would like to add in however my step is just a html file.

                <block class="Magento\Checkout\Block\Cart\Crosssell" name="checkout.cart.crosssell" template="Magento_Catalog::product/list/items.phtml" after="-">
                    <arguments>
                        <argument name="type" xsi:type="string">crosssell</argument>
                    </arguments>
                    <block class="Magento\Catalog\Block\Product\ProductList\Item\Container" name="crosssell.product.addto" as="addto">
                        <block class="Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare"
                               name="crosssell.product.addto.compare" as="compare"
                               template="Magento_Catalog::product/list/addto/compare.phtml"/>
                    </block>
                </block>

So as suggested i am going to create a controller for cross sell and then call this controller using ajax and place into my html with javascript.

<?php
namespace Harri\HelloWorld\Controller\Index;

use Magento\Framework\Controller\ResultFactory;

class Display extends \Magento\Framework\App\Action\Action
{
protected $resultPageFactory;

/**
 * Constructor
 * 
 * @param \Magento\Framework\App\Action\Context  $context
 * @param \Magento\Framework\View\Result\PageFactory $resultPageFactory
 */
public function __construct(
    \Magento\Framework\App\Action\Context $context,
    \Magento\Framework\View\Result\PageFactory $resultPageFactory

)
{
    $this->resultPageFactory = $resultPageFactory;
    $this->_resultFactory = $context->getResultFactory();
    parent::__construct($context);
}

/**
 * Execute view action
 * 
 * @return \Magento\Framework\Controller\ResultInterface
 */
public function execute()
{
    $resultLayout = $this->resultFactory->create(ResultFactory::TYPE_LAYOUT);
    return $resultLayout;
}
}

However i am currently having issues with the controller and rendering the block from xml. This needs to use below code to render the block however am not quite sure how to use it in the controller to generate my cross-sell block.

$resultLayout = $this->resultFactory->create(ResultFactory::TYPE_LAYOUT);
return $resultLayout;

I can echo hello world out into my controller however cannot get my controller to render the block from the xml. Also having a lot of difficulty getting the controller to just be a blank layout with just my cross-sell block.

XML: (using a differnt block at present for testing as have not setup crosssell on this site i ma testing on)

<?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" label="Root">
        <block class="Magento\Cms\Block\Block" name="homeslider">
                <arguments>
                    <argument name="block_id" xsi:type="string">homeslider</argument>
                </arguments>
            </block>
</container>

I have created a github with progress so far with following issues:

  1. Block called from the controller redirects to controller page. Want to either have this redirect to cart when adding a product from controller or use ajax add to cart from checkout.

  2. Checkout step shows both my step and the next step on same page and both highlighted when loading /checkout. I think this may be due to my module requiring dependency on magento_checkout however cant see in docs how to do this.

  3. My other issue once this is all working is hiding the step if there are no cross sells or showing another block in place in this step. Will try and check weather this is empty before the checkout loads and disable the step in the js however worried about page speed with loading controller twice.

Best Answer

If you want to call a block in html file, AJAX loading the content is the way to it. Magento 2's export functionality does the same thing. When you change export entity magento has to change filter html that is rendered. so magento calls this Controller

Magento\ImportExport\Controller\Adminhtml\Export\GetFilter

Observe the code in execute method. There are two key things

 $resultLayout = $this->resultFactory->create(ResultFactory::TYPE_LAYOUT);
return $resultLayout;

So you are going to return a layout and NOT a page layout.

Name of your xml will be .xml That means you will create a xml like this

<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/layout_generic.xsd">
<container name="root" label="Root">
           <block class="Magento\Checkout\Block\Cart\Crosssell" name="checkout.cart.crosssell" template="Magento_Catalog::product/list/items.phtml" after="-">
                <arguments>
                    <argument name="type" xsi:type="string">crosssell</argument>
                </arguments>
                <block class="Magento\Catalog\Block\Product\ProductList\Item\Container" name="crosssell.product.addto" as="addto">
                    <block class="Magento\Catalog\Block\Product\ProductList\Item\AddTo\Compare"
                           name="crosssell.product.addto.compare" as="compare"
                           template="Magento_Catalog::product/list/addto/compare.phtml"/>
                </block>
            </block>
</container>

Notice that parent node is and not That's it. You will have your cross sell html as response to your ajax request. Just insert the html wherever you want.

To an ajax call you can write jquery inside your html. somthing like this

<script>
require(['jquery'],function($){
   $.get('url/of/controller',function(response){
      //do stuff with your html
   })
})
<script>

If you knockout do with knockout. Or doing it with x-magento-init is even better.

Just start with a simple echo "Hello world" in your controller and it will become clear. If your controller action is route/controller/action then your layout name should be route_controller_action.xml . That's why result factory knows which layout.xml to use from your module

Related Topic