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 yourlayout="admin- 2columns-left"
has a lot of excessive space and after testing, I can safely say, you should change that tolayout="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!