Dynamic fields in system configuration in Magento 2

Hi all,

Today this post is all about the dynamic field in the system config area

Recently someone raised the question that How TO Add a TextArea in Dynamic Rows in System.xml

So today I will add a snippet which will helpful for adding any type of field using the renderer functionality.

TODO
  • Create system.xml file
  • Create Frontend model in Block section
  • Create field renderer file like select box or textbox
Path: app/code/Nilesh/Prefech/etc/adminhtml/system.xml
<?xml version="1.0" encoding="UTF-8"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Config:etc/system_file.xsd">
    <system>
        <section id="web">
            <group id="resource_hints" translate="label comment" sortOrder="35" showInDefault="1" showInWebsite="1" showInStore="1">
                <label>Resource Hints</label>
                <comment>Comment</comment>
                <field id="config" translate="label comment" sortOrder="0" showInDefault="1" showInWebsite="1" showInStore="1">
                    <label>Configure</label>
                    <comment>Your comment</comment>
                    <frontend_model>Nilesh\Prefech\Block\System\Config\Form\Field\FieldArray</frontend_model>
                    <backend_model>Magento\Config\Model\Config\Backend\Serialized\ArraySerialized</backend_model>
                </field>
            </group>
        </section>
    </system>
</config>

Path: app/code/Nilesh/Prefech/Block/System/Config/Form/Field/FieldArray.php
<?php

namespace Nilesh\Prefech\Block\System\Config\Form\Field;

use Magento\Config\Block\System\Config\Form\Field\FieldArray\AbstractFieldArray;

class FieldArray extends AbstractFieldArray
{
    /** @var array $_columns */
    protected $_columns = [];

    /** @var bool $_addAfter */
    protected $_addAfter = true;

    /** @var $_addButtonLabel */
    protected $_addButtonLabel;

    /** @var $resourceTypeRenderer */
    private $resourceTypeRenderer;

    /** @var $preloadTypeRenderer */
    private $preloadTypeRenderer;

    /** @var $textAreaType */
    private $textAreaType;

    /**
     * FieldArray Constructor
     */
    protected function _construct()
    {
        parent::_construct();
        $this->_addButtonLabel = __('Add Resource Hint');
    }

    /**
     * @return \Magento\Framework\View\Element\BlockInterface
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    private function listResourceHintTypes()
    {
        if (!$this->resourceTypeRenderer) {
            $this->resourceTypeRenderer = $this->getLayout()->createBlock(
                '\Nilesh\Prefech\Block\Adminhtml\Form\Field\ResourceHintType',
                '',
                ['data' => ['is_render_to_js_template' => true]]
            );
        }

        return $this->resourceTypeRenderer;
    }

    /**
     * @return \Magento\Framework\View\Element\BlockInterface
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    private function listPreloadTypes()
    {
        if (!$this->preloadTypeRenderer) {
            $this->preloadTypeRenderer = $this->getLayout()->createBlock(
                '\Nilesh\Prefech\Block\Adminhtml\Form\Field\PreloadType',
                '',
                ['data' => ['is_render_to_js_template' => true]]
            );
        }

        return $this->preloadTypeRenderer;
    }

    /**
     * @return \Magento\Framework\View\Element\BlockInterface
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    private function textAreaTypes()
    {
        if (!$this->textAreaType) {
            $this->textAreaType = $this->getLayout()->createBlock(
                '\Nilesh\Prefech\Block\Adminhtml\Form\Field\Textarea',
                ''
            );
        }

        return $this->textAreaType;
    }

    /**
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    protected function _prepareToRender()
    {
        $this->addColumn(
            'type',
            [
                'label'    => __('Type'),
                'renderer' => $this->listResourceHintTypes(),
            ]
        );
        $this->addColumn(
            'resource',
            [
                'label' => __('Resource')
            ]
        );
        $this->addColumn(
            'sort_order',
            [
                'label' => __('Sort Order'),
                'renderer' => $this->textAreaTypes()
            ]
        );
        $this->addColumn(
            'preload_as',
            [
                'label' => __('Preload as'),
                'renderer' => $this->listPreloadTypes()
            ]
        );
        $this->_addAfter       = false;
    }

    /**
     * @param \Magento\Framework\DataObject $row
     *
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    protected function _prepareArrayRow(\Magento\Framework\DataObject $row)
    {
        $type    = $row->getType();
        $options = [];

        if ($type) {
            $options['option_' . $this->listResourceHintTypes()->calcOptionHash($type)] = 'selected="selected"';
        }

        $preloadAs = $row->getPreloadAs();

        if ($preloadAs) {
            $options['option_' . $this->listPreloadTypes()->calcOptionHash($preloadAs)] = 'selected="selected"';
        }

        $row->setData('option_extra_attrs', $options);
    }

    /**
     * @param string $columnName
     *
     * @return string
     * @throws \Exception
     */
    public function renderCellTemplate($columnName)
    {
        if ($columnName == "type") {
            $this->_columns[$columnName]['class'] = 'input-select required-entry';
        }

        if ($columnName == 'resource') {
            $this->_columns[$columnName]['class'] = 'input-text required-entry';
            $this->_columns[$columnName]['style'] = 'width: 200px';
        }

        if ($columnName == 'sort_order') {
            $this->_columns[$columnName]['class'] = 'input-text required-entry validate-number';
            $this->_columns[$columnName]['style'] = 'width: 50px';
        }

        if ($columnName == "preload_as") {
            $this->_columns[$columnName]['class'] = 'input-select';
        }

        return parent::renderCellTemplate($columnName);
    }
}

Path: app/code/Nilesh/Prefech/Block/Adminhtml/Form/Field/Textarea.php
<?php

namespace Nilesh\Prefech\Block\Adminhtml\Form\Field;

// use Magento\Framework\View\Element\Context;

class Textarea extends \Magento\Framework\View\Element\Template
{
    /**
     * @return string
     */
    public function _toHtml()
    {
        $inputName = $this->getInputName();
        $columnName = $this->getColumnName();
        $column = $this->getColumn();

        return '<textarea id="' . $this->getInputId().'" name="' . $inputName . '" ' .
            ($column['size'] ? 'size="' . $column['size'] . '"' : '') . ' class="' .
            (isset($column['class']) ? $column['class'] : 'input-text') . '"'.
            (isset($column['style']) ? ' style="'.$column['style'] . '"' : '') . '></textarea>';
    }
}

Path: app/code/Nilesh/Prefech/Block/Adminhtml/Form/Field/PreloadType.php
<?php

namespace Nilesh\Prefech\Block\Adminhtml\Form\Field;

use Magento\Framework\View\Element\Context;
use Magento\Framework\View\Element\Html\Select;

class PreloadType extends Select
{
    /** @var array $_options */
    protected $_options = [
        'script'   => 'Script',
        'style'    => 'Style',
        'font'     => 'Font',
        'image'    => 'Image',
        'fetch'    => 'Fetch/XHR',
        'document' => 'Document'
    ];

    /**
     * PreloadType constructor.
     *
     * @param Context $context
     * @param array   $data
     */
    public function __construct(
        Context $context,
        array $data = []
    ) {
        parent::__construct($context, $data);
    }

    /**
     * @return string
     */
    public function _toHtml()
    {
        if (!$this->getOptions()) {
            foreach ($this->_options as $value => $label) {
                $this->addOption($value, $label);
            }
        }

        $this->setClass('input-select required-entry');
        $this->setExtraParams('style="width: 100px;"');

        return parent::_toHtml();
    }

    /**
     * @param $value
     *
     * @return mixed
     */
    public function setInputName($value)
    {
        return $this->setName($value);
    }
}

That’s it

This snippet is not complete as my aim was to give an idea

Please refer this link for more – https://daan.dev/magento-2-extensions/resource-hints/

Any question just comment below

Happy Coding

You all are the semicolon to my statements; Please support me; Thanks