Наверх

Как работают дополнения MODX. Часть 4 — ExtJS Grid

Познакомимся с новым типом объектов в ExtJS — таблицей.

Давайте, добавим на страницу нашего дополнения объект типа GridPanel (xtype: grid).

У таблицы обязательными параметрами являются columns — список колонок и store — собственно список данных, которые нужно отобразить в таблице.

ExtJS Store

Параметр store есть у многих объектов в ExtJS. Обычно, если нужно отобразить какой-то список, используется параметр store. Это может быть как таблица, так и выпадающий список элементов (как в админке MODX выпадает список шаблонов при редактировании ресурса). Ещё есть, например ListView (xtype: listview), который просто выводит список элементов без возможности редактировать.

Из-за того, что таких типов несколько (а мы можем ещё и свои собственные типы создавать), store — это не просто массив, а тоже объект со своими свойствами. В ExtJS для store есть несколько типов объектов:


И не забывайте, что вы можете создавать свои типы объектов на основе существующих или вообще с нуля. Но для простой таблички нам хватит того, что уже есть. Возьмём, к примеру, ArrayStore (xtype: arraystore). В нём нужно в первую очередь перечислить список полей, которые содержатся в массиве, и, собственно, массив данных:

// ...
}, {
    xtype: 'grid',
    columns: [ // Перечисляем список столбцов
        {dataIndex: 'id'},
        {dataIndex: 'name'}
    ],
    store: new Ext.data.ArrayStore({ // Объект ArrayStore
        fields: ['id','name'], // Поля, доступные в массиве данных
        data: [ // Собственно, массив данных ([id, name])
            [1, 'Pencil'],
            [2, 'Umbrella'],
            [3, 'Ball'],
        ]
    })
}
// ...

Получившийся табличкой не очень удобно пользоваться =))

Это потому что мы не указали таблице высоту. Можно воспользоваться параметром height и указать высоту таблицы в пикселях, но лучше давайте скажем ExtJS, чтобы он подстроил высоту таблички под имеющиеся в ней данные:
autoHeight: true,

Уже лучше, но теперь хочется растянуть табличку ещё и по ширине. Для управлением внешним видом таблички есть параметр viewConfig
viewConfig: {
    forceFit: true
}

Данные в таблицах бывают разные и ширина столбцов не должна быть одинаковой — ведь в каком-то столбце могут быть только числа, а в каком-то может быть длинный текст. У колонок в ExtJS можно задать ширину в пикселях. А если указан параметр forceFit, то ширина колонок будет пропорциональна числу, указанному в параметре width.

Поэтому, возьмём общую условную ширину таблицы за 1000 пикселей и укажем ширину столбцов, соответствующую такой таблице. А уже ExtJS растянет её на всю ширину экрана самостоятельно. Я сделаю пропорцию столбцов 33% — 67%. А ещё, давайте укажем названия столбцов:
// ...
}, {
    xtype: 'grid',
    columns: [ // Добавляем ширину и заголовок столбца
        {dataIndex: 'id', width: 330, header: 'ID'},
        {dataIndex: 'name', width: 670, header: 'Name'}
    ],
    autoHeight: true, // Высота таблицы вычисляется автоматически
    viewConfig: {
        forceFit: true, // Растягиваем таблицу на всю ширину
        scrollOffset: 0 // Убираем вертикальный скролл (у нас же автовысота)
    },
    store: new Ext.data.ArrayStore({
        fields: ['id','name'],
        data: [
            [1, 'Pencil'],
            [2, 'Umbrella'],
            [3, 'Ball'],
        ]
    })
}
// ...

У MODX есть свой объект таблицы с прописанными свойствами, методами и оформлением — MODx.grid.Grid попробуем создать свою табличку на основе этой заготовки.

Сначала опишем свойства нашего объекта в конце файла (там, где заканчивается Things.panel.Home). В объекте Things у нас уже есть вложенный объект gird, вот внутри него и создадим нашу табличку:

// ...
}, { // Убираем все свойства в описание объекта
    xtype: 'things-grid-names' // Оставляем только xtype, заменив его на наш собственный
}
// ...

// Внутри Things есть grid. Вот внутри него и создаём свой объект
Things.grid.Names = function (config) { // Придумываем название, например, «Names»
    config = config || {};
    Ext.apply(config, {
        // Сюда перемещаем все свойства нашей таблички
        columns: [
            {dataIndex: 'id', width: 330, header: 'ID'},
            {dataIndex: 'name', width: 670, header: 'Name'}
        ],
        autoHeight: true,
        viewConfig: {
            forceFit: true,
            scrollOffset: 0
        },
        store: new Ext.data.ArrayStore({
            fields: ['id','name'],
            data: [
                [1, 'Pencil'],
                [2, 'Umbrella'],
                [3, 'Ball'],
            ]
        })
    });
    Things.grid.Names.superclass.constructor.call(this, config); // Магия
}
Ext.extend(Things.grid.Names, Ext.grid.GridPanel); // Наша табличка расширяет GridPanel
Ext.reg('things-grid-names', Things.grid.Names); // Регистрируем новый xtype

Мы можем зарегистрировать любой xtype, например, 'my-super-puper-table'. Но чтобы потом не запутаться в своих xtype, лучше в их названиях отображать структуру объектов.

Теперь попробуем изменить объект, который будет расширять наша табличка:
// ...
Ext.extend(Things.grid.Names, MODx.grid.Grid);
// ...

Объект MODx.grid.Grid подразумевает, что store не прописывается в файле ввиде массива, а запрашивает данные у сервера. Поэтому нужно указать адрес нашего коннектора и action, который будет запрашиваться, а также сюда перенесём список полей:
// ...
url: Things.config.connector_url,
action: 'mgr/thing/getlist',
fields: ['id','name']
// ...

Свойство store удаляем — оно нам больше не нужно.

Чтобы вместо надписи Request completed появился список записей, меняем код процессора:
<?php
class ThingsGetListProcessor extends modProcessor {

    public function process() {
        return json_encode(array(
            'success' => true,
            'total' => 3,
            'results' => array(
                array('id' => 1, 'name' => 'Pencil'),
                array('id' => 2, 'name' => 'Umbrella'),
                array('id' => 3, 'name' => 'Ball')
            )
        ));
    }

}
return "ThingsGetListProcessor";

Если в таблице предполагается большое количество записей, принято не выводить их все, а реализовать пагинацию. Нам для этого делать ничего не надо — пагинацией пусть занимается ExtJS. Просто добавим параметр paging
// ...
paging: true,
pageSize: 1, // количество записей на странице
// ...

Осталось научить процессор работать с параметрами start и limit:
<?php
class ThingsGetListProcessor extends modProcessor {

    public function process() {
        $array = array();
        switch ($this->getProperty('start')) {
            case 0:
                $array[] = array('id' => 1, 'name' => 'Pencil');
                break;
            case 1:
                $array[] = array('id' => 2, 'name' => 'Umbrella');
                break;
            case 2:
                $array[] = array('id' => 3, 'name' => 'Ball');
                break;
            default:
                break;
        }
        return json_encode(array(
            'success' => true,
            'total' => 3,
            'results' => $array
        ));
    }

}
return "ThingsGetListProcessor";

Конечно, данные в процессоре нужно получать из базы данных, а параметры start и limit передавать в запрос. Для этого в MODX тоже есть свои заготовки. Мы рассмотрим их в следующей части.


4 комментария

  1. Саня 28 октября 2017, 19:49 # +1
    Круто! Спасибо, Илья.
    1. Станислав 08 ноября 2017, 13:32 # 0
      Привет, Илья. Посмотри, все ли верно. У меня почему-то не работает. Таблица не выводится, а в консоли ошибка «TypeError: this.proxy is undefined». До добавления объекта MODx.grid.Grid все работает. С ним нет.Спасибо!

      // Ext.onReady(function() { // Когда страница загрузилась
      
      var Things = function (config) {
          config = config || {};
          Things.superclass.constructor.call(this, config);
      };
      Ext.extend(Things, MODx.Component, { // Перечисляем группы, внутрь которых будем "складывать" объекты
          panel: {}, page: {}, window: {}, grid: {}, tree: {}, combo: {}, config: {}, view: {}, utils: {}
      });
      Ext.reg('things', Things);
      // Мы не будем вставлять компонент на страницу с помощью MODx.load,
      // поэтому нужно создать экземпляр нашего класса
      // чтобы можно было обращатсья к его свойствам
      Things = new Things();
      
      // Создаём внутри компонента главную панель
      // (через точку в JS обозначется вложенность массива, то есть мы создаём
      // объект Home внутри panel, который, в свою очередь, находится в Things)
      Things.panel.Home = function (config) {
          config = config || {};
          Ext.apply(config, {
              // xtype можно не указывать, так как внутри MODx.Panel xtype уже указан
              // А вот если вы захотите xtype изменить, его можно указать здесь
              renderTo: 'modx-panel-holder',
              cls: 'container', // Добавляем отступы
              items: [{
                  html: '<h2>Things</h2>'
              },{
                  xtype: 'modx-tabs', // Добавляем объект табов в нашу панель
                  paging: true,
                  pageSize: 1, // количество записей на странице
                  items: [{ // Перечисляем, какие именно табы нам нужны
                      title: 'Things 1', // Заголовок первого таба
                      items: [{ // А внутри таба HTML-блок с классом panel-desc
                          html: 'Things 1 description', 
                          cls: 'panel-desc',
                      },{
                          xtype: 'panel',
                          cls: 'container',
                          items: [{
                              xtype: 'button',
                              text: 'Load',
                              cls: 'primary-button',
                              handler: function() {
                                  MODx.Ajax.request({
                                      url: Things.config.connector_url, // запрос пойдёт на адрес /connectors/index.php
                                      params: { // указываем параметры запроса
                                          action: 'mgr/thing/getlist', // здесь путь к процессору
                                          // можно указать и дополнительные параметры запроса
                                          parent: 0,
                                          fields: ['id','name']
                                      },
                                      listeners: {
                                          success: { // при успешном запросе
                                              fn: function ( r ) {
                                                  console.log( r ); // выведем ответ в консоль и покажем окошко
                                                  Ext.MessageBox.alert('Load', JSON.stringify(r.results));
                                              }, scope: this
                                          }
                                      }
                                  });
                              }
                              
                          },
                          {
                              xtype: 'things-grid-names'
                             
                          }
                      ]
                          
                      } ]
                  }, {
                      title: 'Things 2', // Заголовок второго таба
                      items: [{ // Внутри таба ещё один HTML-блок с классом panel-desc
                          html: 'Things 2 description', 
                          cls: 'panel-desc',
                      } ]
                  } ]
              } ]
          });
          Things.panel.Home.superclass.constructor.call(this, config); // Опять магия
      };
      Things.grid.Names = function (config) { // Придумываем название, например, «Names»
          config = config || {};
          Ext.apply(config, {
              // Сюда перемещаем все свойства нашей таблички
              columns: [
                  {dataIndex: 'id', width: 330, header: 'ID'},
                  {dataIndex: 'name', width: 670, header: 'Name'}
              ],
              autoHeight: true,
              viewConfig: {
                  forceFit: true,
                  scrollOffset: 0
              }
              
          });
          Things.grid.Names.superclass.constructor.call(this, config); // Магия
      }
      
      Ext.extend(Things.grid.Names, MODx.grid.Grid); // Наша табличка расширяет GridPanel
      Ext.reg('things-grid-names', Things.grid.Names); // Регистрируем новый xtype
      
      Ext.extend(Things.panel.Home, MODx.Panel); // Наша панель расширяет объект MODX.Panel
      Ext.reg('things-panel-home', Things.panel.Home); // Регистрируем новый xtype для панели
      
      
      Ext.onReady(function() {
          MODx.load({ // На странице выводим сразу нашу панель
              xtype: 'things-panel-home'
          });
      });
      
      
      1. Илья Уткин 08 ноября 2017, 13:41 # +1
        У таблицы MODx.grid.Grid нужно указать url и action
        1. Станислав Однолетко 08 ноября 2017, 13:55 # 0
          Спасибо)

      Авторизация

      через сервис Loginza:

      Подписка или RSS

      Буду присылать новые статьи — никакого спама



      Шаблоны MODX

      1 2 Дальше »

      Объектная
      модель
      MODX