Magento 2 — How to add a system config Multi-select field with the ‘chosen’ jQuery option?

Jickson Johnson Koottala
3 min readNov 25, 2020

As I mentioned in the title, this multi-select is with chosen jquery goes inside system configuration of any custom module.

First, we have to define the system config multi-select field.

<field id="category_to_select" translate="label comment" type="multiselect" sortOrder="10" showInDefault="1" showInWebsite="0" showInStore="0"><label><![CDATA[select categories]]></label>
<source_model>HelloMage\CategorySelect\Model\Config\Source\Category\Extended</source_model>
<frontend_model>HelloMage\CategorySelect\Block\System\Multiselect</frontend_model>
<comment><![CDATA[this is to demonstrate multi-select items using chosen jquery]]></comment>
</field>

what are the important elements here on above code?

field id="category_to_select"type="multiselect"source_model_class:  HelloMage\CategorySelect\Model\Config\Source\Category\Extended - This is responsible to provide the multi-select values in ARRAY format.frontend_model_class: HelloMage\CategorySelect\Block\System\Multiselect - This is responsible to alter the multi-select with 'chosen' jquery.

Let’s create a source model class. Here I am creating an array of categories with id, name, and path

so, this is the classpath HelloMage\CategorySelect\Model\Config\Source\Category\Extended

<?php

namespace HelloMage\CategorySelect\Model\Config\Source\Category;
use HelloMage\CategorySelect\Model\Config\Source\CategoryList;/**
* Class Extended
*
@package HelloMage\CategorySelect\Model\Config\Source\Category
*/
class Extended extends CategoryList
{
public function toOptionArray($boolean = 1)
{
$allOption = [[
'value' => 10000000001, // you can use any value here.
'label' => (string)(__('NONE'))
]];
return array_merge($allOption, parent::toOptionArray());
}
}

As you can see, this class has been extended from HelloMage\CategorySelect\Model\Config\Source\CategoryList and I have used parent::toOptionArray() . I extended this because of the same category listing array I am using in other places.

I am not explaining the below code. it only does the job of collecting all categories and returns in form of an array with id as the value and a combination of category ID, name, and path as a label.

<?php

namespace HelloMage\CategorySelect\Model\Config\Source;

use Magento\Catalog\Model\CategoryFactory;
use Magento\Catalog\Model\ResourceModel\Category\CollectionFactory;
use Magento\Framework\Option\ArrayInterface;

class CategoryList implements ArrayInterface
{
/**
*
@var CategoryFactory
*/
protected $categoryFactory;

/**
*
@var CollectionFactory
*/
protected $categoryCollectionFactory;

/**
* CategoryList constructor.
*
*
@param CategoryFactory $categoryFactory
*
@param CollectionFactory $categoryCollectionFactory
*/
public function __construct(
CategoryFactory $categoryFactory,
CollectionFactory $categoryCollectionFactory
) {
$this->categoryFactory = $categoryFactory;
$this->categoryCollectionFactory = $categoryCollectionFactory;
}

/**
*
@return array
*
@throws \Magento\Framework\Exception\LocalizedException
*/
public function toOptionArray()
{
$arr = $this->toArray();
$ret = [];

foreach ($arr as $key => $value) {
$ret[] = [
'value' => $key,
'label' => $value
];
}

return $ret;
}

/**
*
@return array
*
@throws \Magento\Framework\Exception\LocalizedException
*/
public function toArray()
{
$categories = $this->getCategoryCollection();

$categoryList = [];
foreach ($categories as $category) {
$categoryList[$category->getEntityId()] = [
'name' => $category->getName(),
'path' => $category->getPath(),
'cat_id' => $category->getId()
];
}

$catagoryArray = [];
foreach ($categoryList as $k => $v) {
if ($path = $this->getCategoryPath($v['path'], $categoryList)) {
$catagoryArray[$k] = '[' . $v['cat_id'] . '] -- ' . $path;
}
}

asort($catagoryArray);

return $catagoryArray;
}

/**
*
@return mixed
*
@throws \Magento\Framework\Exception\LocalizedException
*/
public function getCategoryCollection()
{
$collection = $this->categoryCollectionFactory->create();
$collection->addAttributeToSelect(['path', 'name']);

return $collection;
}

/**
*
@param $path
*
@param $categoryList
*
*
@return string
*/
public function getCategoryPath($path, $categoryList)
{
$categoryPath = [];
$rootCats = [1, 2];
$path = explode('/', $path);

if ($path) {
foreach ($path as $catId) {
if (!in_array($catId, $rootCats)) {
if (!empty($categoryList[$catId]['name'])) {
$categoryPath[] = $categoryList[$catId]['name'];
}
}
}
}

if (!empty($categoryPath)) {
return implode(' » ', $categoryPath);
}

return false;
}
}

Let’s create a frontend model class to use ‘chosen’ jquery on multi-select

so, this is the classpath HelloMage\CategorySelect\Block\System\Multiselect

<?php

namespace HelloMage\CategorySelect\Block\System;

use Magento\Config\Block\System\Config\Form\Field;
use Magento\Framework\Data\Form\Element\AbstractElement;

/**
* Class Multiselect
*
@package HelloMage\CategorySelect\Block\System
*/
class Multiselect extends Field
{
/**
*
@param AbstractElement $element
*
@return string
*/
protected function _getElementHtml(AbstractElement $element)
{
// @codingStandardsIgnoreLine
return parent::_getElementHtml($element) . "
<script>
require([
'jquery',
'chosen'
], function ($, chosen) {
$('#" . $element->getId() . "').chosen({
width: '100%',
placeholder_text: '" . __('Select Options') . "'
});
})

</script>";
}
}

After this, run the setup: upgrade and di: compile. clear cache and see how the system config looks.

--

--