Magento 2 Add category chooser without UI component
-
23-02-2021 - |
Вопрос
I am trying to add category chooser in my custom admin form without ui component but unfortunately I am getting error in system.log
[2019-03-22 09:27:47] main.CRITICAL: Vendor\Module\Block\Adminhtml\Chooser doesn'n extend \Magento\Framework\Data\Form\Element\AbstractElement [] []
My form field is like:
$fieldset->addField(
'category_ids',
'Vendor\Module\Block\Adminhtml\Chooser',
[
'name' => 'category_ids',
'label' => __('Categories'),
'title' => __('Categories'),
'required' => true
]
);
And Chooser.php is like:
<?php
namespace Vendor\Module\Block\Adminhtml;
class Chooser extends \Magento\Catalog\Block\Adminhtml\Category\Widget\Chooser
{
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;
}
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;
}
}
Can anyone help me to resolve this issue?
Решение
I have solved the issue by following codes: Adding field by following code:
$fieldset->addField(
'category_ids',
'\Vendor\Module\Block\Adminhtml\Chooser',
[
'name' => 'category_ids',
'label' => __('Categories'),
'title' => __('Categories'),
'required' => true
]
);
The code for
app/code/Vendor/Module/Block/Adminhtml/Chooser.php
is like below:
<?php
namespace Vendor\Module\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:\'topbannerCategory\'" 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": {
"topbannerCategory": {
"component": "uiComponent",
"children": {
"select_category": {
"component": "Vendor_Module/js/components/new-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 finally added js file to the below location:
app/code/Vendor/Module/view/adminhtml/web/js/components/new-category.js
with below content:
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 everything works great!
Не связан с magento.stackexchange