Add Category Chooser to Admin form without UI Component

Add Category Chooser to Admin form without UI Component

Its very easy to add Category chooser to UI forms. But many times we create Magento 2 admin forms without UI component, so today I am coming with a solution to add Category chooser without UI component like below screenshot:

To achieve this functionality, first you need to add the field like below:

$fieldset->addField(
'category_ids',
'\M2Expert\Categories\Block\Adminhtml\Chooser',
[
'name' => 'category_ids',
'label' => __('Categories'),
'title' => __('Categories'),
'required' => true
]
);

Next you need to create a class Chooser.php into below location

app/code/M2Expert/Categories/Block/Adminhtml/Chooser.php

Code of Chooser.php

<?php
namespace M2Expert\Categories\Block\Adminhtml;
use Magento\Catalog\Model\Category as CategoryModel;
use Magento\Framework\AuthorizationInterface;
use Magento\Framework\Data\Form\Element\CollectionFactory;
use Magento\Framework\Data\Form\Element\Factory;
use Magento\Framework\Data\Form\Element\Multiselect;
use Magento\Framework\Escaper;
use Magento\Framework\UrlInterface;
class Chooser extends Multiselect
{
public $collectionFactory;
public $authorization;
protected $_urlBuilder;
public function __construct(
Factory $factoryElement,
CollectionFactory $factoryCollection,
Escaper $escaper,
\Magento\Catalog\Model\ResourceModel\Category\CollectionFactory $collectionFactory,
AuthorizationInterface $authorization,
UrlInterface $urlBuilder,
array $data = []
) {
$this->collectionFactory = $collectionFactory;
$this->authorization = $authorization;
$this->_urlBuilder = $urlBuilder;
parent::__construct($factoryElement, $factoryCollection, $escaper, $data);
}
public function getElementHtml()
{
$html = '<div class="admin__field-control admin__control-grouped">';
$html .= '<div id="topbanner-category-select" class="admin__field" data-bind="scope:\'categoryChooser\'" data-index="index">';
$html .= '<!-- ko foreach: elems() -->';
$html .= '<input name="category_ids" data-bind="value: value" style="display: none"/>';
$html .= '<!-- ko template: elementTmpl --><!-- /ko -->';
$html .= '<!-- /ko -->';
$html .= '</div></div>';
$html .= $this->getAfterElementHtml();
return $html;
}
public function getCategoriesTree()
{
/* @var $collection \Magento\Catalog\Model\ResourceModel\Category\Collection */
$collection = $this->collectionFactory->create()->addAttributeToSelect('name')->addAttributeToSort('position','asc');
$categoryById = [
CategoryModel::TREE_ROOT_ID => [
'value'    => CategoryModel::TREE_ROOT_ID,
'optgroup' => null,
],
];
foreach ($collection as $category) {
foreach ([$category->getId(), $category->getParentId()] as $categoryId) {
if (!isset($categoryById[$categoryId])) {
$categoryById[$categoryId] = ['value' => $categoryId];
}
}
$categoryById[$category->getId()]['is_active'] = 1;
$categoryById[$category->getId()]['label'] = $category->getName();
$categoryById[$category->getParentId()]['optgroup'][] = &$categoryById[$category->getId()];
}
return $categoryById[CategoryModel::TREE_ROOT_ID]['optgroup'];
}
public function getValues()
{
$values = $this->getValue();
if (!is_array($values)) {
$values = explode(',', $values);
}
if (!sizeof($values)) {
return [];
}
$collection = $this->collectionFactory->create()
->addIdFilter($values);
$options = [];
foreach ($collection as $category) {
$options[] = $category->getId();
}
return $options;
}
public function getAfterElementHtml()
{
$html = '<script type="text/x-magento-init">
{
"*": {
"Magento_Ui/js/core/app": {
"components": {
"categoryChooser": {
"component": "uiComponent",
"children": {
"select_category": {
"component": "Vendor_Module/js/components/category",
"config": {
"filterOptions": true,
"disableLabel": true,
"chipsEnabled": true,
"levelsVisibility": "1",
"elementTmpl": "ui/grid/filters/elements/ui-select",
"options": ' . json_encode($this->getCategoriesTree()) . ',
"value": ' . json_encode($this->getValues()) . ',
"listens": {
"index=create_category:responseData": "setParsed",
"newOption": "toggleOptionSelected"
},
"config": {
"dataScope": "select_category",
"sortOrder": 10
}
}
}
}
}
}
}
}
}
</script>';
return $html;
}
}

And final step is to create another js file category.js into below location

app/code/M2Expert/Categories/view/adminhtml/web/js/components/category.js

Code of category.js

define([
'underscore',
'Magento_Catalog/js/components/new-category'
], function (_, Category) {
'use strict';
function flattenCollection(array, separator, created) {
var i = 0,
length,
childCollection;
array = _.compact(array);
length = array.length;
created = created || [];
for (i; i < length; i++) {
created.push(array[i]);
if (array[i].hasOwnProperty(separator)) {
childCollection = array[i][separator];
delete array[i][separator];
flattenCollection.call(this, childCollection, separator, created);
}
}
return created;
}
return Category.extend({
/**
* Set option to options array.
*
* @param {Object} option
* @param {Array} options
*/
setOption: function (option, options) {
var parent = parseInt(option.parent);
if (_.contains([0, 1], parent)) {
options = options || this.cacheOptions.tree;
options.push(option);
var copyOptionsTree = JSON.parse(JSON.stringify(this.cacheOptions.tree));
this.cacheOptions.plain = flattenCollection(copyOptionsTree, this.separator);
this.options(this.cacheOptions.tree);
} else {
this._super(option, options);
}
}
});
});

Thats it!. Now go back and see the magic!

Back to top