Наверх

Как работают дополнения 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();
    }
} ]
// ...

Добавляя новые объекты я заметил, что справа не появляется скролл и низ таблицы уже ушёл за пределы экрана. Проведя небольшое расследование я понял, что подключать нашу корневую панель надо не с помощью renderTo, а используя MODx.add. Тогда он будет вставлен в нужный div с заранее прописанными стилями. Меняем файл контроллер:
<?php
class ThingsIndexManagerController extends modExtraManagerController {
    
    public function getPageTitle() {
        return 'Things';
    }
    
    public function loadCustomCssJs() {
        $assets = $this->modx->getOption('assets_url');
        $this->addJavascript($assets . 'components/things/home.js?time=' .time());
        $this->addHtml('<script type="text/javascript">
            Things.config.connector_url = "' . $assets . 'components/things/connector.php";
            Ext.onReady(function() {
                MODx.add("things-panel-home"); // «добавляем» на страницу нашу панель
            });
        </script>');
    }
    
}

И из JS-файла можно удалить свойство renderTo у панели.
// ...
Things.panel.Home = function (config) {
    config = config || {};
    Ext.apply(config, {
        cls: 'container',
        items: [{
            html: '<h2>Things</h2>'
        }, {
            xtype: 'modx-tabs',
            items: [{
                title: 'Things 1',
                items: [{
                    html: 'Things 1 description', 
                    cls: 'panel-desc',
                }, {
                    xtype: 'things-grid-names',
                    cls: 'container',
                    // id можно тоже смело удалять
                } ]
            }, {
                title: 'Things 2',
                items: [{
                    html: 'Things 2 description', 
                    cls: 'panel-desc',
                } ]
            } ]
        } ]
    });
    Things.panel.Home.superclass.constructor.call(this, config);
};
Ext.extend(Things.panel.Home, MODx.Panel);
Ext.reg('things-panel-home', Things.panel.Home);
// ...

Теперь скролл должен появиться.

Сейчас вне зависимости от того, отметим ли мы чекбокс или нет, объект создаётся с полем 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(); // Показываем окно пользователю
    }
} ]
// ...

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


1 комментарий

  1. УстюПаша 06 ноября 2017, 17:24 # 0
    Отлично!

    Авторизация

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

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

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



    Шаблоны MODX

    1 2 Дальше »

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