Ссылка на документацию - https://docs.modx.com/current/en/extending-modx/custom-manager-pages/modext/modx.combo.combobox
Combobox - это тип поля с выпадающим списком, одиночным выбором. Проще говоря, select в html. По всей видимости, combobox'ом он назывался когда то очень давно!
Допустим, наш компонент называется Doodles и у нас имеется стандартная архитектура компонента modx. Нам понадобятся следующие файлы:
- assets
- components
- doodles
- js
- mgr
- misc
- combo.js
- widgets
- items.grid.js
- items.windows.js
- doodles.js
- misc
- mgr
- js
- doodles
- components
- core
- components
- doodles
- controllers
- home.class.php
- processors
- mgr
- item
- getcombolist.class.php
- item
- mgr
- controllers
- doodles
- components
Файл combo.js
В этом файле происходит расширение объекта Doodles и добавление в него нового типа полей.
Ключевые параметры:
- fields - передается массив с 2 полями, один из которых будет использоваться для значения, другой для отображения. Как правило, это
id
иtitle
. - data - массив с возможными вариантами выбора, 1 параметр - то, что запишется в базу, второй - то, что отобразится пользователю.
- displayField - то, что будет отображаться из параметра
fields
. - valueField - то, что будет записано в базу из параметра
fields
.
Если нужно быстрое решение, которое не планируется масштабировать то можно прямов в коде записать возможные значения в параметр data
.
Doodles.combo.Units = function(config) {
config = config || {};
Ext.applyIf(config,{
store: new Ext.data.ArrayStore({
id: 0
,fields: ['unit','display']
,data: [
['MB','Megabyte']
,['GB','Gigabyte']
,['TB','Terabyte']
,['PB','Petabyte']
,['EB','Exabyte']
,['ZB','Zettabyte']
,['YB','Yottabyte']
]
})
,mode: 'local'
,displayField: 'display'
,valueField: 'unit'
});
Doodles.combo.Units.superclass.constructor.call(this,config);
};
Ext.extend(Doodles.combo.Units,MODx.combo.ComboBox);
Ext.reg('doodle-combo-units',Doodles.combo.Units);
Для динамического получения возможных вариантов выбора можно поступить следующим образом:
Вместо параметра store
, указываем этот код:
,url: Doodles.config.connectorUrl
,baseParams: {
action: 'mgr/doodle/combogetlist'
}
Таким образом, будет отправлен ajax-запрос
на коннектор, а он, в свою очередь, вызовет процессор, который указан в action
. Естественно, его нужно создать и сделать так, чтобы результат его работы был массив нужного формата (как на первом участке кода).
2. Процессор combogetlist.class.php
Вариант 1. Железно указываем какую то свою логику, хоть хардкодим, хоть генерируем.
<?php
/**
* Get a list of Types
*/
class TypeGetListProcessor extends modObjectGetListProcessor {
public function process() {
$arrTypes = [
['id' => 0, 'display' => '-Тип не выбран-'],
['id' => 1, 'display' => 'Расчет собственных средств'],
['id' => 2, 'display' => 'Годовая отчетность'],
['id' => 3, 'display' => 'Квартальная отчетность'],
];
$count = count($arrTypes);
return $this->outputArray($arrTypes, $count);
}
}
return 'TypeGetListProcessor';
Вариант 2. Указываем логику согалсно архитектуре Modx, указываем класс, с которым работаем и перегружаем один метод, в котором укажем условия выборки. В данном случае, в качестве элементов выпадающего списка будут ресурсы, у которых родитель - 12.
<?php
class TypesGetListProcessor extends modObjectGetListProcessor {
public $objectType = 'modResource';
public $classKey = 'modResource';
public $defaultSortField = 'id';
public $defaultSortDirection = 'ASC';
/**
* * We doing special check of permission
* because of our objects is not an instances of modAccessibleObject
*
* @return boolean|string
*/
public function beforeQuery() {
if (!$this->checkPermissions()) {
return $this->modx->lexicon('access_denied');
}
return true;
}
/**
* @param xPDOQuery $c
*
* @return xPDOQuery
*/
public function prepareQueryBeforeCount(xPDOQuery $c) {
$c->where([
'parent' => 12,
]);
return $c;
}
}
return 'TypesGetListProcessor';
3. Контроллер home.class.php
Это контроллер, которые инициализирует работу компонента. В нем выгружается tpl
компонента и подключаются все стили и скрипты.
Добавляем в метод public function loadCustomCssJs()
подключение нашего combo.js
:
public function loadCustomCssJs()
{
$this->addCss($this->doodles->config['cssUrl'] . 'mgr/main.css');
$this->addCss($this->doodles->config['cssUrl'] . 'mgr/bootstrap.buttons.css');
$this->addJavascript($this->doodles->config['jsUrl'] . 'mgr/doodles.js');
$this->addJavascript($this->doodles->config['jsUrl'] . 'mgr/misc/utils.js');
$this->addJavascript($this->doodles->config['jsUrl'] . 'mgr/misc/combo.js');
$this->addJavascript($this->doodles->config['jsUrl'] . 'mgr/widgets/items.grid.js');
$this->addJavascript($this->doodles->config['jsUrl'] . 'mgr/widgets/items.windows.js');
$this->addJavascript($this->doodles->config['jsUrl'] . 'mgr/widgets/home.panel.js');
$this->addJavascript($this->doodles->config['jsUrl'] . 'mgr/sections/home.js');
$this->addHtml('<script type="text/javascript">
doodles.config = ' . json_encode($this->doodles->config) . ';
doodles.config.connector_url = "' . $this->doodles->config['connectorUrl'] . '";
Ext.onReady(function() {
MODx.load({ xtype: "doodles-page-home"});
});
</script>
');
}
4. Добавляем combobox в окно
В файле items.windows.js, там где формируются поля для окна, добавляем элемент:
{
xtype: 'doodles-combo-units'
,fieldLabel: _('unit')
,name: 'unit'
,hiddenName: 'unit'
,anchor: '100%'
},
Результат
5. Добавляем combobox в элемент сетки
В файле items.grid.js, там где формируются поля для окна, добавляем элемент:
{
header: _('unit')
,dataIndex: 'unit'
,sortable: false
,width: 50
,editor: { xtype: 'doodle-combo-units', renderer: true }
}
Таким образом, можно будет редактировать элемент через нажатие прямо в сетке, не вызывая диалогового окна!
Результат
Обязательно нужно проверить корневой файл в
assets/components/coodles/js/mgr/doodles.js
. В нем должна присутствовать инициализация Doodles.combo.
Ext.extend(fscontests, Ext.Component, { page: {}, window: {}, grid: {}, tree: {}, panel: {}, combo: {}, config: {}, view: {}, utils: {} });
В противном случае, будет выдана ошибка:
Uncaught TypeError: Cannot set property 'Units' of undefined
.