如何在Magento 2中使用AJAX呈现HTML


12

我尝试在Magento 2中找到通过AJAX呈现HTML的最佳方法。

方法1:使用不带布局的控制器

文件 Foo/Bar/Controller/Popin/Content.php

<?php

namespace Foo\Bar\Controller\Popin;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;

/**
 * Class Content
 */
class Content extends Action
{

    /**
     * Content constructor.
     *
     * @param Context $context
     */
    public function __construct(
        Context $context
    ) {
        parent::__construct($context);
    }

    /**
     *
     */
    public function execute()
    {
        /** @var \Magento\Framework\View\Layout $layout */
        $layout = $this->_view->getLayout();

        /** @var \Foo\Bar\Block\Popin\Content $block */
        $block = $layout->createBlock(\Foo\Bar\Block\Popin\Content::class);
        $block->setTemplate('Foo_Bar::popin/content.phtml');

        $this->getResponse()->setBody($block->toHtml());
    }
}   

方法2:使用具有自定义布局的Controller

文件 Foo/Bar/Controller/Popin/Content.php

<?php

namespace Foo\Bar\Controller\Popin;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;

/**
 * Class Content
 */
class Content extends Action
{

    /**
     * Content constructor.
     *
     * @param Context $context
     */
    public function __construct(
        Context $context
    ) {
        parent::__construct($context);
    }

    /**
     *
     */
    public function execute()
    {
        $this->_view->loadLayout();
        $this->_view->renderLayout();
    }
}    

文件 Foo/Bar/view/frontend/page_layout/ajax-empty.xml

<?xml version="1.0"?>

<layout xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_layout.xsd">
    <container name="root"/>
</layout>

文件 Foo/Bar/view/frontend/layout/foo_bar_popin_content.xml

<?xml version="1.0"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="ajax-empty" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceContainer name="root">
            <block class="Foo\Bar\Block\Popin\Content" name="foo_bar_popin_content" template="Foo_Bar::popin/content.phtml" cacheable="false"/>
        </referenceContainer>
    </body>
</page>

IMO的最佳实践似乎是方法2,因为它将逻辑与控制器分开。
但是方式2的问题在于,生成了<body>and <head>CSS/ JS,因此它不是仅包含我的块模板的完全清除的HTML。

  • 我是否使用错误的自定义布局?
  • 路1被认为是一个好的做法呢?
  • 还有其他方法吗?

Answers:


18

我也将按照第2种方法,实际上,您实际上可以通过AJAX渲染“纯” HTML,而无需头部,主体,css等。

诀窍是:

  • 告诉您的控制器实例化一个响应,\Magento\Framework\View\Result\Layout而不是类型\Magento\Framework\View\Result\Page
  • 将布局XML文件的根节点使用<layout...>...</layout>而不是<page...>...</page>

这是一个非常简单的实现。

控制器

<?php    
namespace Namespace\Module\Controller\Index;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\ResponseInterface;
use Magento\Framework\Controller\ResultFactory;

class Index extends Action
{
    /**
     * Dispatch request
     *
     * @return \Magento\Framework\Controller\ResultInterface|ResponseInterface
     * @throws \Magento\Framework\Exception\NotFoundException
     */
    public function execute()
    {
        return $this->resultFactory->create(ResultFactory::TYPE_LAYOUT);
    }
}

布局

<?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="Namespace\Module\Block\Some\Block" name="namespace_module.some_block" />
    </container>
</layout>

Github上的示例

参见以下示例模块:https : //github.com/herveguetin/Herve_AjaxLayout_M2

该模块生成以下内容:

在此处输入图片说明


如果我想加载整个布局(带有少量容器,块等的XML)怎么办?创建-> toHtml,并通过json发送到ajax?
mattkrupnik

5

开箱即用,Magento不使用任何这些方法通过AJAX呈现HTML。

从我所看到的情况来看,每当需要完成此类操作时,便会使用JSON来传输结果。

来自的示例Magento/Checkout/Controller/Cart/Add

$this->getResponse()->representJson(
    $this->_objectManager->get('Magento\Framework\Json\Helper\Data')->jsonEncode($result)
);

然后,Magento 2使用称为节的新机制来处理前端上的数据并更新需要更新的特定块,您可以在此问答中了解有关节的更多信息:https : //magento.stackexchange.com/a/ 143381/2380

编辑我的回答的第二部分:如Max在评论中所述,部分仅用于客户特定的数据,并且使用此功能而不是每个AJAX调用都不是正确的解决方案。


是的,我也使用JSON传输结果,但出于问题的目的简化了我的类;)但是我不知道该部分功能,这似乎是执行所需操作的正确方法。我会看一下。如果有其他回应,我将等待,否则我将验证您的答案。谢谢 !
MatthéoGeoffray

2
我同意使用Json响应而不是原始html数据。但是答案的第二部分并不完全正确。请注意,仅使用客户特定数据并使用此功能而非每个Ajax调用的客户部分不是一个好主意。
最大

2
@Matthéo是的,我知道:)我的评论写给Raphael来纠正答案,因为答案的第二部分可能会被其他用户误解。
Max

1
@MaxStsepantsevich感谢您发现问题,我已对答案进行了编辑以反映您的意见
Digital Pianism的Raphael

1
我使用您的反馈添加了答案。谢谢两个人的帮助。
MatthéoGeoffray

3

在我的示例中,我不能使用sections它,因为它不是customer data,不是在PUT/ POST动作之后,而是使用Raphael at Digital Pianism答案,我弄清楚了Magento如何渲染部分。

如果以cart小节为例,则使用该方法\Magento\Customer\CustomerData\SectionPool::getSectionDataByNames从小节中检索数据。这导致我们进入\Magento\Checkout\CustomerData\Cart::getSectionData一个包含该部分区域的数组,包括$this->layout->createBlock('Magento\Catalog\Block\ShortcutButtons')->toHtml()

取决于此的是最终的Controller类:

<?php

namespace Foo\Bar\Controller\Popin;

use Magento\Framework\App\Action\Action;
use Magento\Framework\App\Action\Context;
use Magento\Framework\Controller\Result\JsonFactory;
use Magento\Framework\Data\Form\FormKey\Validator;
use Psr\Log\LoggerInterface;

/**
 * Class Content
 */
class Content extends Action
{

    /**
     * @var LoggerInterface $logger
     */
    private $logger;
    /**
     * @var Validator $formKeyValidator
     */
    private $formKeyValidator;
    /**
     * @var JsonFactory $resultJsonFactory
     */
    private $resultJsonFactory;

    /**
     * Content constructor.
     *
     * @param Context $context
     * @param LoggerInterface $logger
     * @param Validator $formKeyValidator
     * @param JsonFactory $resultJsonFactory
     */
    public function __construct(
        Context $context,
        LoggerInterface $logger,
        Validator $formKeyValidator,
        JsonFactory $resultJsonFactory
    ) {
        $this->logger            = $logger;
        $this->formKeyValidator  = $formKeyValidator;
        $this->resultJsonFactory = $resultJsonFactory;
        parent::__construct($context);
    }

    /**
     *
     */
    public function execute()
    {
        if (!$this->formKeyValidator->validate($this->getRequest())) {
            return $this->resultRedirectFactory->create()->setPath('checkout/cart/');
        }

        /** @var \Magento\Framework\Controller\Result\Json $resultJson */
        $resultJson = $this->resultJsonFactory->create();

        try {
            /** @var \Magento\Framework\View\Layout $layout */
            $layout = $this->_view->getLayout();
            /** @var \Foo\Bar\Block\Popin\Content $block */
            $block = $layout->createBlock(\Foo\Bar\Block\Popin\Content::class);
            /** @var array $response */
            $response = [
                'content' => $block->toHtml(),
            ];
        } catch (\Exception $exception) {
            $resultJson->setStatusHeader(
                \Zend\Http\Response::STATUS_CODE_400,
                \Zend\Http\AbstractMessage::VERSION_11,
                'Bad Request'
            );
            /** @var array $response */
            $response = [
                'message' => __('An error occurred')
            ];
            $this->logger->critical($exception);
        }

        return $resultJson->setData($response);
    }
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.