Cách kết xuất HTML bằng AJAX trong Magento 2


12

Tôi cố gắng tìm cách tốt nhất để kết xuất HTML thông qua AJAX trong Magento 2.

Cách 1: Sử dụng Trình điều khiển không có bố cục

Tập tin 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());
    }
}   

Cách 2: Sử dụng Trình điều khiển với bố cục tùy chỉnh

Tập tin 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();
    }
}    

Tập tin 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>

Tập tin 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 cách thực hành tốt nhất dường như là Cách 2 vì nó tách logic khỏi Bộ điều khiển.
Nhưng vấn đề với Cách 2<body><head>với CSS/ JSđược tạo ra nên nó không phải là một HTML được làm sạch hoàn toàn chỉ có mẫu khối của tôi trong đó.

  • Tôi đang sử dụng bố trí tùy chỉnh sai cách?
  • cách 1 được coi là một thực hành tốt?
  • Có cách nào khác để làm điều đó?

Câu trả lời:


18

Tôi cũng sẽ đi theo cách 2 và, thực sự, bạn thực sự có thể hiển thị HTML "thuần túy" thông qua AJAX mà không cần đầu, thân, css, v.v.

Bí quyết là:

  • nói với bộ điều khiển của bạn để kích hoạt một Phản hồi thuộc loại \Magento\Framework\View\Result\Layoutchứ không phải\Magento\Framework\View\Result\Page
  • sử dụng tệp XML bố trí với một nút gốc <layout...>...</layout>chứ không phải là<page...>...</page>

Đây là một thực hiện rất đơn giản.

Bộ điều khiển

<?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);
    }
}

Bố cục

<?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>

Ví dụ trên Github

Xem mô-đun ví dụ này: https://github.com/herveguetin/Herve_AjaxLayout_M2

Mô-đun này tạo ra điều này:

nhập mô tả hình ảnh ở đây


Điều gì xảy ra nếu tôi muốn tải toàn bộ bố cục (XML với một vài thùng chứa, khối, v.v.)? tạo -> toHtml và gửi qua json đến ajax?
mattkrupnik

5

Ra khỏi hộp, Magento không sử dụng bất kỳ phương pháp nào để kết xuất HTML thông qua AJAX.

Từ những gì tôi đã thấy, mỗi khi cần thực hiện điều đó, JSON được sử dụng để vận chuyển kết quả.

Ví dụ từ Magento/Checkout/Controller/Cart/Add:

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

Sau đó, Magento 2 sử dụng một cơ chế mới gọi là các phần, để xử lý dữ liệu trên frontend và cập nhật các khối cụ thể cần cập nhật, bạn có thể tìm hiểu thêm về các phần trong Q & A này: /magento//a/ 143381/2380

EDIT liên quan đến phần thứ hai trong câu trả lời của tôi: như Max đã nêu trong nhận xét, các phần chỉ được sử dụng với dữ liệu cụ thể của khách hàng và sử dụng chức năng này thay vì mỗi cuộc gọi AJAX không phải là giải pháp phù hợp.


Có, tôi cũng sử dụng JSON để vận chuyển kết quả nhưng tôi đơn giản hóa các lớp học của mình cho mục đích của câu hỏi;) Nhưng tôi không biết về tính năng phần đó, nó có vẻ là cách đúng để làm những gì tôi muốn. Tôi sẽ xem qua nó. Tôi sẽ đợi nếu có bất kỳ phản hồi nào khác nếu không tôi sẽ xác nhận câu trả lời của bạn. Cảm ơn !
Matthéo Geoffray

2
Tôi đồng ý với việc sử dụng phản hồi Json thay vì dữ liệu html thô. Nhưng phần thứ hai của câu trả lời của bạn không đầy đủ chính xác. Lưu ý rằng các phần khách hàng chỉ sử dụng cho dữ liệu dành riêng cho khách hàng và sử dụng chức năng này thay vì mỗi cuộc gọi Ajax là ý tưởng không hay.
Tối đa

2
@ Matthéo vâng, tôi hiểu :) Nhận xét của tôi gửi đến Raphael vì đã trả lời đúng, vì phần thứ hai của câu trả lời có thể bị người dùng khác hiểu nhầm.
Tối đa

1
@MaxStsepantsevich cảm ơn vì đã phát hiện ra điều đó, tôi đã chỉnh sửa câu trả lời của mình để phản ánh những gì bạn đã nói
Raphael tại Digital Pianism

1
Tôi đã thêm một câu trả lời bằng cách sử dụng thông tin phản hồi của bạn. Cảm ơn hai bạn đã giúp đỡ.
Matthéo Geoffray

3

Trong ví dụ của tôi, tôi không thể sử dụng sectionsvì nó không phải customer datavà nó không phải sau một hành động PUT/ POSTnhưng sử dụng Raphael at Digital Pianismcâu trả lời tôi đã tìm ra cách Magento kết xuất các phần.

Nếu chúng ta lấy ví dụ về cartphần thì nó sử dụng phương thức \Magento\Customer\CustomerData\SectionPool::getSectionDataByNamesđể lấy dữ liệu từ các phần. Điều này dẫn chúng ta đến \Magento\Checkout\CustomerData\Cart::getSectionDatavới một mảng duy nhất chứa các khu vực của phần, bao gồm$this->layout->createBlock('Magento\Catalog\Block\ShortcutButtons')->toHtml()

Tùy thuộc vào đây là lớp Trình điều khiển cuối cùng:

<?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);
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.