| Summary: Это руководство поможет вам в разработке сложного приложения Ext. |
| Author: Jozef Sakalos (перевод: Kupuyc) |
| Published: 30 мая 2008 г. |
| Ext Version: 2.0+ |
Languages: English Русский
|
Contents |
ПредисловиеЯ решил написать эту статью для тех пользователей Ext 2.x, которые уже переросли одну единственную HTML-страницу со встроенными скриптом, создающим простое окно или форму, для тех, которые уже решили, что Ext — это их путь и для тех, которые превозмогая трудности связанные с большими объемами кода, понимают, что нуждаются в его структурировании.
Сколько людей, столько и мнений. И поэтому способ, который я опишу ниже, не является одним единственным возможным. Также хотелось бы отметить, что не каждое приложение, написанное с применением этого подхода, является гарантированно хорошим. Ничего подобного.
Замечу, что описываемый подход является работоспособным, четко структурированным, без труда поддерживаемым и, одним словом: рабочим!
Что значит сложное приложение?Когда вы оперируете объектом Viewport с BorderLayout, grid-таблицей и формой в одном файле, это, конечно же, не сложное приложение, правда? Если же у вас десятки окон, в каждом из которых grid-таблицы, формы или BorderLayout и все это добро раскидано по десяткам файлов, то это уже сложное и большое приложение, верно?
В немецком языке имеется одно милое словечко: Jein = Ja + Nein
Ответом на оба приведенных утверждения будет Jein. Вопрос заключается в следующем: когда приложение становится большим и сложным? Ответ прост: в тот самый момент как вы начинаете его таковым ощущать. Это момент когда становится сложно ориентироваться в большом количестве файлов или начинаются проблемы при поиске определенного места в случае попытки понять отношение компонентов, например, и так далее.
Можно с уверенностью говорить, что каждое приложение сложно столь же насколько простое достойно быть хорошо написанным и может стать по-настоящему большим, как только мы начнем добавлять новый функционал, наращивать объем кода, добавлять правила CSS и т.д.
Лучшей и самой безопасной установкой при создании нового приложения является — «Я начинаю сложное приложение!»
Файлы и директорииТо, что необходимо организовать в первую очередь. В нашем распоряжении всегда имеется каталог DocumentRoot так что все подкаталоги, затронутые ниже, будут приводиться относительно него.
Рекомендуемая структура каталогов:
./css (optionally link) ./ext (link) ./img (link) ./js index.html
Слово «ссылка» означает, что каталог является мягкой ссылкой на реальный каталог, где лежат файлы. Преимущество этого приема в том, что, например, вы можете загрузить новую версию Ext в любой реальный каталог и заставить ссылку указывать на него, что избавит от редактирования путей в вашем коде. Можно проводить тестирование новой версии и если все в порядке, оставить ссылку указывать на новую версию, а если нет, то просто вернуть ее назад.
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="./js/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Поместите в этот файл все свои стили, если они у вас есть. В случае если вам необходимо лишь малое число таковых, то, возможно, не имеет смысла создавать для них отдельный файл. Можно записать их непосредственно в документ, используя <style>.
Наоборот, помните, что вы создаете сложное приложение, стало быть, всему свое место. Если вы будете писать стили в заголовок документа, то вам рано или поздно придется решать проблемы с отрисовкой и вы не будете знать какие стили побудили их.
Как делать не надоЧто обычно следует за тем как был получен некий базис, такой, какой имеется у нас на данном этапе? Начинаем писать код. Итак, мы погрузились в кресло и начали творить:
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'} ,.... });
Организация и хранение преднастроенных классовКод нашей grid-таблицы не нуждается, да и не будет запускаться в обработчике onReady — он не оперирует DOM, а просто создает JavaScript объект. Следовательно, он может и должен быть помещен в отдельный файл (js/Application.PersonnelGrid.js) и будет включаться в хедер index.html:
<script type="text/javascript" src="./js/Application.PersonnelGrid.js></script>
Что ж, пока все идет хорошо — у нас почти все готово и все (почти), что нам нужно так это продолжать описывать преднастроенный классы, класть их в ./js, включать в index.html и собирать наше приложение из их экземпляров как кусочки головоломки.
Выглядит неплохо, а?
Межкомпонентное сообщениеПредставьте себе, что вам необходимо рамочный макет (border layout) со списком ссылок слева (west region) и панелью закладок посередине (center region). Щелчок по ссылке должен создавать новую закладку в центре. Где вы поместите логику всего происходящего, обработчик события и код создания? Слева или в центре?
Нигде. Почему? Если у нас есть преднастроенный класс, который создает и отображает список слева, а мы помещаем логику туда, то его существование теряет смысл без центрального региона. Мы просто не сможем использовать список без панели закладок.
Если же мы поместим логику в центральную область, итог будет схожим: панель закладок не может существовать без списка ссылок.
Есть только один компонент осознающий существование левой и центральной панелей — это их контейнер с рамочным макетированием, единственное правильное место для размещения логики межкомпонентного сообщения.
Как же нам тогда поступить? Контейнер с рамочным макетом должен быть «подписан» на событие клика по элементу списка в левой панели и создавать, в ответ на это, закладку в центре. Увидеть такое взаимодействие можно в одном из примеров серии «Примеры Ext от Saki».
Производственная системаВвиду соглашения об организации нашего приложения очень рано мы столкнемся с большим числом JavaScript файлов (в моем случае порядка 80 и их число растет с каждым днем), что может негативно сказаться на производительности реально работающей, не тестовой, системы.
Наилучшим решением является слияние (конкатенация) всех JavaScript файлов в нужном порядке следования итогом чего станет один большой файл, который затем необходимо сжать одним из инструментов минификации или сжатия.
Производственная система будет подключать:
Дополнительная информация по сжатию вашего кода, а также созданию файлов билдов может быть найдена в другом руководстве.
ЗаключениеВ общем-то это все… Существуют специализированные техники для некоторых классов Ext, много иных серверных и клиентских фишек, но то, что было изложено выше — общая концепция.
Счастливого кодинга!
См. также