Автор: Jozef Sakalos, aka Saki
Статья в оригинале: Создание сложного приложение в Ext на blog.extjs.eu
Я решил написать эту статью для тех пользователей Ext 2.x, которые уже переросли одну единственную HTML-страницу со встроенными скриптом, создающим простое окно или форму, для тех, которые уже решили, что Ext — это их путь и для тех, которые превозмогая трудности связанные с большими объемами кода, понимают, что нуждаются в его структурировании.
Сколько людей, столько и мнений. И поэтому способ, который я опишу ниже, не является одним единственным возможным. Также хотелось бы отметить, что не каждое приложение, написанное с применением этого подхода, является гарантированно хорошим. Ничего подобного.
Замечу, что описываемый подход является работоспособным, четко структурированным, без труда поддерживаемым и, одним словом: рабочим!
Когда вы оперируете объектом Viewport с BorderLayout, grid-таблицей и формой в одном файле, это, конечно же, не сложное приложение, правда? Если же у вас десятки окон, в каждом из которых grid-таблицы, формы или BorderLayout и все это добро раскидано по десяткам файлов, то это уже сложное и большое приложение, верно?
В немецком языке имеется одно милое словечко: Jein = Ja + Nein
Ответом на оба приведенных утверждения будет Jein. Вопрос заключается в следующем: когда приложение становится большим и сложным? Ответ прост: в тот самый момент как вы начинаете его таковым ощущать. Это момент когда становится сложно ориентироваться в большом количестве файлов или начинаются проблемы при поиске определенного места в случае попытки понять отношение компонентов, например, и так далее.
Можно с уверенностью говорить, что каждое приложение сложно столь же насколько простое достойно быть хорошо написанным и может стать по-настоящему большим, как только мы начнем добавлять новый функционал, наращивать объем кода, добавлять правила CSS и т.д.
Лучшей и самой безопасной установкой при создании нового приложения является — «Я начинаю сложное приложение!»
То, что необходимо организовать в первую очередь. В нашем распоряжении всегда имеется каталог DocumentRoot так что все подкаталоги, затронутые ниже, будут приводиться относительно него.
Рекомендуемая структура каталогов:
Слово «ссылка» означает, что каталог является мягкой ссылкой на реальный каталог, где лежат файлы. Преимущество этого приема в том, что, например, вы можете загрузить новую версию Ext в любой реальный каталог и заставить ссылку указывать на него, что избавит от редактирования путей в вашем коде. Можно проводить тестирование новой версии и если все в порядке, оставить ссылку указывать на новую версию, а если нет, то просто вернуть ее назад.
Минимальное содержимое index.html может быть следующим:
Хотя вы и можете работать с файлом подобным приведенному выше, я рекомендую добавлять заголовочную информацию как к этому типу файлов, так и ко всем иным. Маркер конца файла также имеет значение. См. примеры такого рода заголовков.
Нам понадобится файл, где мы сможем поместить onReady обработчик. Пусть его имя будет application.js. Минимальное содержимое такого файла приведено ниже:
Ваши код может иным, но обязательным шагом будет установка Ext.BLANK_IMAGE_URL в значение, ссылающееся на ваш сервер. Это путь к прозрачному 1х1 изображению, которое используется Ext в качестве плэйсхолдера и если он ведет в пустоту, вы можете столкнуться с различными проблемами отрисовки такими как: отсутствие изображения стрелки в Ext.form.ComboBox, иконок и т.п.
Возможно, что вам также понадобится создать глобальную переменную для вашего приложения (в данном случае это Application).
В чем вы должны быть уверенны, так это в том, что onReady обработчик наличествует у вас лишь однажды — в точке входа в приложение.
Поместите в этот файл все свои стили, если они у вас есть. В случае если вам необходимо лишь малое число таковых, то, возможно, не имеет смысла создавать для них отдельный файл. Можно записать их непосредственно в документ, используя
Наоборот, помните, что вы создаете сложное приложение, стало быть, всему свое место. Если вы будете писать стили в заголовок документа, то вам рано или поздно придется решать проблемы с отрисовкой и вы не будете знать какие стили побудили их.
Что обычно следует за тем как был получен некий базис, такой, какой имеется у нас на данном этапе? Начинаем писать код. Итак, мы погрузились в кресло и начали творить:
Одну минуточку. Развивая подобное, мы очень скоро будем иметь все 10000 строк кода в application.js, а это последнее что нам необходимо. Очевидно, был пропущен какой-то этап. Если мы создали такой огромный файл, почему бы нам не вставить его код непосредственно в index.html?
Любое целое, вне зависимости от его размера, состоит из более малых систем, которые, в свою очередь, состоят из еще более малых частей, содержащих некоторые элементы. Ваше разрабатываемое сложное приложение не исключение. И вот сейчас как раз время чтобы определить для себя эти части, компоненты и связи между ними.
Итак, еще более удобно усядьтесь, хорошо подумайте, нарисуйте эскиз, составьте список, не принципиально, что вы будете именно делать, главное чтобы в результате у вас на руках был перечень компонент, из которых будет состоять ваше приложение. По крайней мере, главных из них.
Ну вот, как только вы покончили с анализом и определением составных частей своего приложения можно приступить к написанию одного из них. Как лучше всего это сделать? Наилучшим решением будет написание классов расширяющих стандартные компоненты Ext, ввиду того, что последние уже имеют все настройки, перезаписываемые переданными в их конструкторы значениями. Я называю такие расширения преднастроенными классами т.к. они редко вносят новый функционал и служат в основном для конфигурирования. Примером может служить grid-таблица «Персонал» со своею моделью колонок, хранилищем, настройками сортировки, редакторами и т.д.
В таком случае конфигурация нашего окна могла бы выглядеть следующим образом:
Разберемся на примере:
Что у нас тут происходит? Мы расширяем Ext.grid.GridPanel, создавая новый класс-расширение Application.PersonnelGrid и регистрируем для него новый xtype с именем personnelgrid.
По сути, мы передаем обычной grid-таблице все настройки достаточные для превращения ее в специализированную grid-таблицу «Персонала». Начиная с этого момента, у нас имеется новый компонент, строительный блок нашего приложения, который мы можем использовать где угодно (в окне, на панели, самостоятельно) для отображения списка сотрудников. Создать его можно следующим образом:
или используя xtype (т.н. ленивое создание):
Код приведенный выше не нуждается, да и не будет запускаться в обработчике onReady — он не оперирует DOM, а просто создает JavaScript объект. Следовательно, он может и должен быть помещен в отдельный файл (js/Application.PersonnelGrid.js) и будет включаться в хедер index.html.
Что ж, пока все идет хорошо — у нас почти все готово и все (почти), что нам нужно так это продолжать описывать преднастроенный классы, класть их в ./js, включать в index.html и собирать наше приложение из их экземпляров как кусочки головоломки.
Выглядит неплохо, а?
Представьте себе, что вам необходимо рамочный макет (border layout) со списком ссылок слева (west region) и панелью закладок посередине (center region). Щелчок по ссылке должен создавать новую закладку в центре. Где вы поместите логику всего происходящего, обработчик события и код создания? Слева или в центре?
Нигде. Почему? Если у нас есть преднастроенный класс, который создает и отображает список слева, а мы помещаем логику туда, то его существование теряет смысл без центрального региона. Мы просто не сможем использовать список без панели закладок.
Если же мы поместим логику в центральную область, итог будет схожим: панель закладок не может существовать без списка ссылок.
Есть только один компонент осознающий существование левой и центральной панелей — это их контейнер с рамочным макетированием, единственное правильное место для размещения логики межкомпонентного сообщения.
Ввиду соглашения об организации нашего приложения очень рано мы столкнемся с большим числом JavaScript файлов (в моем случае порядка 80 и их число растет с каждым днем), что может негативно сказаться на производительности реально работающей, не тестовой, системы.
Наилучшим решением является слияние (конкатенация) всех JavaScript файлов в нужном порядке следования итогом чего станет один большой файл, который затем необходимо сжать одним из инструментов минификации или сжатия.
Производственная система будет подключать:
Дополнительная информация по сжатию вашего кода, а также созданию файлов билдов может быть найдена в другом руководстве.
В общем-то это все… Существуют специализированные техники для некоторых классов Ext, много иных серверных и клиентских фишек, но то, что было изложено выше — общая концепция.
Счастливого кодинга!
На правах переводчика:
Статья в оригинале: Создание сложного приложение в Ext на blog.extjs.eu
Предисловие
Я решил написать эту статью для тех пользователей Ext 2.x, которые уже переросли одну единственную HTML-страницу со встроенными скриптом, создающим простое окно или форму, для тех, которые уже решили, что Ext — это их путь и для тех, которые превозмогая трудности связанные с большими объемами кода, понимают, что нуждаются в его структурировании.
Сколько людей, столько и мнений. И поэтому способ, который я опишу ниже, не является одним единственным возможным. Также хотелось бы отметить, что не каждое приложение, написанное с применением этого подхода, является гарантированно хорошим. Ничего подобного.
Замечу, что описываемый подход является работоспособным, четко структурированным, без труда поддерживаемым и, одним словом: рабочим!
Что значит сложное приложение?
Когда вы оперируете объектом Viewport с BorderLayout, grid-таблицей и формой в одном файле, это, конечно же, не сложное приложение, правда? Если же у вас десятки окон, в каждом из которых grid-таблицы, формы или BorderLayout и все это добро раскидано по десяткам файлов, то это уже сложное и большое приложение, верно?
В немецком языке имеется одно милое словечко: Jein = Ja + Nein
Ответом на оба приведенных утверждения будет Jein. Вопрос заключается в следующем: когда приложение становится большим и сложным? Ответ прост: в тот самый момент как вы начинаете его таковым ощущать. Это момент когда становится сложно ориентироваться в большом количестве файлов или начинаются проблемы при поиске определенного места в случае попытки понять отношение компонентов, например, и так далее.
Можно с уверенностью говорить, что каждое приложение сложно столь же насколько простое достойно быть хорошо написанным и может стать по-настоящему большим, как только мы начнем добавлять новый функционал, наращивать объем кода, добавлять правила CSS и т.д.
Лучшей и самой безопасной установкой при создании нового приложения является — «Я начинаю сложное приложение!»
Файлы и директории
То, что необходимо организовать в первую очередь. В нашем распоряжении всегда имеется каталог DocumentRoot так что все подкаталоги, затронутые ниже, будут приводиться относительно него.
Рекомендуемая структура каталогов:
- ./css (опциональная link)
- ./ext (ссылка)
- ./img (ссылка)
- ./js
- index.html
Слово «ссылка» означает, что каталог является мягкой ссылкой на реальный каталог, где лежат файлы. Преимущество этого приема в том, что, например, вы можете загрузить новую версию Ext в любой реальный каталог и заставить ссылку указывать на него, что избавит от редактирования путей в вашем коде. Можно проводить тестирование новой версии и если все в порядке, оставить ссылку указывать на новую версию, а если нет, то просто вернуть ее назад.
- css — хранилище всех ваших стилей. Если у вас имеются глобальные стили, вроде цветов компании или оформления шрифтов, то вы также можете создать этот каталог как ссылку;
- ext — ссылка на используемую вами версию Ext, как это было описано ранее;
- img — каталог с вашими изображениями. Также может содержать подкаталоги для иконок и т.п.;
- js — будет содержать все JavaScript файлы приложения, а также их скомпонованную версию;
- index.html — HTML-файл являющийся входной точкой вашего приложения. Вы можете назвать его как угодно, да и быть html-файлов может несколько, например, для операции аутентификации. Но в любом случае файл входной точки (с обработчиком onReady) должен быть один;
- дополнительно вы можете создать директорию или ссылку на серверную часть приложения (./classes в моем случае). Можно дать ей какое угодно имя, но следует выбрать согласующееся со всеми приложениями, которые вы разрабатываете (имена ./server, ./php будут неплохими вариантами).
index.html
Минимальное содержимое index.html может быть следующим:
<html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <link rel="stylesheet" type="text/css" href="./ext/resources/css/ext-all.css"> <link rel="stylesheet" type="text/css" href="./css/application.css"> <script type="text/javascript" src="./ext/adapter/ext/ext-base.js"></script> <script type="text/javascript" src="./ext/ext-all-debug.js"></script> <script type="text/javascript" src="./application.js"></script> <title>A Big Application</title> </head> <body></body> </html>
Хотя вы и можете работать с файлом подобным приведенному выше, я рекомендую добавлять заголовочную информацию как к этому типу файлов, так и ко всем иным. Маркер конца файла также имеет значение. См. примеры такого рода заголовков.
js/application.js
Нам понадобится файл, где мы сможем поместить onReady обработчик. Пусть его имя будет application.js. Минимальное содержимое такого файла приведено ниже:
// vim: sw=4:ts=4:nu:nospell:fdc=4 /** * An Application * * @author Ing. Jozef Sakalos * @copyright (c) 2008, by Ing. Jozef Sakalos * @date 2. April 2008 * @version $Id$ * * @license application.js is licensed under the terms of the Open Source * LGPL 3.0 license. Commercial use is permitted to the extent that the * code/component(s) do NOT become part of another Open Source or Commercially * licensed development library or toolkit without explicit permission. * * License details: http://www.gnu.org/licenses/lgpl.html */ /*global Ext, Application */ Ext.BLANK_IMAGE_URL = './ext/resources/images/default/s.gif'; Ext.ns('Application'); // application main entry point Ext.onReady(function() { Ext.QuickTips.init(); // code here }); // eo function onReady // eof
Ваши код может иным, но обязательным шагом будет установка Ext.BLANK_IMAGE_URL в значение, ссылающееся на ваш сервер. Это путь к прозрачному 1х1 изображению, которое используется Ext в качестве плэйсхолдера и если он ведет в пустоту, вы можете столкнуться с различными проблемами отрисовки такими как: отсутствие изображения стрелки в Ext.form.ComboBox, иконок и т.п.
Возможно, что вам также понадобится создать глобальную переменную для вашего приложения (в данном случае это Application).
В чем вы должны быть уверенны, так это в том, что onReady обработчик наличествует у вас лишь однажды — в точке входа в приложение.
css/application.css
Поместите в этот файл все свои стили, если они у вас есть. В случае если вам необходимо лишь малое число таковых, то, возможно, не имеет смысла создавать для них отдельный файл. Можно записать их непосредственно в документ, используя
.
Наоборот, помните, что вы создаете сложное приложение, стало быть, всему свое место. Если вы будете писать стили в заголовок документа, то вам рано или поздно придется решать проблемы с отрисовкой и вы не будете знать какие стили побудили их.
Как делать не надо
Что обычно следует за тем как был получен некий базис, такой, какой имеется у нас на данном этапе? Начинаем писать код. Итак, мы погрузились в кресло и начали творить:
var vp = new Ext.Viewport({ layout:'border' ,items:[ new Ext.grid.GridPanel({ store:new Ext.data.Store({ proxy:new Ext.data.HttpProxy({ ...
Одну минуточку. Развивая подобное, мы очень скоро будем иметь все 10000 строк кода в application.js, а это последнее что нам необходимо. Очевидно, был пропущен какой-то этап. Если мы создали такой огромный файл, почему бы нам не вставить его код непосредственно в index.html?
Правильный путь: разделяй и властвуй
Любое целое, вне зависимости от его размера, состоит из более малых систем, которые, в свою очередь, состоят из еще более малых частей, содержащих некоторые элементы. Ваше разрабатываемое сложное приложение не исключение. И вот сейчас как раз время чтобы определить для себя эти части, компоненты и связи между ними.
Итак, еще более удобно усядьтесь, хорошо подумайте, нарисуйте эскиз, составьте список, не принципиально, что вы будете именно делать, главное чтобы в результате у вас на руках был перечень компонент, из которых будет состоять ваше приложение. По крайней мере, главных из них.
Преднастроенные классы
Ну вот, как только вы покончили с анализом и определением составных частей своего приложения можно приступить к написанию одного из них. Как лучше всего это сделать? Наилучшим решением будет написание классов расширяющих стандартные компоненты Ext, ввиду того, что последние уже имеют все настройки, перезаписываемые переданными в их конструкторы значениями. Я называю такие расширения преднастроенными классами т.к. они редко вносят новый функционал и служат в основном для конфигурирования. Примером может служить grid-таблица «Персонал» со своею моделью колонок, хранилищем, настройками сортировки, редакторами и т.д.
В таком случае конфигурация нашего окна могла бы выглядеть следующим образом:
var win = new Ext.Window({ title:'Personnel' ,widht:600 ,height:400 ,items:{xtype:'personnelgrid'} }); win.show();
Написание преднастроенного класса
Разберемся на примере:
Application.PersonnelGrid = Ext.extend(Ext.grid.GridPanel, { border:false ,initComponent:function() { Ext.apply(this, { store:new Ext.data.Store({...}) ,columns:[{...}, {...}] ,plugins:[...] ,viewConfig:{forceFit:true} ,tbar:[...] ,bbar:[...] }); Application.PersonnelGrid.superclass.initComponent.apply(this, arguments); } // eo function initComponent ,onRender:function() { this.store.load(); Application.PersonnelGrid.superclass.onRender.apply(this, arguments); } // eo function onRender }); Ext.reg('personnelgrid', Application.PersonnelGrid);
Что у нас тут происходит? Мы расширяем Ext.grid.GridPanel, создавая новый класс-расширение Application.PersonnelGrid и регистрируем для него новый xtype с именем personnelgrid.
По сути, мы передаем обычной grid-таблице все настройки достаточные для превращения ее в специализированную grid-таблицу «Персонала». Начиная с этого момента, у нас имеется новый компонент, строительный блок нашего приложения, который мы можем использовать где угодно (в окне, на панели, самостоятельно) для отображения списка сотрудников. Создать его можно следующим образом:
var pg = new Application.PersonnelGrid();
или используя xtype (т.н. ленивое создание):
var win = new Ext.Window({ items:{xtype:'personnelgrid'} ,.... });
Организация и хранение преднастроенных классов
Код приведенный выше не нуждается, да и не будет запускаться в обработчике onReady — он не оперирует DOM, а просто создает JavaScript объект. Следовательно, он может и должен быть помещен в отдельный файл (js/Application.PersonnelGrid.js) и будет включаться в хедер index.html.
Что ж, пока все идет хорошо — у нас почти все готово и все (почти), что нам нужно так это продолжать описывать преднастроенный классы, класть их в ./js, включать в index.html и собирать наше приложение из их экземпляров как кусочки головоломки.
Выглядит неплохо, а?
Межкомпонентное сообщение
Представьте себе, что вам необходимо рамочный макет (border layout) со списком ссылок слева (west region) и панелью закладок посередине (center region). Щелчок по ссылке должен создавать новую закладку в центре. Где вы поместите логику всего происходящего, обработчик события и код создания? Слева или в центре?
Нигде. Почему? Если у нас есть преднастроенный класс, который создает и отображает список слева, а мы помещаем логику туда, то его существование теряет смысл без центрального региона. Мы просто не сможем использовать список без панели закладок.
Если же мы поместим логику в центральную область, итог будет схожим: панель закладок не может существовать без списка ссылок.
Есть только один компонент осознающий существование левой и центральной панелей — это их контейнер с рамочным макетированием, единственное правильное место для размещения логики межкомпонентного сообщения.
Производственная система
Ввиду соглашения об организации нашего приложения очень рано мы столкнемся с большим числом JavaScript файлов (в моем случае порядка 80 и их число растет с каждым днем), что может негативно сказаться на производительности реально работающей, не тестовой, системы.
Наилучшим решением является слияние (конкатенация) всех JavaScript файлов в нужном порядке следования итогом чего станет один большой файл, который затем необходимо сжать одним из инструментов минификации или сжатия.
Производственная система будет подключать:
- ext-all.js
- app-all.js
- application.js
Дополнительная информация по сжатию вашего кода, а также созданию файлов билдов может быть найдена в другом руководстве.
Заключение
В общем-то это все… Существуют специализированные техники для некоторых классов Ext, много иных серверных и клиентских фишек, но то, что было изложено выше — общая концепция.
Счастливого кодинга!
На правах переводчика:
- Статья зацепила меня тем, что описывает общую концепцию работы со сложным приложением в конкретном случае разработки в ExtJS;
- далее перевод планируется поместить в официальный мануал проекта рядом с оригиналом, поэтому просьба реагировать не только плюсами и минусами, но и в текстовом виде, пожалуйста;
- кажущийся странным танец вокруг ссылок при описании файловой организации обусловлен тем, что сами разработчики ExtJS интенсивно используют построение собранных из множества файлов билдов. В таком случае новая версия того же ExtJS это как все исходное дерево, так и билд фреймворка. Гораздо удобнее, конечно, изменить ссылку, чем копировать туда-сюда много файлов.