Add Dynamic Rows to Custom Widget – Magento2 Guide

dynamic rowsmagento2widgetwidgets

I need to add dynamic rows into the custom-made-widget, without uiComponent.
I have a system.xml with the block:

<section id="general" type="text">
   <group id="quantity_ranges" translate="label" type="text" sortOrder="10" showInDefault="1" showInWebsite="1" showInStore="1">
       <label>Quantity Ranges</label>
         <field id="ranges" translate="label" sortOrder="0" showInDefault="1" showInWebsite="1" showInStore="1">
             <label>Ranges</label>
                <frontend_model>Vendor\module\Block\Adminhtml\Form\Field\DynamicRows</frontend_model>
...

and the Block:
Vendor\module\Block\Adminhtml\Form\Field\DynamicRows

class DynamicRows extends AbstractFieldArray
{
    public function _prepareToRender()
    {
        $this->addColumn('sku', ['label' => __('Sku'), 'class' => 'required-entry']);
        $this->addColumn('position', ['label' => __('Position'), 'class' => 'required-entry']);

        $this->_addAfter = false;
        $this->_addButtonLabel = __('Add');
    }
} 

and it works. BUT, I need this into the widget, which does NOT work:

<parameter name="hotspots" xsi:type="block" required="false" visible="true">
  <label translate="true">Hotspots products</label>
  <block class="Vendor\Module\Block\Adminhtml\Form\Field\Hospot" />
</parameter>

block:
\Vendor\Module\Block\Adminhtml\Form\Field\Hospot

class Hospot extends Template
{
    public function prepareElementHtml(AbstractElement $element): AbstractElement
    {
        // Create the dynamics rows
        $dynamicRows = $this->getLayout()->createBlock(AbstractFieldArray::class);
        $dynamicRows->addColumn('label', ['label' => __('Sku')]);
        $dynamicRows->addColumn('position', ['label' => __('Position')]);

        $element->setData('after_element_html', $dynamicRows->toHtml() . "<script>require(['mage/adminhtml/browser']);</script>");

        return $element;
    }
}

Has anyone had faced this particular case?

Best Answer

In your new Vendor\Module\Block\Adminhtml\Form\Field\Hospot class you're trying to create a block from an abstract class. This is not possible. Instead, you should use a separate class that is instantiated from the given abstract class. And additionally, you have to set an $element for this new class. So here is how your Vendor\Module\Block\Adminhtml\Form\Field\Hospot class should look like:

<?php

namespace Vendor\Module\Block\Adminhtml\Form\Field;

use Magento\Framework\Data\Form\Element\AbstractElement;
use Magento\Framework\View\Element\Template;

class Hotspot extends Template
{
    public function prepareElementHtml(AbstractElement $element)
    {
        $dynamicRows = $this->getLayout()
            ->createBlock(
                DynamicRows::class
            )->setElement(
                $element
            );

        $element->setData('after_element_html', $dynamicRows->toHtml() . "<script>require(['mage/adminhtml/browser']);</script>");

        return $element;
    }
}

BUT! When trying to save the widget you will be getting an error. Because in Magento\Widget\Model\Widget::getDirectiveParam() Magento tries to implode the array of the widget settings. And since the dynamic rows value is a multi-level array, it cannot be converted to a string using the implode() function. So probably you will need to add a couple of class plugins to convert the value to a string.

Related Topic