Reading list Switch to dark mode

    Magento 2 Development 22: Multi tab form

    Updated 12 March 2024

    Till now we have seen two ways to create form. There are multiple ways to create form and there are multiple types of form that can be created.

    In this blog, we will see how to create a multi-tab form as you see in the customer edit section. You can create this form with ui component or the widget. Here I will create it with the widget. But I will advise you to create the multi-tab structure with the ui component as an exercise.


    First of all, we will edit the layout file view/adminhtml/layout/blogmanager_manage_edit.xml

    Updated code for view/adminhtml/layout/blogmanager_manage_edit.xml file

    <?xml version="1.0"?>
    <page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="admin-2columns-left" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
        <body>
            <referenceContainer name="left">
                <block class="Webkul\BlogManager\Block\Adminhtml\Blog\Edit\Tabs" name="blog_edit_tabs"/>
            </referenceContainer>
            <referenceContainer name="content">
                <block class="Webkul\BlogManager\Block\Adminhtml\Blog\Edit" name="blog_edit"/>
            </referenceContainer>
        </body>
    </page>

    Since we will be using 2 columns layout, one for the tab and the other for the form itself, we need to specify the layout type as admin-2columns-left. In the left column, we have added the tabs block and in the right or the main column, we have added the main form class which is the same as before.

    Searching for an experienced
    Magento 2 Company ?
    Find out More

    We will keep the Block/Adminhtml/Blog/Edit.php class as same as before. But we have to edit the form class Block/Adminhtml/Blog/Edit/Form.php as shown below

    Updated code for Block/Adminhtml/Blog/Edit/Form.php file

    <?php
    namespace Webkul\BlogManager\Block\Adminhtml\Blog\Edit;
    
    class Form extends \Magento\Backend\Block\Widget\Form\Generic
    {
        /**
         * Prepare form data
         *
         * @return \Magento\Backend\Block\Widget\Form
         */
        protected function _prepareForm()
        {
            $form = $this->_formFactory->create(
                ['data' => [
                                'id' => 'edit_form',
                                'enctype' => 'multipart/form-data',
                                'action' => $this->getData('action'),
                                'method' => 'post'
                            ]
                ]
            );
            $form->setUseContainer(true);
            $this->setForm($form);
            return parent::_prepareForm();
        }
    }

    As you can see we have removed all the fields from this class. And we have kept only the form attributes that is because this class will act as the main form. And in this main form, we will attach the fields related to each tab. Also, note that the form id is edit_form.

    Now let’s create the tabs block class as, Block/Adminhtml/Blog/Edit/Tabs.php

    <?php
    namespace Webkul\BlogManager\Block\Adminhtml\Blog\Edit;
    
    class Tabs extends \Magento\Backend\Block\Widget\Tabs
    {
        /**
         * @var \Magento\Framework\Registry
         */
        protected $_coreRegistry;
    
        /**
         * Dependency Initilization
         *
         * @param \Magento\Backend\Block\Template\Context $context
         * @param \Magento\Framework\Json\EncoderInterface $jsonEncoder
         * @param \Magento\Backend\Model\Auth\Session $authSession
         * @param \Magento\Framework\Registry $coreRegistry
         * @param array $data
         */
        public function __construct(
            \Magento\Backend\Block\Template\Context $context,
            \Magento\Framework\Json\EncoderInterface $jsonEncoder,
            \Magento\Backend\Model\Auth\Session $authSession,
            \Magento\Framework\Registry $coreRegistry,
            array $data = []
        ) {
            $this->_coreRegistry = $coreRegistry;
            parent::__construct($context, $jsonEncoder, $authSession, $data);
        }
    
        /**
         * Dependency Initilization
         *
         * @return void
         */
        protected function _construct()
        {
            parent::_construct();
            $this->setId('blog_tabs');
            $this->setDestElementId('edit_form');
            $this->setTitle(__('Blog Data'));
        }
    
        /**
         * Prepare form data
         *
         * @return \Magento\Backend\Block\Widget\Form
         */
        protected function _prepareLayout()
        {
            $this->addTab(
                'main',
                [
                    'label' => __('Blog Data'),
                    'content' => $this->getLayout()->createBlock(
                        'Webkul\BlogManager\Block\Adminhtml\Blog\Edit\Tab\Main'
                    )->toHtml(),
                    'active' => true
                ]
            );
    
            $this->addTab(
                'status',
                [
                    'label' => __('Blog Status'),
                    'content' => $this->getLayout()->createBlock(
                        'Webkul\BlogManager\Block\Adminhtml\Blog\Edit\Tab\Status'
                    )->toHtml()
                ]
            );
    
            return parent::_prepareLayout();
        }
    }

    Now here you can see with the setDestElementId method we have set the destination element for the tabs output as edit_form which is the id of the form that we created earlier.

    In the _prepareLayout function we can add the tabs. The code for adding the tab is very self-explanatory. We have given an id to the tab and added a label. We have also mentioned which tab should be active when we first load the page. In the content param, we have mentioned the block class that will produce the HTML content for this tab.

    The important thing to mention here is that we can also load Ajax content in the tab. And this is very common practice when we show data in grid form in a tab. The syntax for the tab with Ajax data is given below,

    $this->addTab(
        'tab_id',
        [
            'label' => __('Tab Label'),
            'url' => $this->getUrl('frontname/controller/action', ['param' => $value]),
            'class' => 'ajax'
        ]
    );

    Instead of content, we are using url param and we pass the Ajax URL to load. We can also pass some param if we want to load the relevant data.
    One use case in our scenario can be if we show the comments of a blog in a grid. There we will pass the blog id in param and load the comments with Ajax.

    Let’s see how we can create the two tab classes. First, we will create the main tab as Block/Adminhtml/Blog/Edit/Tab/Main.php

    <?php
    namespace Webkul\BlogManager\Block\Adminhtml\Blog\Edit\Tab;
    
    class Main extends \Magento\Backend\Block\Widget\Form\Generic
    {
        /**
         * @var \Webkul\BlogManager\Model\Blog\Status
         */
        protected $blogStatus;
    
        /**
         * Dependency Initilization
         *
         * @param \Magento\Backend\Block\Template\Context $context
         * @param \Magento\Framework\Registry $registry
         * @param \Magento\Framework\Data\FormFactory $formFactory
         * @param \Webkul\BlogManager\Model\Blog\Status $blogStatus
         * @param array $data
         */
        public function __construct(
            \Magento\Backend\Block\Template\Context $context,
            \Magento\Framework\Registry $registry,
            \Magento\Framework\Data\FormFactory $formFactory,
            \Webkul\BlogManager\Model\Blog\Status $blogStatus,
            array $data = []
        ) {
            $this->blogStatus = $blogStatus;
            parent::__construct($context, $registry, $formFactory, $data);
        }
    
        /**
         * Prepare form data
         *
         * @return \Magento\Backend\Block\Widget\Form
         */
        protected function _prepareForm()
        {
            $model = $this->_coreRegistry->registry('blog_data');
    
            $form = $this->_formFactory->create();
    
            $form->setHtmlIdPrefix('blogmanager_');
    
            $fieldset = $form->addFieldset(
                'base_fieldset',
                ['legend' => __('Edit Blog'), 'class' => 'fieldset-wide']
            );
    
            $fieldset->addField('entity_id', 'hidden', ['name' => 'entity_id']);
    
            $fieldset->addField(
                'title',
                'text',
                [
                    'name' => 'title',
                    'label' => __('Title'),
                    'id' => 'title',
                    'title' => __('Title'),
                    'class' => 'required-entry',
                    'required' => true,
                ]
            );
    
            $fieldset->addField(
                'content',
                'textarea',
                [
                    'name' => 'content',
                    'label' => __('Content'),
                    'id' => 'content',
                    'title' => __('Content'),
                    'class' => 'required-entry',
                    'required' => true,
                ]
            );
            
            $form->setValues($model->getData());
            $this->setForm($form);
    
            return parent::_prepareForm();
        }
    }

    It is very similar to what we saw in the previous blog. There are only two differences. We have removed the $form->setUseContainer(true); line from the code because it will create a nested form. Also, you can see while creating the form we have not passed the data param as we did earlier. That is because we are using the main form itself.

    Now let’s create the other tab, Block/Adminhtml/Blog/Edit/Tab/Status.php

    Code for Block/Adminhtml/Blog/Edit/Tab/Status.php file

    <?php
    namespace Webkul\BlogManager\Block\Adminhtml\Blog\Edit\Tab;
    
    class Status extends \Magento\Backend\Block\Widget\Form\Generic
    {
        /**
         * @var \Webkul\BlogManager\Model\Blog\Status
         */
        protected $blogStatus;
    
        /**
         * Dependency Initilization
         *
         * @param \Magento\Backend\Block\Template\Context $context
         * @param \Magento\Framework\Registry $registry
         * @param \Magento\Framework\Data\FormFactory $formFactory
         * @param \Webkul\BlogManager\Model\Blog\Status $blogStatus
         * @param array $data
         */
        public function __construct(
            \Magento\Backend\Block\Template\Context $context,
            \Magento\Framework\Registry $registry,
            \Magento\Framework\Data\FormFactory $formFactory,
            \Webkul\BlogManager\Model\Blog\Status $blogStatus,
            array $data = []
        ) {
            $this->blogStatus = $blogStatus;
            parent::__construct($context, $registry, $formFactory, $data);
        }
    
        /**
         * Prepare form data
         *
         * @return \Magento\Backend\Block\Widget\Form
         */
        protected function _prepareForm()
        {
           
            $model = $this->_coreRegistry->registry('blog_data');
    
            $form = $this->_formFactory->create();
    
            $form->setHtmlIdPrefix('blogmanager_');
    
            $fieldset = $form->addFieldset(
                'base_fieldset',
                ['legend' => __('Edit Blog'), 'class' => 'fieldset-wide']
            );
    
            $fieldset->addField(
                'status',
                'select',
                [
                    'name' => 'status',
                    'label' => __('Status'),
                    'options' => [0=>__('Disabled'), 1=>__('Enabled')],
                    'id' => 'status',
                    'title' => __('Status'),
                    'class' => 'required-entry',
                    'required' => true,
                ]
            );
    
            $form->setValues($model->getData());
            $this->setForm($form);
    
            return parent::_prepareForm();
        }
    }

    It is essentially the same as the main class. So no explanation is needed here.

    Now when you click on edit, you should see something like,

    ezgif.com-gif-maker-5


    Note that sometimes you may want to create a custom form with a custom design. You can do that too in Magento by creating your own form with phtml file. I will not cover it but you can easily find it in many blogs and follow along.


    Next Blog -> Magento 2 Development 23: Add Button

    Previous Blog -> Magento 2 Development 21: Form Widget

    . . .

    Leave a Comment

    Your email address will not be published. Required fields are marked*


    Be the first to comment.

    Back to Top

    Message Sent!

    If you have more details or questions, you can reply to the received confirmation email.

    Back to Home