Як візуалізувати 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>

Найкращою практикою ІМО, здається, є Шлях 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 з кількома контейнерами, блоком тощо)? create -> 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 використовує новий механізм, який називається розділами, для обробки даних на передній панелі та оновлення конкретних блоків, які потрібно оновити, ви можете дізнатися більше про розділи в цьому запитанні: https://magento.stackexchange.com/a/ 143381/2380

EDIT щодо другої частини моєї відповіді: як зазначено Максом у коментарі, розділи використовуються лише з конкретними даними клієнта, і використання цієї функціональності замість кожного дзвінка AJAX не є правильним рішенням.


Так, я також використовую JSON для транспортування результату, але я спрощую свої класи з метою питання;) Але я не знаю цієї функції розділу, здається, це правильний спосіб робити те, що я хочу. Я погляну на це. Я зачекаю, якщо є якась інша відповідь, інакше я підтверджую вашу відповідь. Дякую !
Маттео Джеффрай

2
Я погоджуюся з використанням відповіді Json замість необроблених даних HTML. Але друга частина вашої відповіді не є повною правильною. Зауважте, що розділи клієнтів, які використовують лише для конкретних даних про клієнта та використовують цю функціональність замість кожного дзвінка Ajax, не є хорошою ідеєю.
Макс

2
@ Matthéo так, я зрозумів :) Мій коментар був адресований Рафаелю для виправлення відповіді, оскільки друга частина відповіді може бути неправильно зрозуміла іншими користувачами.
Макс

1
@MaxStsepantsevich дякую, що помітили, що я відредагував свою відповідь, щоб відобразити те, що ви сказали
Рафаель в Digital Pianism

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);
    }
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.