Наверх

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

Добавим возможность пользователю создавать новые объекты без использования phpMyAdmin. Воспользуемся для этого кнопкой, которую мы создали раньше
{
    xtype: 'button',
    text: 'Create thing', // Меняем надпись
    cls: 'primary-button',
    handler: function() { // После клика на кнопку
        MODx.load({       // будем загружать новый ExtJS-объект
            xtype: 'things-window-names',
        });
    }
}

У ExtJS есть такой объект, как окно


Но разработчики MODX адаптировали его к функционалу MODX, так что мы будем расширять объект MODx.Window:
// ...
Things.window.Names = function (config) {
    config = config || {};
    Ext.applyIf(config, {
        title: 'Create thing'
    });
    Things.window.Names.superclass.constructor.call(this, config); // Магия
};
Ext.extend(Things.window.Names, MODx.Window); // Расширяем MODX.Window
Ext.reg('things-window-names', Things.window.Names); // Регистрируем новый xtype

Если сейчас кликнуть на кнопку, ничего внешне не произойдёт. Дело в том, что у окна есть разные методы и ExtJS «не знает», что с окном делать, пока мы ему об этом не скажем.

При клике на кнопку окно нам надо показать:
// ...
handler: function() {
    MODx.load({
        xtype: 'things-window-names',
    }).show(); // Сразу показываем окно пользователю
}
// ...

MODX предполагает, что обычно в окошках размещаются формы, которые пользователь должен заполнять, поэтому внутри MODX.Window уже создана форма и прописано свойство items. Нам нужно только указать список полей в свойстве fields. Каждое поле тоже должно быть объектом ExtJS. Посмотреть существующие типы полей можно опять в документации:


Мы будем использовать поля следующих типов:
// ...
title: 'Create thing',
fields: [{
    xtype: 'textfield', // Текстовое поле
    name: 'name',
    fieldLabel: 'Name'
}, {
    xtype: 'textarea', // Текстовая область
    name: 'description',
    fieldLabel: 'Description'
}, {
    xtype: 'checkbox', // Чекбокс
    name: 'active',
    fieldLabel: 'Active'
} ]
// ...

Если мы сейчас нажмём в окне «Сохранить», то в консоли браузера можем увидеть, что запрос отправляется на /manager/?a=index&namespace=things, а нам надо передать запрос в процессор создания объектов. Указываем адрес коннектора и action:
// ...
url: Things.config.connector_url,
action: 'mgr/thing/create',
// ...

Ну и создаём процессор create.class.php
<?php
class ThingsNameCreateProcessor extends modObjectCreateProcessor {
    public $classKey = 'ThingsName'; // Класс объекта
}
return "ThingsNameCreateProcessor";

Теперь при сохранении данных в окне новый объект будет создан, но в таблице он сразу не появится. Если у вас до сих пор pageSize равен 1, то новой, четвёртой страницы сразу не появится, а если свойство pageSize удалить (тогда оно станет равным 20 по умолчанию), то новой строчки мы не увидим, пока не нажмём кнопку «Обновить».

Чтобы пользователя не вводить в заблуждение, после успешного сохранения данных нужно обновлять табличку. Разберёмся, как отдавать нужным объектам команды.

В ExtJS, так же, как и в jQuery наиболее удобным способом получить какой-то объект является его вызов по ID. Давайте, придумаем id для таблички:
// ...
}, {
    xtype: 'things-grid-names',
    cls: 'container',
    id: 'my-fantastic-grid'
}
// ...

Теперь из любого места мы можем получить наш объект с помощью Ext.getCmp (так же, как мы получаем его с помощью $('#id')):
// ...
{
    xtype: 'button',
    text: 'Create thing',
    cls: 'primary-button',
    handler: function() {
        MODx.load({
            xtype: 'things-window-names',
            listeners: {
                success: { // При успешном сохранении
                    fn: function () { // обновляем табличку
                        Ext.getCmp('my-fantastic-grid').refresh();
                    }
                }
            }
        }).show();
    }
}
// ...

Всё, вроде бы, хорошо. Но это только до того момента, пока вы не захотите использовать табличку в нескольких экземплярах. В таком случае для каждого экземпляра надо будет придумывать свой id и в них потом не запутаться.

Чтобы выйти из этой ситуации, обычно кнопку создания нового объекта «внедряют» в объект таблицы. Благодаря этому мы можем получить доступ к объекту таблицы с помощью ключевого слова this. Если интересно разобраться, можно почитать, например, в процессе написания тетриса. Но сейчас достаточно понять, что this позволяет обратиться к объекту, чтобы отдавать ему команды.

У таблицы для размещения кнопок и других инструментов управления есть верхняя и нижняя панель. В нижней панели уже размещается пагинация и кнопка обновления. Поэтому мы будем использовать верхнюю панель:

// ...
action: 'mgr/thing/getlist',
fields: ['id','name', 'description', 'active'],
tbar: [{ // Указываем список объектов для размещения в верхней панели
    xtype: 'button', // Перемещаем сюда нашу кнопку
    text: 'Create thing',
    cls: 'primary-button',
    handler: function() {
        MODx.load({
            xtype: 'things-window-names',
            listeners: {
                success: {
                    fn: function () {
                        this.refresh(); // Заменяем указание id на слово this
                    }, scope: this // Передаём this дальше, чтобы он указывал
                                   // не на окно, а на таблицу
                }
            }
        }).show();
    }
} ]
// ...

MODX xcheckbox


Сейчас вне зависимости от того, отметим ли мы чекбокс или нет, объект создаётся с полем active=true. Дело в том, что HTML-чекбокс передаёт значение в POST-параметре только если он отмечен. Нужно заставить ExtJS передавать процессору значение 0, если чекбокс не отмечен.

К счастью, за нас это сделали разработчики MODX — нам нужно только использовать другой xtype: xcheckbox.

Ну и можно немного украсить наше окошко:
// ...
fields: [{
    xtype: 'textfield',
    name: 'name',
    fieldLabel: 'Name',
    anchor: '100%' // Растянем ширину поля
}, {
    xtype: 'textarea',
    name: 'description',
    fieldLabel: 'Description',
    anchor: '100%' // Растянем ширину поля
}, {
    xtype: 'xcheckbox', // Меняем xtype
    name: 'active',
    fieldLabel: 'Active',
    boxLabel: 'Yes' // Добавим текст у флажка
} ],
// ...

Напоследок рассмотрим, как можно указать для формы значения по умолчанию. Пусть чекбокс будет по умолчанию отмечен.

Для этого перед тем, как показать окно пользователю, нужно установить нужные значения. Чтобы не нагромождать «змейку» из команд для окна, сохраним окно в переменную, например, w:
// ...
tbar: [{
    xtype: 'button',
    text: 'Create thing',
    cls: 'primary-button',
    handler: function() {
        var w = MODx.load({ // Сохраним объект окна в переменную
            xtype: 'things-window-names',
            listeners: {
                success: {
                    fn: function () {
                        this.refresh();
                    }, scope: this
                }
            }
        });
        w.setValues({active: true}); // Устанавливаем нужные значения
        w.show(); // Показываем окно пользователю
    }
} ]
// ...

На этом механизм создания объектов можно считать реализованным. Дальше попробуем дать пользователю возможность наши объекты редактировать.


18 комментариев

  1. УстюПаша 06 ноября 2017, 17:24 # 0
    Отлично!
    1. meh 28 ноября 2017, 18:38 # 0
      Добрый день Илья! Планируется ли продолжение)?
      1. Илья Уткин 29 ноября 2017, 07:31 # 0
        Да, планируется. Но пока срок не могу назвать — много работы)
      2. sir 10 декабря 2017, 14:28 # 0
        Илья, можешь подсказать в чем ошибка?
        Пытаюсь заджоинить таблицу
            ...
            public $classKey = 'tableTwo';
            public $defaultSortField = 'id';
        
            public function prepareQueryBeforeCount(xPDOQuery $c) {
                $c->leftJoin('tableOne', 'tableOne', 'tableTwo.id = tableOne.id');
                $c->select('tableTwo.* , tableOne.name as tableone_name');
                return $c;
            }
            ...
        
        Т.е. при выполнении в консоли (я про компонент Console), все отрабатывает корректно, но при попытки переноса данную конструкцию в процессор, к сожалению ничего не выходит.
        В журнале ошибок девственно пусто!
        1. sir 10 декабря 2017, 14:51(Комментарий был изменён) # 0
          В общем, все работает корректно.
          Проблема была вот в чем:
          	...
          	fields: ['id','name', 'tableone_id'],
          	...
          	 columns: [
                      this.sm,
                      {dataIndex: 'id',       width: 100, header: 'ID'},
                      {dataIndex: 'name',     width: 500, header: 'name'},
                      { dataIndex: 'tableone_name', width: 400, header: 'tableone_name' },	
          	...
          
          Искал проблему в запросе а она оказалась в рендере «fields: ['id','name', 'tableone_id']» :)
          Может кому пригодится, проверил ответ так:
          Ext.onReady(function() {
              MODx.Ajax.request({
                  url: Component.config.connector_url,
                  params: {
                      action: 'mgr/component/processor',
                  },
                  listeners: {
                      success: {fn:function( r ) {
                              console.log( r );
                          } }
                  }
              });
          });
          
          1. Илья Уткин 11 декабря 2017, 14:57 # 0
            Да, сам с этим пару раз сталкивался и не мог понять, почему данные не вставляются в поле))
        2. Андрей Малеев 21 декабря 2017, 15:11(Комментарий был изменён) # 0
          Илья, спасибо большое за ценную и качественную информацию!

          Подскажи, пожалуйста, как используя несколько экземпляров таблицы передавать в них уникальный параметр?

          Например, расписание на неделю. В семи вкладках по дням недели располагаем по экземпляру таблицы. Как в каждую из них передать номер дня недели и затем этот номер передать в процессоры?
          1. Илья Уткин 08 января 2018, 15:16 # 0
            Я с этим пока не разобрался — это как раз будет разобрано в следующей части, когда будем редактировать объект
          2. Алексей 14 января 2018, 17:58 # 0
            Илья, спасибо тебе большое за уроки!
            1. Андрей 26 марта 2018, 19:50 # 0
              Илья, добрый день. Подскажите, как правильно создать группу чекбоксов (checkboxgroup). В частности, не получается сделать так, чтобы при загрузке окна в нем отобразилась группа чекбоксов со значениями из моей таблицы.
              { 
                          id:'myGroup',
                          xtype: 'checkboxgroup',
                          fieldLabel: 'Single Column',
                          columns: 3,
                          vertical: true,
                          items: [
                              {boxLabel: 'Item 1', name: 'cb-1'},
                              {boxLabel: 'Item 2', name: 'cb-2', checked: true},
                              {boxLabel: 'Item 3', name: 'cb-3'},
                              {boxLabel: 'Item 4', name: 'cb-4'},
                              {boxLabel: 'Item 5', name: 'cb-5'},
                              {boxLabel: 'Item 6', name: 'cb-6'},
                          ]
                      }
              Вот в блоке items хотелось бы подгрузить чекбоксы, со значениям со своей таблицы, а не те, что прописаны жестко в коде. Спасибо заранее)
              1. Андрей 20 июля 2018, 23:42 # 0
                Илья, подскажи пожалуйста как сделать поле необязательным к заполнению? Использую заготовку modExtra, все добавляемые поля просят, чтобы их заполнили.

                {
                            xtype: 'textfield',
                            fieldLabel: _('sonesites_item_name'),
                            name: 'name',
                            id: config.id + '-name',
                            anchor: '99%',
                            allowBlank: false,
                            width: '95%',
                        }, {
                            xtype: 'textfield',
                            fieldLabel: _('sonesites_item_domain'),
                            name: 'domain',
                            id: config.id + '-domain',
                            anchor: '99%',
                            allowBlank: false,
                            width: '95%',
                        },
                
                1. Илья Уткин 22 июля 2018, 10:51 # 0
                  allowBlank: true,
                2. Алексей 17 октября 2018, 18:17 # 0
                  Статья супер, у меня вопрос по сортировке таблицы, добавил sortable: true
                  {dataIndex: 'name', width: 100, header: 'Имя', sortable: true }
                  Сортировка происходит, но только в пределах загруженной страницы, Илья подскажите пожалуйста, как сделать чтоб сортировалась полностью таблица
                  1. Кирилл Киселев 24 апреля 2019, 13:08 # 0
                    Илья, когда будет часть 7? Спасибо за уроки)
                    1. Илья Уткин 24 апреля 2019, 13:17 # 0
                      Я чё-то подустал с этими уроками)

                      Если все предыдущие уроки усвоены, думаю, можно перейти к урокам Василия Наумкина: bezumkin.ru/training/course1/
                      1. Кирилл Киселев 25 апреля 2019, 22:14 # 0
                        Я наоборот от курсов Василия пришёл, у тебя это более разжёвано… Очень легко информацию преподносишь.
                        1. Максим 25 июня 2020, 19:18(Комментарий был изменён) # 0
                          Да, Илья, тоже именно твои статьи по ExtJS «зашли» мне. Другие (в том числе Василия) по этой теме не мог усвоить.

                          Было бы здорово продолжить)) Осталось совсем немного)) На самом интересном остановились))
                      2. Павел 10 мая 2020, 23:16 # 0
                        Уроки хорошие, спасибо! Жаль заброшено(

                        Авторизация

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


                        Шаблоны MODX

                        1 2 Дальше »

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