Разработка
В предыдущей статье
, я начал рассказ о технологии, которая позволяет гибко конфигурировать Flex приложение, где рассмотрел первый аспект, а именно связывание различных компонентов приложения в одно целое. В данной статье хочу подробнее остановиться на организации модели обмена данными в приложении созданном с использованием Parsley
.
Чтобы создать действительно отторжимые модули, в приложении должна быть создана грамотная система обмена данными. Как мне кажется в Parsley есть всё, чтобы построение такой системы было максимально удобным и лёгким в использовании.
В Parsley предусмотрено два варианта инициализации процесса передачи данных и три варианта получения данных. Рассмотрим каждый из них. Начнём именно с вариантов инициализации передачи данных.
Первый вариант использует уже существующий механизм Flex -dispatchEvent. Но к этому добавляется один момент - мы должны указать Parsley, какие события он должен "слушать". Делается это добавлением мета-нформации в начале класса, сам класс должен, естественно расширять EventDispatcher. Рассмотрим данный вариант на примере класса ContactPanel.
<?xml version="1.0" ?>
<mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml"
xmlns="ru.gubber.sample.flex.view.component.*" addedToStage="init(event)">
<mx:Metadata>
[ManagedEvents("createContactEvent, showContactEvent, copyContactEvent, deleteContactEvent,showListEvent")]
</mx:Metadata>
<mx:Script><![CDATA[
import mx.collections.ArrayCollection;
import ru.gubber.sample.flex.model.Contact;
import ru.gubber.sample.flex.view.event.ApplicationEvent;
[MessageBinding(messageProperty="list",type="ru.gubber.sample.flex.controller.massages.ContactListMessage")]
[Bindable]
public var list:ArrayCollection;
private var inited:Boolean = false;
private function init(event:Event):void {
if (!inited) {
inited = true;
dispatchEvent(new Event('configureIOC', true));
showList();
}
}
private function showList():void {
dispatchEvent(new ApplicationEvent(ApplicationEvent.SHOW_LIST_EVENT))
}
private function createNew():void {
dispatchEvent(new ApplicationEvent(ApplicationEvent.CREATE_CONTACT_EVENT))
}
private function createCopy():void {
dispatchEvent(new ApplicationEvent(ApplicationEvent.COPY_CONTACT_EVENT, table.selectedItem as Contact))
}
private function makeDelete():void {
dispatchEvent(new ApplicationEvent(ApplicationEvent.DELETE_CONTACT_EVENT, table.selectedItem as Contact))
}
private function showContact():void {
dispatchEvent(new ApplicationEvent(ApplicationEvent.SHOW_CONTACT_EVENT, table.selectedItem as Contact))
}
[MessageHandler(selector="updateContactSuccess")]
public function saveContactHandler():void {
showList();
}
]]></mx:Script>
<PagedDataGrid id="table" pageSizes="[10, 20, 30, 50, 100]" origData="{list}" width="100%" height="100%">
<columns>
<mx:DataGridColumn dataField="id" headerText="№"/>
<mx:DataGridColumn dataField="name" headerText="Имя"/>
<mx:DataGridColumn dataField="email" headerText="e-mail"/>
<mx:DataGridColumn dataField="phone" headerText="Телефон"/>
</columns>
</PagedDataGrid>
<mx:HBox width="100%" horizontalAlign="center">
<mx:LinkButton label="Добавить" click="createNew()"/>
<mx:LinkButton label="Обновить" enabled="{table.selectedItem != null}" click="showContact()"/>
<mx:LinkButton label="Копировать" enabled="{table.selectedItem != null}" click="createCopy()"/>
<mx:LinkButton label="Удалить" enabled="{table.selectedItem != null}" click="makeDelete()"/>
</mx:HBox>
<ContactForm height="140" width="100%"/>
</mx:VBox>
В данном случае код приведён без "купюр". Объявление необходимых в обработке событий описывается в 6 строке. В то же время генерация событий происходит в с 27 строки по 45. В каждом из методов не делается ровным счётом ничего, кроме генерации события с тем набором данных, которые надо передать в другой компонент.
Второй метод инициализации процесса передачи данных рассмотрим на примере класса LocalContactService.
package ru.gubber.sample.flex.controller.services {
...
public class LocalContactService implements IContactService{
[MessageDispatcher]
public var dispatcher:Function;
private var connection:SQLConnection = new SQLConnection();
private var dbFile:File;
public var fileName:String;
private var exists:Boolean = true;
public function LocalContactService() {
super();
}
[PostConstruct]
public function init() : void {
if (!fileName) {
fileName = "default.db";
exists = false;
}
dbFile = File.applicationStorageDirectory.resolvePath(fileName);
}
[PreDestroy]
public function dispose() : void {
if ((connection) && (connection.connected)) {
connection.commit();
}
connection.close();
}
public function tryBD():void {
if ((!exists) && (!dbFile.exists))
dispatcher(new ContactMessage(ContactMessage.DB_ERROR_MESSAGE));
else {
try {
connection.open(dbFile, SQLMode.UPDATE);
connection.addEventListener(SQLErrorEvent.ERROR, function (event:SQLErrorEvent):void {
dispatcher(new ContactMessage(ContactMessage.DB_ERROR_MESSAGE));
});
var stm:SQLStatement = new SQLStatement();
stm.sqlConnection = connection;
stm.text = "SELECT count(*) FROM CONTACTS";
stm.addEventListener(SQLErrorEvent.ERROR, function (event:SQLErrorEvent):void {
dispatcher(new ContactMessage(ContactMessage.DB_ERROR_MESSAGE));
});
stm.addEventListener(SQLEvent.RESULT, function (event:SQLEvent):void {
dispatcher(new ContactMessage(ContactMessage.DB_SUCCESS_MESSAGE));
});
stm.execute();
} catch(e:Error) {
Alert.show(e.message);
dispatcher(new ContactMessage(ContactMessage.DB_ERROR_MESSAGE));
}
}
}
...
}
}{/code }
В данном примере стоит обратить внимание на 6 и 7 строку, где внедряется функция, которая будет инициировать процесс передачи сообщения в другой модуль. В данном примере это происходит на 38, 51, 54 и 60 строках, где генерируется событие об успешном или ошибочном завершении запроса к таблице в нашей БД. В данном случае стоит отметить, что класс <strong>ContactMessage</strong> <em>НЕ</em> является наследником класса <strong>Event</strong>.
Так же можно отметить внимание на директивы на 18 и 27 строках, которые используются для организации жизненного цикла компонента. В первом случае указывается метод, который будет выполнятся после создания этого объекта, второй метод будет выполнятся перед тем, как будет выгружен из контекста.
Теперь, когда мы умеем отправлять сообщения с данными, нам надо научиться принимать эти данные.
Первый способ - прямое связывание данных из сообщения/события с определённым объектом(<strong>MessageBinding</strong>). Для этого обратимся к строчкам 21-23 класса <strong>ContactForm</strong>, где используется данный вариант отслеживания данных.
{code class="brush:as3" }[MessageBinding(messageProperty="item", type="ru.gubber.sample.flex.controller.massages.ContactMessage")]
[Bindable]
public var contact:Contact;
Директива MessageBinding, сообщает, что эта переменная будет принимать то же значение, что и свойство item для любого объекта ContactMessage, которое было инициализировано одним способов передачи данных, описанных ранее. Что приводит нас к мысли, что надо чётко понимать - использование этого варианта чревато ошибками, когда событие данного типа генерируется для отличных от установления данной переменной целей. С другой стороны его можно использовать когда данные не требуют дополнительной обработки.
Второй вариант - обработка сообщений (MessageHadler). Для этого обратимся к 19-23 строкам класса ContactAction.
[MessageHandler(selector="tryDbEvent")]
public function tryDBHandler(contMessage:ApplicationEvent):void {
service.tryBD();
}
Первое, что надо иметь в виду, что так же как и связывание объектов, обработчик сообщений привязывается по КЛАССУ сообщения/события. Другими словами если использовать синтаксис
[MessageHandler]
public function myHandler(contMessage:ApplicationEvent):void {
service.tryBD();
}
Тогда функция myHandler будет обрабатывать ВСЕ события класса ApplicationEvent. Если же необходимо обрабатывать строго определённые события, то для этого так же как и при связывании используется механизм выбора. Только в данном случае есть небольшое отличие при обработке событий (наследники Event) и собственных сообщений (не наследники Event). Для событий поле, по которому происходит выбор - это поле type. В то же время для своих сообщений вы это поле должны указать сами. используя директиву [Selector], как это сделано в классе ContactMessage.
[Selector]
public var type : String;
Если мы вернёмся к описанию метода tryDBHandler. То теперь мы понимаем, что данный метод должен обрабатывать события ApplicationEvent, у которых поле type принимает значение "tryDbEvent".
Последний метод обработки событий - перхватчик события (MessageInterceptor). В данном примере я его не использовал. Суть данного метода сводиться к тому, что перед тем как событие будет обработано либо первым либо вторым способом это событие можно перехватить, обработать и самое главное - отправить событие дальше в обработку или запретить его дальнейшую обработку. Этот метод, как и несколько других моментов, которые не попадут в текущую серию заметок, я постараюсь рассмотреть отдельно.
Следующая статья будет завершать краткий обзор библиотеки Parsley. В нём я постараюсь изложить своё видение того, как можно грамотно организовать приложение.
Добавить комментарий