Пишем интерфейс: виджеты ExtJS и процессоры

На прошлом уроке мы разобрались с контроллерами Custom Manager Page (CMP) нашего компонента, и выяснили, что основной смысл их существования - подготовить все нужные файлы для вывода страницы.

Пока у нас используются js файлы от modExtra, и сегодня нам нужно их изучить и переписать для Sendex.

Первым делом нужно понять, что ExtJs - это javascript фреймфорк, куда более мощный чем jQuery. Он не требует HTML верстки, он все генерирует на лету. То есть, вы пишете javascript код, а на странице получаете готовые таблички, кнопочки, пагинацию и т.д.

Причем, все эти элементы уже подчиняются какой-то логике: одна кнопка переключает странички, другая вызывает модальное окно и т.д.

Сначала довольно трудно въехать в ExtJS, особенно если не знаешь javascript, но со временем начинаешь понимать его преимущества, и реально экономить время.

Виджеты

Сразу говорю, я не гуру ExtJS, освоил его на минимальном уровне, необходимом для написания компонентов MODX. Поэтому, буду писать "своими словами", и возможно не всегда верно.

На мой взгляд, 2 основных момента для понимания ExtJS: 1. Все элементы ExtJS являются объектами javascript, со своими свойствами. Когда вы пишите какой-то свой виджет, нужно просто унаследовать и расширить уже готовый от авторов ExtJS или MODX. Например, пишите свою таблицу - расширяете MODx.grid.Grid, в таблицах будут всплывающие окошки - писуете фиджет, расширяющий MODx.Window

Это очень похоже на Объектно-Ориентированное Программирование (ООП) в PHP и если принять эту логику - жизнь становится очень простой и лёгкой.

2. Практически все видежты или уже работают, или умеют работать через Ajax. Все свои данные они отправляют на сервер без перезагрузки страницы, и ответ получают в формате JSON.

То есть, здесь полнейшее отделение логики от представления. Вам не нужно что-то там парсить или рендерить на PHP, чтобы отдать готовый HTML. Вставлять его тоже никуда не нужно, сервер должен отдать массив в таком формате:


$array = array(
    'success' => false,
    'message' => 'Возника ошибка при сохранении формы',
    'total' => 1
    'data' => array(
        'id' => 'alias',
        'msg' => 'Это поле обязательно'
    )
);

А виджет ExtJS смотрит в этот массив, понимает, что сервер отдал ошибку и выводит сообщение о ней. В данном примере - это требование заполнить alias страницы при её сохранении.

Уверяю вас, такая схема делает работу очень простой и удобной. Вы отдельно пишите код на javascript, отдельно пишите процессоры на PHP, которые с ним будут работать, а потом открываете админку - и начинается магия!

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

Процессоры

Мы не будем рассматривать старые скучные процессоры, в которых были просто портянки кода, которые возвращали JSON. С версии MODX 2.2 у нас появились "классные процессоры", о которых вы можете почитать вот здесь - с ними мы и будем работать.

Итак, процессор это php файл, который содержит класс, наследующий modProcessor и возвращающий своё имя. В MODX есть уже куча готовых классов на разные случаи жизни, которые можно наследовать для своих целей: - modObjectGetProcessor - Получает и возвращает указанный объект

  • modObjectGetListProcessor - Выводит список объектов для таблицы
  • modObjectCreateProcessor - Создаёт новый объект
  • modObjectUpdateProcessor - Обновляет существующий объект
  • modObjectDuplicateProcessor - Делает копию существующего объекта
  • modObjectRemoveProcessor - Удаляет объект

То есть, здесь нам тоже, как правило, нужно просто унаследовать уже имеющийся класс, переопределить пару свойств и методов - и всё готово.

Сам файл-процессор состоит из класса, и обязан содержать в конце вернуть своё имя. Вот, простейший пример:

<?php
class SendexItemCreateProcessor extends modObjectCreateProcessor {
    // Тип объекта, с которым будем работать - это имя выводится в логе операций MODX
    // и подставляется в сообщения об ошибках
    public $objectType = 'sxNewsletter';
    // xPDO класс объекта
    public $classKey = 'sxNewsletter';
    // Используемый лексикон - нужен для вывода ошибок
    public $languageTopics = array('sendex');
    // Разрешение, которое проверяеся при работе с процессором
    // Можно оставить пустым - тогда смогут работать все менеджеры
    public $permission = 'new_document';
}
// Обязательно возвращаем имя класса, чтобы MODX знал, что именно нужно инициализировать в процессоре
return 'SendexItemCreateProcessor';

Это процессор абсолютно рабочий, его можно вызвать через $modx->runProcessor(), передать нужные параметры и будет создан объект sxNewsletter (или получена ошибка).

Для того, чтобы узнать, какие методы можно переопределить - просто смотрите в исходник наследуемого modObjectCreateProcessor. Напоминаю, что в PhpStorm нужно просто кликнуть на класс и нажать Ctrl+B.

С теорией покончено, давайте закрепим на практике.

Основные js файлы админки

От modExtra у нас осталась демонстрационная страница, которая создаётся следующими скриптами в /assets/components/sendex/js/mgr/: - sendex.js - скрипт с объявлением самого объекта Sendex. В него будут вписываться все остальные объекты компонента. Этот файл мы не трогаем.

  • sections/home.js - Скрипт единственной страницы компонента. Тоже не трогаем, но обращаем внимание на строку:
,renderTo: 'sendex-panel-home-div'
И вспоминаем, что [именно такой div у нас указан в шаблоне](https://github.com/bezumkin/Sendex/blob/5416d620300261025420f9e73c41ee3a6fb9fd5a/core/components/sendex/elements/templates/home.tpl), который [загружается контроллером](https://github.com/bezumkin/Sendex/blob/5416d620300261025420f9e73c41ee3a6fb9fd5a/core/components/sendex/controllers/home.class.php#L45). А потом смотрим на строку, где объявляется **xtype** нашей страницы:
xtype: 'sendex-panel-home'
Именно [этот xtype у нас и запускает контроллер](https://github.com/bezumkin/Sendex/blob/5416d620300261025420f9e73c41ee3a6fb9fd5a/core/components/sendex/controllers/home.class.php#L35) после полной загрузки страницы.

То есть, вот она связь HTML страницы, сгенерированной PHP, и запуск виджета ExtJS на ней.
  • widgects/home.panel.js - виджет "Панель", который содержит в себе заголовок, описание и табы (вкладки). Его мы немножко будем менять, когда будем добавлять новые вкладки на страницу.
  • widgects/items.grid.js - виджет с таблицой предметов modExtra. Основной рабочий файл, который выводит записи из БД и позволяет редактировать их во всплывающих окошках. В основном с ним мы и работаем.

Давайте еще раз: у нас есть раздел в админке, он состоит из одного пункта меню и одной страницы. На странице вызывается панель с вкладками, и на первой вкладке у нас таблица. Окошки, создающие новую запись и меняющие существующую, являются частью этой таблицы.

Сегодня работаем именно в этом объеме, другие вкладки добавим позже.

Готовим таблицу для работы с sxNewsletter

Первая вкладка посвящена работе с объектами sxNewsletter, то есть - с подписками.

Нужно указать это в widgets/home.panel.js:


{
    // Имя вкладки
    title: _('sendex_newsletters')
    // Массив с содержимым
    ,items: [
    // Блок HTML с описанием этой вкладки
    {
        html: _('sendex_newsletters_intro')
        ,border: false
        ,bodyCssClass: 'panel-desc'
        ,bodyStyle: 'margin-bottom: 10px'
    }
    // Вызов рабочей таблицы
    ,{
        xtype: 'sendex-grid-newsletters'
        ,preventRender: true
    }
}

Как видите, я просто переименовал кое-что. Самый главный момент здесь, это вызов xtype таблицы

xtype: 'sendex-grid-newsletters'

Как только мы его указали - админка ломается, ибо такого xtype не существует. Как понять, из-за сего сломалось? Ну так нам ExtJS пишет в логе, что перед ошибкой было

create
Ext.Container.Ext.extend.createComponent
Ext.Container.Ext.extend.lookupComponent

То есть: поиск и создание виджета, после чего ошибка. Мы же помним, что мы ег опереименовали - так что всё логично.

Таким образом, нужно поменять имя и в файле widgects/items.grid.js. Хотя стоп, его сначала нужно переименовать в widgects/newsletters.grid.js.

Сразу нужно указать новый файл для загрузки в контроллере controllers/home.class.php на строке 30:

$this->addJavascript($this->Sendex->config['jsUrl'] . 'mgr/widgets/newsletters.grid.js');

Ну а теперь просто меняем в newsletters.grid.js строку 108 с регистрацией виджета:

Ext.reg('sendex-grid-newsletters',Sendex.grid.Items);

Синхронизируем с MODXCloud и обновляем страницу - ошибки нет. Кстати говоря, обычно с переименованием через SFTP возникают какие-то ошибки, и часто PhpStorm просто создаёт на сервере новый файл рядом со старым. Это не страшно, просто удаляйте его через админку.

У вас должно получиться всё как на скриншоте выше. Обратите внимание, что у меня не отображается заголовок вкладки. Так и должно быть - я же указал несуществующую запись из лексикона.

Вообще, поддержка лексиконов в компонентах сделана отлично, вам нужно просто указывать ключ из ваших файлов лексикона в виде:

_('ключ')

Нижнее подчеркивание здесь - это функция MODX, которая выведет нужную фразу. Очень элегантное решение, не правда ли?

Поэтому редактируем /core/components/sendex/lexicon/ru/default.inc.php


$_lang['sendex'] = 'Sendex';
$_lang['sendex_menu_desc'] = 'Управление подписками';

$_lang['sendex_newsletters'] = 'Подписки';
$_lang['sendex_newsletters_intro'] = 'На этой странице вы создаёте и редактируете ваши подписки.';

и /core/components/sendex/lexicon/en/default.inc.php.


$_lang['sendex'] = 'Sendex';
$_lang['sendex_menu_desc'] = 'Newsletters management';

$_lang['sendex_newsletters'] = 'Newsletters';
$_lang['sendex_newsletters_intro'] = 'On this page you create and edit your subscription.';

Напоминаю, что лексиконы Sendex загружает контроллер home.class.php.

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

Ну а теперь в newsletters.grid.js осталось переименовать все встречающиеся Items в Newsletters, а item в newsletter. В результате этого пропадает кнопка создания (меняем ей запись на sendex_btn_create и добавляем это в лексиконы) и получается ошибка > Процессор не найден: mgr/newsletter/getlist

Конечно не найден, мы же переименовали всё только в javascript файлах, а php остались где были. Кстати, вот мой коммит со всеми изменениями на текущий момент - сравнивайте.

Вот картинка с изменёнными файлами: Теперь нужно переделать процессоры.

Готовим процессоры для работы с sxNewsletter

Первым делом переименовывем /core/components/sendex/processors/mgr/item/ в /core/components/sendex/processors/mgr/newsletter/ и заливаем новую директорию на сервер.

У меня получилось, что старая тоже осталась - поэтому я её удаляю в админке:

Затем меняем классы в файлах процессорах. Просто заменяем SendexItem, получившийся при изначальном переименовании modExtra, на sxNewsletter - наш объект с подписками.

После переименования синхронизируем изменения файлов, обновляем страницу в админке и ошибка пропадает. Более того, можно создавать, изменять и удалять подписки, так как основные поля объекта modExtraItem и sxNewsletter совпадают (name и description).

Затем приводим в порядок остальные записи в лексиконе (меняем и там item на newsletter) и получается все довольно приятно: -

Вот и всё, для начала. Мы уже может управлять объектами - у нас есть базовый интерфейс на ExtJS, который работает с процессорами и поддерживает сразу 2 языка: русский и английский.

Вот мой коммит с изменениями.

Вот какие файлы я редактировал:

Заключение

Дальнейшая работа сводится к трём пунктам: - Придумали и нарисовали что-то на ExtJS

  • Добавили работу с этим в процессоры
  • Добавили нужные записи в словари

Конечно, нужно будет создавать новые javascript файлы и процессоры, подключать их в контроллерах и вызывать из виджетов, но в целом - вот она, основная база дальнейшей разработки. Выходит несложно, если работать с заготовкой modExtra.

На следующем уроке мы плотнее поработаем с ExtJS и сделаем свой интерфейс для sxNewsletter.

← Предыдущая заметка
Собираем и устанавливаем первую версию пакета
Следующая заметка →
Пишем интерфейс: таблица подписок и окошко создания
Комментарии (15)
k07nAndrei Kilin
20.11.2013 10:11

Нужно указать это в sections/home.js:

в widgets/home.panel.js

5636504Василий
20.11.2013 16:13

У вас должно получиться всё как на скриншоте выше.

До этого момента проделал все трижды с откатами. Таблица как на скриншоте так и не появилась =\ В консоли - "Uncaught TypeError: Cannot call method 'request' of undefined". Конечные файлы пока не синхронизирую с твоими коммитами, т.к., видимо, ошибка где-то в промежуточном шаге =\

bezumkinВасилий Наумкин
20.11.2013 16:14

Что-то где-то не переименовал.

А с сервером то не забываешь синхронизировать изменения? У тебя есть на сервере файл widgets/newsletters.grid.js?

Ну и еще бывает, что браузер сильно кэширует - нужно обновлять через Ctrl+F5.

5636504Василий
20.11.2013 16:18

Вроде как да, после всех изменений синхронизация. Но так как при сохранении файла идет его автоматическая закачка - таблица синхронизации пустая. Единственный момент - вручную удалял item.grid.js, как ты и предупреждал.

bezumkinВасилий Наумкин
20.11.2013 16:19

Пришли данные для входа на сайт на bezumkin@ya.ru

bezumkinВасилий Наумкин
20.11.2013 16:31

Ты на прошлом уроке пропустил подготовку контроллеров.

Там нужно заменить

$this->modx->regClientCSS()

на

$this->addCSS()

и т.д.

5636504Василий
20.11.2013 16:38

Если вы хотите, чтобы ваш компонент дружил с AjaxManager — их нужно заменить

Видимо, повелся на "Если", т.к. AjaxManager там не установлен. Спасибо, будем продолжать!

bezumkinВасилий Наумкин
20.11.2013 16:39

Ну, просто дальше уже идут расхождения в коде и будет труднее.

Поэтому - делай как я =)

ershov.ilyaИлья Ершов
22.11.2013 14:46

Кому надо - могу поделиться книжкой по ExtJS 4, издательство PACKTpub, с архивом с примерами кода. Добавляйтесь в скайп: ershov.ilya (да простят меня буржуйские издатели) Книжка в формате .epub (формат родной Apple, читалка для Win тоже есть), на английском языке. В оригинале бумажная стоит 100$, цифровое издание я покупал за 30$.

С целью поддержания MODX сообщества книжку кину безвозмездно ;) Василий, этот коммент можно через пару дней удалить :)

ershov.ilyaИлья Ершов
22.11.2013 16:39

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

Мда... я понимаю защита авторских прав, но всё равно, неожиданно, неудобно и слегка не приятно...

bezumkinВасилий Наумкин
22.11.2013 16:42

Нет никакой зашиты, скорее это скрипт смены размера шрифта мешается.

Я легко сохранил в pdf через Chrome - печатай на здоровье.

ershov.ilyaИлья Ершов
22.11.2013 16:43

Да тоже заметил, проблема только в Firefox - Chrome совершенно спокойно распечатал. Так что я уже доволен ))

И кстати уже в нынешнем виде, если распечатать все статьи Курса - получится полноценная книга...

ershov.ilyaИлья Ершов
27.11.2013 14:54

Сразу под заголовком "Готовим процессоры для работы с sxNewsletter" Явно какая-то опечатка что на что переименовываем. Но по смыслу можно и самостоятельно разобраться...

Roman Smile
06.02.2014 00:51

modObjectGetProcessor — Получает и возвращает указанный объект modObjectGetListProcessor — Выводит список объектов для таблицы modObjectCreateProcessor — Создаёт новый объект modObjectUpdateProcessor — Обновляет существующий объект modObjectDuplicateProcessor — Делает копию существующего объекта modObjectRemoveProcessor — Удаляет объект

Например, мы создали несколько "классных" процессоров для работы со своими таблицами. Если то, что они делают, нам пригодится в каком-то левом сниппете или плагине, то как нам следует их вызывать? Или они только для использования в компонентах?

bezumkinВасилий Наумкин
06.02.2014 06:21
inetlover
Александр Наумов
19.03.2024 10:06
Спасибо большое за такой развернутый ответ!!
bezumkin
Василий Наумкин
14.03.2024 13:13
Спасибо!
Андрей
14.03.2024 10:47
Василий! Как всегда очень круто! Моё почтение!
russelgal
russel gal
09.03.2024 17:17
А этот стоило написать хотя бы затем, чтобы получить комментарий от юзера, который ничего не писал ...
inetlover
Александр Наумов
27.01.2024 00:06
Василий, спасибо! Извини, тупанул.
bezumkin
Василий Наумкин
22.01.2024 04:43
Давай-давай!
bezumkin
Василий Наумкин
24.12.2023 11:26
Спасибо!
bezumkin
Василий Наумкин
27.11.2023 02:43
Ура!
bezumkin
Василий Наумкин
25.11.2023 08:30
Vesp тянет 2 зависимости: vesp-frontent для фронта и vesp-core для бэкенда. Их можно обновлять, но э...
bezumkin
Василий Наумкин
22.11.2023 08:09
Отлично, поздравляю!