<?php
class ThingsIndexManagerController extends modExtraManagerController {
public function getPageTitle() {
return 'Things';
}
public function getTemplateFile() {
return dirname(__FILE__) . '/home.tpl';
}
public function loadCustomCssJs() {
$this->addHtml("<script>
Ext.onReady(function() {
var title = 'Мой заголовок';
var msg = 'Модель DOM готова...';
Ext.MessageBox.alert(title,msg);
});
</script>");
}
}Пример кода я взял из статьи, найденной в Яндексе по запросу «Уроки ExtJS». Единственное — нам не надо подключать скрипты и стили самого ExtJS, так как за нас это уже сделал MODX.
Писать JavaScript внутри PHP — то ещё извращение, поэтому JS-скрипты лучше выносить в отдельные файлы и подключать с помощью addJavascript:
<?php
class ThingsIndexManagerController extends modExtraManagerController {
public function getPageTitle() {
return 'Things';
}
public function getTemplateFile() {
return dirname(__FILE__) . '/home.tpl';
}
public function loadCustomCssJs() {
$this->addJavascript('/things/home.js');
}
}Чтобы каждый раз не очищать кеш браузера, можно указать дополнительный параметр у файла. Не забудьте потом удалить его, когда закончите разработку:
$this->addJavascript('/things/home.js?time=' .time());В ExtJS принят объектно-ориентированный подход. В JavaScript как такового ООП нет и каждый фреймворк реализует его по-своему. Если объяснить простыми словами, то всё в ExtJS является объектами: дерево ресурсов, панель редактирования ресурса, панель системных настроек. Даже любое поле или чекбокс — это объект. Каждый объект может находиться внутри другого объекта — для реализации вложенности у объектов ExtJS есть свойство items.
ExtJS items
Если у объекта есть родительский элемент, то объект будет размещён (или отрендерен) внутри этого родительского элемента. Но вы можете изменить это поведение, указав свойство renderTo — оно указывает, где отобразить конкретный объект. Обычно свойство renderTo указывается только у тех объектов, у которых нет родителя.
И ещё одно важное свойство — это xtype.
ExtJS xtype
В этом свойстве указывается тип объекта. По аналогии можно сказать, что xtype — это класс объекта. В зависимости от xtype у объекта могут быть дополнительные свойства, методы, может быть прописано какое-то поведение и даже набор дочерних объектов.
Вся работа с ExtJS обычно построена следующим образом:
- Ищем наиболее подходящий для задачи xtype. Для поиска можно воспользоваться документацией. Она на английском, но по названию объектов можно хотя бы сориентироваться. Например, если нам нужно отобразить какое-то окошко, идём в раздел Ext.Window и видим, что нужный нам xtype — это window: https://ilyaut.ru/cloud/1WpUHf.png
- Решаем, внутри какого объекта отобразить наш новый объект, вставляем его внутрь соответствующего свойства items
- Переопределяем те свойства и методы объекта, в которых мы хотим изменить поведение.
Давайте, уберём из контроллера метод getTemplateFile и отобразим заголовок с помощью ExtJS. Файл home.tpl тоже можно удалить, он нам больше не понадобится — всю HTML-структуру будет создавать ExtJS.
MODX создаёт DIV с id='modx-panel-holder' на странице компонента. Вот в нём мы и будем создавать всю внутреннюю структуру. Добавить объект в этот div можно с помощью команды MODx.add(). Загрузку нашего компонента будем производить по окончании загрузки страницы. Воспользуемся аналогом записи $(document).ready():
Ext.onReady(function() { // Когда страница загрузилась
MODx.add({ // Вставляем на страницу объект
xtype: 'panel', // Тип объекта - Панель
items: [{ // Внутри панели будет другой объект
html: '<h2>Things</h2>', // который представляет из себя HTML-блок
} ]
});
});Обновляем страницу компонента и видим, что ничего не изменилось)) Но это на первый взгляд. Посмотрите в инспекторе кода — ExtJS вставил заголовок внутрь дива modx-panel-holder, но не напрямую, а сгенерировал целый набор вложенных дивов. Так он работает.
Преимущество ExtJS в том, что мы можем избавиться от дублирования кода и, в то же время, использовать его повторно, если нам это нужно. Для этого определим новый ExtJS-компонент — тогда мы сможем обращаться к нему из любого места и даже переопределять некоторые его свойства:
Things = function (config) { // Создаём объект Things
config = config || {}; // Определяем настройки объекта
Ext.applyIf(config, { // Указываем состав объекта
components: [{
xtype: 'panel',
items: [{
html: '<h2>Things</h2>',
} ]
} ]
});
Things.superclass.constructor.call(this, config); // Магия =)
};
Ext.extend(Things, MODx.Component); // Расширяем объект MODx.Component нашим объектом
Ext.reg('things', Things); // Регистрируем xtype для нашего объекта
Ext.onReady(function() {
MODx.add({
xtype: 'things', // Командуем загрузить наш новый объект
});
});Как же нам теперь переопределить свойства нашего объекта? Нужно всего лишь передать их внутри config. У нашего объекта есть только свойство components. Давайте его и переопределим, ради эксперимента:
// ...
Ext.onReady(function() {
MODx.add({
xtype: 'things',
components: [{ // Указываем новый состав объекта
xtype: 'panel',
items: [{
html: '<h2>Things override</h2>',
} ]
} ]
});
});Компонент — это, обычно, просто контейнер, который содержит в себе объекты, которые мы собираемся использовать. Поэтому часто можно увидеть, что сам компонент не содержит элементов. А уже его дочерние объекты являются разными сущностями — страницы, панели, таблицы, окна и прочее.
Для удобства доступа к тем или иным дочерним объектам, разделим их на группы:
// Создаём компонент - главный объект
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.add,
// поэтому нужно создать экземпляр нашего класса
// чтобы можно было обращатсья к его свойствам
Things = new Things();
// Создаём внутри компонента главную панель
// (через точку в JS обозначется вложенность массива, то есть мы создаём
// объект Home внутри panel, который, в свою очередь, находится в Things)
Things.panel.Home = function (config) {
config = config || {};
Ext.apply(config, {
// xtype можно не указывать, так как внутри MODx.Panel xtype уже указан
// А вот если вы захотите xtype изменить, его можно указать здесь
items: [{
html: '<h2>Things</h2>'
} ]
});
Things.panel.Home.superclass.constructor.call(this, config); // Опять магия
};
Ext.extend(Things.panel.Home, MODx.Panel); // Наша панель расширяет объект MODX.Panel
Ext.reg('things-panel-home', Things.panel.Home); // Регистрируем новый xtype для панели
Ext.onReady(function() {
MODx.add({ // На странице выводим сразу нашу панель
xtype: 'things-panel-home'
});
});ExtJS TabPanel
Немного украсим нашу страничку и, заодно, посмотрим, как добавлять на неё элементы. Для начала добавим панели класс container, чтобы появились отступы от других элементов админки. А так же добавим блок с табами и описанием. У объекта табов xtype — tabpanel, но в MODX есть свой объект табов, который расширяет Ext.TabPanel. Этот объект добавляет CSS-классы, чтобы табы смотрелись более органично внутри админки. Кроме того, табы MODX могут быть ещё и вертикальными. Поэтому мы будем использовать более «навороченные» MODX-табы:// ...
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-блок с классом panel-desc
html: 'Things 1 description',
cls: 'panel-desc',
} ]
}, {
title: 'Things 2', // Заголовок второго таба
items: [{ // Внутри таба ещё один HTML-блок с классом panel-desc
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);
// ...Как вы поняли, кроме документации по ExtJS, нужно смотреть ещё и в исходный код MODX. В основном, все стандартные ExtJS-объекты в MODX лежат в папке /manager/assets/modext/widgets/
Примеры кода у нас начали разбухать, поэтому я буду пропускать некоторые строки, будьте внимательны, чтобы не затереть нужное.
В следующей статье «оживим» наш интерфейс — рассмотрим отправку запросов и обработку ответа.
Объектная
Работа проделана колоссальная. Спасибо Вам за нее.
Но повторить ваш урок успешно не получается.
Если бы вы
Выложили файлики
home.js и index.class.php
для скачивания.
Было бы очень окей.
Где-то закралась ошибочка, и не получается это урок
компонентстраничку в админке с нуля. Тут надо отдать должное, вы все хорошо расписали и все прошло как по маслу. Но я пытаюсь сделать, чтобы весь Ext.JS загружался в div, указанный в файле .tpl и в теории (а так же подобию других дополнений) это не должно было быть сложным. Я создал div с id=«test»Далее в файле c extjs
Thing.page.Home = function(config) { config = config || {}; Ext.applyIf(config, { components: [{ xtype: 'thing-panel-home', renderTo: 'test' ]}, }); Thing.page.Home.superclass.constructor.call(this, config); }; Ext.extend(Thing.page.Home, MODx.Component); Ext.reg('thing-page-list', Thing.page.Home);thing-panel-home — зарегистрированная панелька рабочая.ну и в контроллере
$this->addHtml('<script type="text/javascript"> Ext.onReady(function() { MODx.load({ xtype: 'thing-page-list'}); });</script>');Но, вместо того, чтобы подгружаться в div все добавляется после него. Я пришел к выводу, что extjs грузится раньше, чем div и поэтому не находит его и просто загружает. Хотя, по моей теории, должна быть ошибка.Я разделил все на 3 js файла, вначале подгружаю тот, где
var Thing= function (config) { config = config || {}; Thing.superclass.constructor.call(this, config); }; Ext.extend(Thing, Ext.Component, { page: {}, window: {}, grid: {}, tree: {}, panel: {}, combo: {}, config: {} }); Ext.reg('Thing', Thing); Thing= new Thing();и сама панелькаThing.panel.Home = function (config) { config = config || {}; Ext.apply(config, { baseCls: 'modx-formpanel', //пробовал это cls: 'container', items: [ { html: 'Проверка' }, { xtype: 'panel', cls: 'container', items: [{ html: 'Тестовая панелька', cls: 'panel-desc', ]} } ] }); Thing.panel.Home.superclass.constructor.call(this, config); }; Ext.extend(Thing.panel.Home, MODx.Panel); Ext.reg('thing-panel-home', Thing.panel.Home);Потом гриды и файл инициализации ранее. tpl вообще загружаю в конце контроллера. И я не знаю что делать. что уже только не перебирал. В див не грузится, только после него. Все работает, все функционирует. Я даже панель упрастил. Классы переименовывал. Будто что-то надо добавить, но что — не понимаю. Кто-нибудь, может подсказать, что случилось.Вот контроллер мой
class StmdbWelcomeManagerController extends modExtraManagerController { public function getPageTitle() { return 'Тестовая страница'; } function loadCustomCssJs() { $assets = $this->modx->getOption('assets_url'); $this->addJavascript('/assets/components/thing/js/conf.js?time=' . time()); $this->addJavascript('/assets/components/thing/js/index.js?time=' . time()); $this->addJavascript('/assets/components/thing/js/thing.grid.js?time=' . time()); $this->addHtml('<script type="text/javascript"> Thing.config.connector_url = "' . $assets . 'components/thing/connector.php"; Ext.onReady(function() { MODx.load({ xtype: \'films-page-list\'}); }); </script>'); } public function getTemplateFile() { return 'welcome.tpl'; } }Почему это очень удобно (если вдруг кто из новичков прочтет), иначе tpl файл будет выглядеть как position:absolute, а вся панелька будет «скроллиться» под него. Тут или полностью отказаться от шаблона и просто делать слои в extjs или еще потратить бессонные ночи :-) Я уже думал делал класс в классе, точнее в этом контроллере все рендерить, а потом require_once файл с вызовом панели.
Благодаря вашим урокам, я, кстати, еще в одни дебри полез :-) но на это можете не отвечать. получаю в handler, через get запросом данные и с помощью Ext.getCmp('id_поля').setValue(); присваиваю дальше. Одно из таких значений — путь к картинке. Что тоже работает и хорошо, т.е. кнопка отработала нажатие в texfield появился адрес к jpg. И вот решаю теперь задачу по кнопке preview рядом с этим полем, открыть условный window.PrevImg, который отрендерит картинку. Ну а на полном серьезе, вы рассмотрели здесь основные вещи при создании CMP, без различных вспомогательных дополнений, что катализирует на творчество и подтягивание знаний. А уж ExtJs — это просто жесть. Даже их страница docs по версии 3.4.0 — дремучая вещь.
{xtype: 'button', text: 'Просмотр', handler: function() { var imgsSrc = Ext.getCmp('poster').getValue(); console.log(imgsSrc); MODx.load({ xtype: 'prev-images', items: [{html:'<img src="' + imgsSrc + '">']}, }).show(); } Thing.window.PrevImg = function (config) { config = config || {}; Ext.applyIf(config, { cls: 'container', width: 300, height: 400, activeItem: 0, }); Thing.window.PrevImg.superclass.constructor.call(this, config); };можно было и без переменной обойтись.прошу прощения, что к теме renderTo вообще не относится, не мог сам себе не ответить, на тот курсив, чтобы не отвлекать на лишнее.
Использую modx3 и заготовку modExtra3.
Что хочу сделать:
1. Отображать список «предметов» на странице компонента, (тут все ок из коробки), запретил только добавление, поскольку надо связывать «предмет» с ресурсом.
2. На вкладке ресурса добавлять «предметы» и сохранять их с указанием id ресурса в бд.
Проблема — не получается добавить вкладку компонента на страницу редактирования ресурса, можете показать как это правильно сделать в моем случае?