Как визуализировать HTML с AJAX в Magento 2

12

Я пытаюсь найти лучший способ визуализации HTML через AJAX в Magento 2.

Способ 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: использование контроллера с пользовательским макетом

файл 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>и <head>с CSS/ JSгенерируются, так что это не полностью очищенный HTML с только моим блочным шаблоном.

  • я использую пользовательский макет неправильно?
  • Является ли Путь 1 хорошей практикой?
  • Есть ли другие способы сделать это?
Маттео Джеффри
источник

Ответы:

18

Я бы также пошел по пути 2, и, действительно, вы можете визуализировать «чистый» HTML через AJAX без заголовка, тела, 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 не использует ни один из этих методов для рендеринга HTML через AJAX.

Из того, что я видел, каждый раз, когда нужно это делать, для передачи результата используется JSON.

Пример из Magento/Checkout/Controller/Cart/Add:

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

Затем Magento 2 использует новый механизм, называемый разделами, для обработки данных на внешнем интерфейсе и обновления конкретных блоков, которые необходимо обновить, вы можете узнать больше о разделах в этом разделе вопросов и ответов: /magento//a/ 143381/2380

РЕДАКТИРОВАТЬ относительно второй части моего ответа: как указано Максом в комментарии, разделы используются только с данными конкретного клиента, и использование этой функции вместо каждого вызова AJAX не является правильным решением.

Рафаэль в цифровом пианизме
источник
Да, я также использую JSON для переноса результата, но я упрощаю свои занятия для цели вопроса;) Но я не знаю об этой функции раздела, кажется, это правильный способ сделать то, что я хочу. Я посмотрю на это. Я подожду, если будет другой ответ, иначе я проверю ваш ответ. Спасибо !
Маттео Джеффри
2
Я согласен с использованием ответа Json вместо необработанных данных HTML. Но вторая часть вашего ответа не совсем верна. Обратите внимание, что разделы о клиентах, использующие только пользовательские данные и использующие эту функциональность вместо каждого вызова Ajax, не являются хорошей идеей.
Макс
2
@ Matthéo да, я понял :) Мой комментарий адресован Рафаэлю за исправление ответа, поскольку другие пользователи могут неправильно понять вторую часть ответа.
Макс
1
@MaxStsepantsevich спасибо, что заметили это, я отредактировал свой ответ, чтобы отразить то, что вы сказали
Рафаэль на Digital
1
Я добавил ответ, используя ваши отзывы. Спасибо вам двоим за вашу помощь.
Маттео Джеффри
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()

В зависимости от этого вот последний класс контроллера:

<?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);
    }
}
Маттео Джеффри
источник