Modx ComboBox

30 Декабря 2019 13:28

Ссылка на документацию - 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
  • core
    • components
      • doodles
        • controllers
          • home.class.php
        • processors
          • mgr
            • item
              • getcombolist.class.php

Файл combo.js

В этом файле происходит расширение объекта Doodles и добавление в него нового типа полей. 
Ключевые параметры:

  1. fields - передается массив с 2 полями, один из которых будет использоваться для значения, другой для отображения. Как правило, это id и title.
  2. data - массив с возможными вариантами выбора, 1 параметр - то, что запишется в базу, второй - то, что отобразится пользователю.
  3. displayField - то, что будет отображаться из параметра fields.
  4. 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.