Cara merender HTML dengan AJAX di Magento 2

12

Saya mencoba mencari cara terbaik untuk membuat HTML melalui AJAX di Magento 2.

Cara 1: Menggunakan Pengontrol tanpa tata letak

Mengajukan 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());
    }
}   

Cara 2: Menggunakan Kontroler dengan tata letak khusus

Mengajukan 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();
    }
}    

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

Mengajukan 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 praktik terbaik tampaknya menjadi Way 2 karena memisahkan logika dari Controller.
Tetapi masalah dengan Way 2 adalah bahwa <body>dan <head>dengan CSS/ JSdihasilkan sehingga bukan HTML yang dibersihkan sepenuhnya dengan hanya templat blok saya di dalamnya.

  • Apakah saya menggunakan tata letak kustom dengan cara yang salah?
  • Apakah Jalan 1 dianggap sebagai praktik yang baik?
  • Apakah ada cara lain untuk melakukan itu?
Matthéo Geoffray
sumber

Jawaban:

18

Saya juga akan pergi jalan 2 dan, memang, Anda benar-benar dapat membuat HTML "murni" melalui AJAX tanpa kepala, tubuh, css dan sebagainya.

Caranya adalah dengan:

  • beri tahu pengontrol Anda untuk memulai Respons yang bertipe \Magento\Framework\View\Result\Layoutdaripada\Magento\Framework\View\Result\Page
  • gunakan file XML layout dengan simpul akar yang <layout...>...</layout>bukan<page...>...</page>

Berikut ini adalah implementasi yang sangat sederhana.

Pengontrol

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

Tata letak

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

Contoh di Github

Lihat contoh modul ini: https://github.com/herveguetin/Herve_AjaxLayout_M2

Modul ini menghasilkan ini:

masukkan deskripsi gambar di sini

Hervé Guétin
sumber
Bagaimana Jika saya ingin memuat seluruh tata letak (XML dengan beberapa wadah, blokir dll)? buat -> toHtml, dan kirim via json ke ajax?
mattkrupnik
5

Di luar kotak, Magento tidak menggunakan metode apa pun untuk merender HTML melalui AJAX.

Dari apa yang saya lihat, setiap kali hal seperti itu perlu dilakukan, JSON digunakan untuk mengangkut hasilnya.

Contoh dari Magento/Checkout/Controller/Cart/Add:

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

Kemudian Magento 2 menggunakan mekanisme baru yang disebut bagian, untuk menangani data di frontend dan memperbarui blok spesifik yang perlu diperbarui, Anda dapat mempelajari lebih lanjut tentang bagian dalam T&J ini: /magento//a/ 143381/2380

EDIT mengenai bagian kedua dari jawaban saya: seperti yang dinyatakan oleh Max dalam komentar, bagian hanya digunakan dengan data spesifik pelanggan dan menggunakan fungsi ini sebagai ganti setiap panggilan AJAX bukanlah solusi yang tepat.

Raphael di Digital Pianism
sumber
Ya saya juga menggunakan JSON untuk mengangkut hasil tetapi saya menyederhanakan kelas saya untuk tujuan pertanyaan;) Tapi saya tidak mengetahui fitur bagian itu, tampaknya menjadi cara yang tepat untuk melakukan apa yang saya inginkan. Saya akan melihatnya. Saya akan menunggu jika ada tanggapan lain jika tidak saya akan memvalidasi jawaban Anda. Terima kasih!
Matthéo Geoffray
2
Saya setuju dengan menggunakan respons Json alih-alih data html mentah. Tetapi bagian kedua dari jawaban Anda tidak sepenuhnya benar. Perhatikan, bahwa bagian pelanggan hanya menggunakan data khusus pelanggan dan menggunakan fungsi ini sebagai ganti setiap panggilan Ajax bukanlah ide yang baik.
Maks
2
@ Matthéo ya, saya mengerti :) Komentar saya ditujukan kepada Raphael untuk mengoreksi jawaban, karena jawaban kedua dapat disalahpahami oleh pengguna lain.
Maks
1
@MaxStsepantsevich terima kasih telah mengetahui hal itu, saya telah mengedit jawaban saya untuk mencerminkan apa yang Anda katakan
Raphael di Digital Pianism
1
Saya menambahkan jawaban menggunakan umpan balik Anda. Terima kasih atas bantuan Anda.
Matthéo Geoffray
3

Dalam contoh saya, saya tidak dapat menggunakan sectionskarena tidak customer datadan tidak setelah PUT/ POSTtindakan tetapi menggunakan Raphael at Digital Pianismjawaban saya tahu bagaimana Magento membuat bagian.

Jika kita mengambil contoh cartbagian itu menggunakan metode \Magento\Customer\CustomerData\SectionPool::getSectionDataByNamesuntuk mengambil data dari bagian. Ini membawa kita ke \Magento\Checkout\CustomerData\Cart::getSectionDatadengan array tunggal yang mengandung area bagian, termasuk$this->layout->createBlock('Magento\Catalog\Block\ShortcutButtons')->toHtml()

Bergantung pada itu di sini adalah kelas Controller terakhir:

<?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);
    }
}
Matthéo Geoffray
sumber