Magento – magento 2 – backend page with tab and form on custom module

backendmagento2modulePHP

I created my custom module for backend, I created the controller, I added the link on standard menu and the acl configuration, all these part works fine.

Now I'm trying to add a tab list on the left with a simple form to allow one image upload. But I have some problem with the creation of the block in the tab panel.

Here my code:

<vendor>\<module>\Block\Adminhtml\StoreLocator\Edit.php

<?php

namespace <vendor>\<module>\Block\Adminhtml\StoreLocator;

use Magento\Backend\Block\Widget\Form\Container;

class Edit extends Container
{
/**
 * Core registry
 *
 * @var \Magento\Framework\Registry
 */
protected $_coreRegistry = null;

/**
 * @param \Magento\Backend\Block\Widget\Context $context
 * @param \Magento\Framework\Registry $registry
 * @param array $data
 */
public function __construct(
    \Magento\Backend\Block\Widget\Context $context,
    \Magento\Framework\Registry $registry,
    array $data = []
) {
    $this->_coreRegistry = $registry;
    parent::__construct($context, $data);
}

/**
 * Department edit block
 *
 * @return void
 */
protected function _construct()
{
    $this->_objectId = 'entity_id';
    $this->_blockGroup = '<vendor>_<module>';
    $this->_controller = 'adminhtml_settings';

    parent::_construct();

    if ($this->_isAllowedAction('<vendor>_<module>::save')) {
        $this->buttonList->update('save', 'label', __('Salva impostazioni'));
        /*$this->buttonList->add(
            'saveandcontinue',
            [
                'label' => __('Salva e continua modifica'),
                'class' => 'save',
                'data_attribute' => [
                    'mage-init' => [
                        'button' => [
                            'event' => 'saveAndContinueEdit',
                            'target' => '#edit_form'
                        ],
                    ],
                ]
            ],
            -100
        );*/
    } else {
        $this->buttonList->remove('save');
    }
}

/**
 * Get header with Department name
 *
 * @return \Magento\Framework\Phrase
 */
public function getHeaderText()
{
    if ($this->_coreRegistry->registry('store_locator')->getId()) {
        return __("Modifica categorie ispirazione '%1'", $this->escapeHtml($this->_coreRegistry->registry('store_locator')->getTypeCode()));
    } else {
        return __('Modifica categorie ispirazione');
    }
}

/**
 * Check permission for passed action
 *
 * @param string $resourceId
 * @return bool
 */
protected function _isAllowedAction($resourceId)
{
    return $this->_authorization->isAllowed($resourceId);
}

/**
 * Getter of url for "Save and Continue" button
 * tab_id will be replaced by desired by JS later
 *
 * @return string
 */
protected function _getSaveAndContinueUrl()
{
    return $this->getUrl('setting/storelocator/save', ['_current' => true, 'back' => 'edit', 'active_tab' => '']);
}

}

Then my tab class:

<vendor>\<module>\Block\Adminhtml\StoreLocator\Edit\Tabs.php

<?php

namespace <vendor>\<module>\Block\Adminhtml\StoreLocator\Edit;

use Magento\Backend\Block\Widget\Tabs as WidgetTabs;

class Tabs extends WidgetTabs
{
/**
 * Class constructor
 *
 * @return void
 */
protected function _construct()
{
    parent::_construct();
    $this->setId('locator_edit_tabs');
    $this->setDestElementId('edit_form');
    $this->setTitle(__('Impostazioini pagina ispirazioni'));
}

/**
 * @return $this
 */
protected function _beforeToHtml()
{
    $this->addTab(
        'ispirazione',
        [
            'label' => __('Ispirazione'),
            'title' => __('Ispirazione'),
            'content' => $this->getLayout()->createBlock(
                '<vendor>\<module>\Block\Adminhtml\StoreLocator\Edit\Tab\StoreLocator'
            )->toHtml(),
            'active' => true
        ]
    );

    return parent::_beforeToHtml();
}
}

Then my form class:

<vendor>\<module>\Block\Adminhtml\StoreLocator\Edit\Form.php

<?php

namespace <vendor>\<module>\Block\Adminhtml\StoreLocator\Edit;

use \Magento\Backend\Block\Widget\Form\Generic;

class Form extends Generic
{
/**
 * @var \Magento\Store\Model\System\Store
 */
protected $_systemStore;

/**
 * @param \Magento\Backend\Block\Template\Context $context
 * @param \Magento\Framework\Registry $registry
 * @param \Magento\Framework\Data\FormFactory $formFactory
 * @param \Magento\Store\Model\System\Store $systemStore
 * @param array $data
 */
public function __construct(
    \Magento\Backend\Block\Template\Context $context,
    \Magento\Framework\Registry $registry,
    \Magento\Framework\Data\FormFactory $formFactory,
    \Magento\Store\Model\System\Store $systemStore,
    array $data = []
) {
    $this->_systemStore = $systemStore;
    parent::__construct($context, $registry, $formFactory, $data);
}

/**
 * Init form
 *
 * @return void
 */
protected function _construct()
{
    parent::_construct();
    $this->setId('locator_form');
    $this->setTitle(__('Impostazioini pagina ispirazioni'));
}

/**
 * Prepare form
 *
 * @return $this
 */
protected function _prepareForm()
{
    /** @var \Magento\Framework\Data\Form $form */
    $form = $this->_formFactory->create(
        [
            'data' => [
                'id' => 'edit_form',
                'action' => $this->getData('action'),
                'method' => 'post'
            ]
        ]
    );

    $form->setUseContainer(true);
    $this->setForm($form);

    return parent::_prepareForm();
}
}

Then here the class called on tabs part:

<vendor>\<module>\Block\Adminhtml\StoreLocator\Edit\Tab\StoreLocator.php

<?php

namespace <vendor>\<module>\Block\Adminhtml\StoreLocator\Edit\Tab;

use Magento\Backend\Block\Widget\Form\Generic;
use Magento\Backend\Block\Widget\Tab\TabInterface;

class StoreLocator extends Generic implements TabInterface
{

protected $_descriptionModel;

public function __construct(
    \Magento\Backend\Block\Template\Context $context,
    \Magento\Framework\Registry $registry,
    \Magento\Framework\Data\FormFactory $formFactory,
    \<vendor>\<module>\Model\StoreLocatorDescription $storeLocatorDescription,
    array $data = []
)
{
    parent::__construct($context, $registry, $formFactory, $data);
    $this->_descriptionModel = $storeLocatorDescription;
}

protected function _prepareForm()
{
    $form = $this->_formFactory->create();

    $fieldset = $form->addFieldset(
        'base_fieldset',
        [
            'legend' => __('Contenuto Store Locator'),
            'class' => 'fieldset-wide'
        ]
    );

    $fieldset->addField(
        'entity_id',
        'hidden',
        [
            'name' => 'entity_id'
        ]
    );

    $fieldset->addField(
        'description',
        'textarea',
        [
            'name' => 'description',
            'label' => __('Descrizione Store Locator'),
            'class' => 'txt-type',
            'title' => __('Descrizione Store Locator'),
            'required' => true,
            'note' => 'Inserisci la descrizione dello store locator da visualizzare nella home page.'
        ]
    );

    $fieldset->addField(
        'image_name',
        'image_name',
        [
            'title' => __('Immagine Store Locator'),
            'label' => __('Immagine Store Locator'),
            'name' => 'image_name',
            'note' => 'Formati immagini s: jpg, jpeg, png',
        ]
    );

    $this->setForm($form);
    return parent::_prepareForm();
}

/**
 * Return Tab label
 *
 * @return string
 * @api
 */
public function getTabLabel()
{
    return __('Ispirazione');
}

/**
 * Return Tab title
 *
 * @return string
 * @api
 */
public function getTabTitle()
{
    return __('Ispirazione');
}

/**
 * Can show tab in tabs
 *
 * @return boolean
 * @api
 */
public function canShowTab()
{
    return true;
}

/**
 * Tab is hidden
 *
 * @return boolean
 * @api
 */
public function isHidden()
{
    return false;
}
}

Finally the layout file:

<vendor>\<module>\view\adminhtml\layout\setting_storelocator_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-    2columns-left"    xsi:noNamespaceSchemaLocation="../../../../../../../lib/internal/Magento/Framework/View/Layout/etc/page_configuration.xsd">
<head>
    <title>
        Contenuto store locator
    </title>
</head>
<body>
    <referenceContainer name="left">
        <block class="<vendor>\<module>\Block\Adminhtml\StoreLocator\Edit\Tabs"
               name="store_locator.tabs"/>
    </referenceContainer>
    <referenceContainer name="content">
        <block class="<vendor>\<module>\Block\Adminhtml\StoreLocator\Edit" name="store_locator_edit"/>
    </referenceContainer>
</body>
</page>

I can't find what is wrong, but it doesn't work.

Best Answer

At a glance, in your <vendor>\<module>\view\adminhtml\layout\setting_storelocator_index.xml, I've noticed that your layout="admin- 2columns-left" has a lot of excessive space and after testing, I can safely say, you should change that to layout="admin-2columns-left" (i.e. remove the spaces)

That caused some unexpected behaviour in my test, that the menu block was being set to a boolean, thereby throwing an error when my Block was calling setActive. So the error message didn't help a whole lot, but I can now say that having spaces in your layout type causes interesting issues.

I hope this helps! Goodluck!

Related Topic