วิธีแสดง 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

แก้ไขเกี่ยวกับส่วนที่สองของคำตอบของฉัน: ตามที่ระบุไว้โดย Max ในความคิดเห็นส่วนจะใช้กับข้อมูลเฉพาะลูกค้าและใช้ฟังก์ชันนี้แทนการโทร AJAX ทุกครั้งไม่ใช่วิธีที่ถูกต้อง


ใช่ฉันยังใช้ JSON เพื่อการขนส่งผลลัพธ์ แต่ฉันทำให้ชั้นเรียนของฉันง่ายขึ้นเพื่อจุดประสงค์ของคำถาม;) แต่ฉันไม่ทราบคุณลักษณะของส่วนนั้นดูเหมือนว่าจะเป็นวิธีที่เหมาะสมในการทำสิ่งที่ฉันต้องการ ฉันจะดูมัน ฉันจะรอถ้ามีคำตอบอื่น ๆ มิฉะนั้นฉันจะตรวจสอบคำตอบของคุณ ขอบคุณมาก!
Matthéo Geoffray

2
ฉันเห็นด้วยกับการใช้ Json response แทนที่จะเป็นข้อมูลดิบ html แต่คำตอบส่วนที่สองของคุณไม่ถูกต้อง โปรดทราบว่าส่วนลูกค้าที่ใช้สำหรับข้อมูลเฉพาะลูกค้าเท่านั้นและการใช้ฟังก์ชันนี้แทนการโทรอาแจ็กซ์ทุกครั้งไม่ใช่ความคิดที่ดี
Max

2
@ Matthéoใช่ฉันเข้าใจแล้ว :) ความคิดเห็นของฉันส่งถึง Raphael เพื่อแก้ไขคำตอบเพราะส่วนที่สองของคำตอบสามารถเข้าใจผิดโดยผู้ใช้รายอื่น
Max

1
@ MaxStsepantsevich ขอบคุณที่ทราบว่าฉันได้แก้ไขคำตอบของฉันเพื่อสะท้อนสิ่งที่คุณพูด
Raphael ที่ Digital Pianism

1
ฉันเพิ่มคำตอบโดยใช้ความคิดเห็นของคุณ ขอบคุณสองสำหรับความช่วยเหลือของคุณ
Matthéo Geoffray

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);
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.