Let’s imagine, you need to create a category chooser for magento admin panel. For example, this might be necessary in the following cases:
1. To be used for some standard form as follows:
1 2 3 4 5 6 7 8 9 10 |
$fieldset->addField( 'category_chooser', '[Vendor]\[Modul]\Block\Adminhtml\[Chooser]', [ 'name' => 'category_chooser', 'label' => __('Categories'), 'title' => __('Categories'), 'required' => true ] ); |
2. For some backend configurations:
1 2 3 4 |
<field id="category_chooser" translate="label" sortOrder="20" showInDefault="1" showInWebsite="1" showInStore="1"> <label>Categories</label> <frontend_model>[Vendor]\[Modul]\Block\Adminhtml\[Chooser]</frontend_model> </field> |
3. Or, like in my case, for some widgets:
1 2 3 4 |
<parameter name="category_chooser" xsi:type="block" visible="true" required="true" sort_order="10"> <label translate="true">Categories</label> <block class="[Vendor]\[Modul]\Block\Adminhtml\[Chooser]" /> </parameter> |
So, let’s consider how it is possible to arrange that. Magento source code offers us several solutions but we have selected the simplest one to use. First off, we write down the code for the element itself:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
protected function _elementHtml() { $element = $this->_element; $htmlId = $element->getId(); $data = $element->getData(); $data['after_element_js'] = $this->_afterElementJs(); $data['after_element_html'] = $this->_afterElementHtml(); $data['readonly'] = 'readonly'; $objectManager = \Magento\Framework\App\ObjectManager::getInstance(); $elementFactory = $objectManager->get('\Magento\Framework\Data\Form\Element\Factory'); $htmlItem = $elementFactory->create('text', ['data' => $data]); $htmlItem ->setId("{$htmlId}") ->setForm($element->getForm()) ->addClass('required-entry') ->addClass('entities'); $return = <<<HTML <div id="{$htmlId}-container" class="chooser_container"> {$htmlItem->getElementHtml()} </div> HTML; return $return; } |
Magento Custom Development
Take your online store to the next level with BelVG Magento custom development
Visit the pageBut that is only a form’s input. Now, let’s write down some html and javascript code to output the chooser in the backend and let it perform properly:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
protected function _afterElementHtml() { $element = $this->_element; $htmlId = $element->getId(); $return = <<<HTML <a href="javascript:void(0)" onclick="MultiCategoryChooser.displayChooser('{$htmlId}-container')" class="widget-option-chooser" title="{__('Open Chooser')}"> <img src="{$this->getViewFileUrl('images/rule_chooser_trigger.gif')}" alt="{__('Open Chooser')}" /> </a> <a href="javascript:void(0)" onclick="MultiCategoryChooser.hideChooser('{$htmlId}-container')" title="{__('Apply')}"> <img src="{$this->getViewFileUrl('images/rule_component_apply.gif')}" alt="{__('Apply')}"> </a> <div class="chooser"></div> HTML; return $return; } protected function _afterElementJs() { $chooserUrl = $this->getUrl('adminhtml/widget_instance/categories', []); $element = $this->_element; $htmlId = $element->getId(); $return = <<<HTML <script> require([ 'jquery', 'Magento_Ui/js/modal/alert', "prototype" ], function (jQuery, alert) { var MultiCategoryChooser = { displayChooser : function(chooser) { chooser = $(chooser).down('div.chooser'); entities = chooser.up('div.chooser_container').down('input[type="text"].entities').value; postParameters = {selected: entities}; url = '{$chooserUrl}'; if (chooser && url) { if (chooser.innerHTML == '') { new Ajax.Request(url, { method : 'post', parameters : postParameters, onSuccess : function(transport) { try { if (transport.responseText) { Element.insert(chooser, transport.responseText); chooser.removeClassName('no-display'); chooser.show(); } } catch (e) { alert({ content: 'Error occurs during loading chooser.' }); } } }); } else { chooser.removeClassName('no-display'); chooser.show(); } } }, hideChooser : function(chooser) { chooser = $(chooser).down('div.chooser'); if (chooser) { chooser.addClassName('no-display'); chooser.hide(); } }, checkCategory : function(event) { node = event.memo.node; container = event.target.up('div.chooser_container'); value = container.down('input[type="text"].entities').value.strip(); if (node.attributes.checked) { if (value) ids = value.split(','); else ids = []; if (-1 == ids.indexOf(node.id)) { ids.push(node.id); container.down('input[type="text"].entities').value = ids.join(','); } } else { ids = value.split(','); while (-1 != ids.indexOf(node.id)) { ids.splice(ids.indexOf(node.id), 1); container.down('input[type="text"].entities').value = ids.join(','); } } } } window.MultiCategoryChooser = MultiCategoryChooser; jQuery(function() { var container = $('{$htmlId}-container'); if (container) { container.up(0).down('.control-value').hide(); } Event.observe(document, 'node:changed', function(event){ MultiCategoryChooser.checkCategory(event); }); Event.observe(document, 'category:beforeLoad', function(event) { container = event.target.up('div.chooser_container'); value = container.down('input[type="text"].entities').value.strip(); event.memo.treeLoader.baseParams.selected = value; }); }); }); </script> HTML; return $return; } |
So, this is the Multi Category Chooser. But what if we need only one category? For such case there is a ready-made solution available, so we do not need to invent anything new:
1 2 3 4 5 6 7 8 9 10 |
<parameter name="category" xsi:type="block" visible="true" required="true" sort_order="10"> <label translate="true">Category</label> <block class="Magento\Catalog\Block\Adminhtml\Category\Widget\Chooser"> <data> <item name="button" xsi:type="array"> <item name="open" xsi:type="string" translate="true">Select Category...</item> </item> </data> </block> </parameter> |
This solution I have applied for one of my widgets, but it is also possible to adapt it for configuring various form elements.
Want to keep track of your store’s unregistered users? Download our free Magento Guests Catalog extension.
Hi, Froppy!
Please turn to our support service at [email protected] and our staff will help you.
Could you please send me the code package?
Hi, Sukumar! Thanks for your question.
[Vendor]\[Modul]\Block\Adminhtml\[Chooser]
Vendor – the name of the vendor that you assign as you create a module
Module – the name of the module
Chooser – name of the class that you assigned to your chooser.
We wrote these attributes in brackets because they are individually assigned by each user; this is how we called ours, but yours could have different names.
Hi.
I am getting error when adding to custom admin form.
\Vendor\Module\Block\Adminhtml\Chooser doesn’n extend \Magento\Framework\Data\Form\Element\AbstractElement
Can you please help me to solve this?
Hi, Arnau, thanks for your question!
If you follow this path Magento\Widget\Controller\Adminhtml\Widget\Instance, you’ll find different classes for Choosers. Personally I use Category for mine.
There is another path Magento\Catalog\Block\Adminhtml\Category\Widget\Chooser.
Bear in mind that Magento is upgraded from time to time, so this aspect can change.
I hope I answered your question. Best wishes!
Hello Mishell and thanks for the post, from wich block did you extended?
Thanks!
Hi Kosta,
We see that the queries are sent to ‘adminhtml/widget_instance/categories’. It’s all in your hands. Change the sent ajax parameters (parameters : postParameters). This must help.
Hello!It’s a very good approach. Can you explain why the only available categories to choose are only the NON anchor categories?
Hi Roshan,
The article describes the creation of one file: [Vendor]\[Modul]\Block\Adminhtml\[Chooser], which is a form element. All these blocks are similar – there are plenty of examples. Do not be lazy and try to explore it yourself: \Magento\Config\Block\System\Config\*. There are three examples of adding fields to a form described at the beginning of the article. Each of the examples is used in many of the modules in default Magento.
hi
can you share the file and and file path where i create these new files
Thanks
Can you please specify the file names as suggested above because that will really help a lot for creating the new type and used in the form.
Dear Daniel,
the code is above, in the article. Just put it into your module.
In this case, we used the code from one of the client’s projects; the module where this code was created is more extensive.
Regards,
Mishel
Can you share please the source code? Thanks.
Can you please specify file name and respective paths
Nice and useful article. Thanks for sharing.