Автор: Stuart Eccles
Перевод: Андрей Горбатов
Оригинал статьи
В этой части урока мы создадим Flex компонент, добавим состояния и переходы (transitions) между ними.
Мы будем работать с тем же приложением rails, что и в первой части урока.
Для начала внесем небольшие изменения в контроллер Reviews:
class ReviewsController < ApplicationController
def list
@reviews = Review.find :all
render :xml => @reviews.to_xml
end
def create
@review = Review.new(params[:review])
@review.save
render :xml => @review.to_xml
end
end
Мы просто сделали так, чтобы контроллер reviews возвращал что-нибудь
назад в ответ на запрос (Flex любит ответы на свои HTTPService).
Теперь создадим новый Flex проект во Flex Builder.
Flex позволяет вам разбить UI на компоненты. Итак, мы собираемся
создать компонент Reviews, который потом сможем использовать во Flex
приложениях.
Создайте в проекте папку frComponents.
Теперь выберите эту папку и, нажав правую кнопку мыши, выберите File > New > MXMLComponent. Теперь вы можете создать новый компонент reviewsPanel. Введите “reviewsPanel” в качестве имени. Выберите Panel (Прим. пер.: я выбрал Vbox, так как с Panel какие-то проблемы) для основы компонента и vertical для лэйаута.

Перед вами содержимое файла компонента:
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" layout="vertical">
</mx:Panel>
Измените его на следующий код:
<?xml version="1.0" encoding="utf-8"?>
<mx:Panel title="Reviews" xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" creationComplete="reviewRequest.send()">
<mx:HTTPService id="reviewRequest" url="http://localhost:3000/reviews/list" useProxy="false"/>
<mx:DataGrid id="dgReviews" width="800" dataProvider="{reviewRequest.lastResult.reviews.review}" editable="false" >
<mx:columns>
<mx:DataGridColumn headerText="Title" dataField="title" width="480"/>
<mx:DataGridColumn headerText="Author" dataField="author" width="240"/>
<mx:DataGridColumn headerText="Score" dataField="score" width="80"/>
</mx:columns>
</mx:DataGrid>
<mx:TextArea id="taReviews" width="800" htmlText="{dgReviews.selectedItem.text}"/>
<mx:ControlBar id="cbReviewControlBar">
<mx:Button label="Create New" id="btnCreateNew"/>
</mx:ControlBar>
</mx:Panel>
Как и в первом уроке, здесь создается DataGrid, отображающая все данные таблицы reviews, полученные из метода контроллера Review list.
Теперь нам надо поместить этот компонент в основном файле приложения. Откройте основной файл приложения (это должен быть имя_проекта.mxml)
Замените его код на следующий:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" layout="absolute" xmlns:fr="frComponents.*">
<fr:reviewsPanel x="0" y="0">
</fr:reviewsPanel>
</mx:Application>
xmlns:fr=”frComponents.*” -
объявление пространства имен, указвающее Flex на то, что загружать
компоненты надо из папки frComponents. Теперь компоненты могут быть
добавлены с помощью конструкции пространство_имен:название_компонента
Убедитесь, что Rails приложение запущено и запустите Flex проект. Результат показан на рисунке:

В отличие от певрого урока, нам необходимо добавить форму добавлени
нового обзора Add Review как новое состояние. Flex использует состояния
для создания различных видов приложения, между которыми можно
переключатся в зависимости от действий пользователя. Вы можете иметь
различные состояния для компоннетов, которые можно обрабатывать из
модели состояний приложения.
Добавим новый HTTPService компоненту. Введите следующий код после существующего HTTPService.
<mx:HTTPService contentType="application/xml" id="reviewCreateRequest" result="reviewRequest.send();" url="http://localhost:3000/reviews/create" useProxy="false" method="POST">
<mx:request xmlns="">
<review>
<title>{fTitle.text}</title>
<author>{fAuthor.text}</author>
<score>{fScore.value}</score>
<text>{fText.text}</text>
</review>
</mx:request>
</mx:HTTPService>
Параметр request=”reviewRequest.send();” обновляет DataGrid из
базы данных после того, как создание нового ряда завершено.
Добавьте следующий код нового состояния Form в конец файла компонента:
<mx:states>
<mx:State name="Create">
<mx:AddChild position="lastChild">
<mx:Form width="800" id="frmCreate">
<mx:FormHeading label="Add a new Review"/>
<mx:FormItem label="Title" required="true">
<mx:TextInput width="260" id="fTitle"/>
</mx:FormItem>
<mx:FormItem label="Author" required="true">
<mx:TextInput width="140" id="fAuthor"/>
</mx:FormItem>
<mx:FormItem label="Score">
<mx:NumericStepper id="fScore"/>
</mx:FormItem>
<mx:FormItem label="Text">
<mx:TextArea width="600" height="200" id="fText"/>
</mx:FormItem>
<mx:FormItem direction="horizontal">
<mx:Button label="Submit" click="reviewCreateRequest.send();currentState=''"/>
<mx:Button label="Cancel" click="currentState=''"/>
</mx:FormItem>
</mx:Form>
</mx:AddChild>
</mx:State>
</mx:states>
Этот код добавляет новое состояние ‘Create’. Это состояние использует тег mx:AddChild
для добавления нового дочернего элемента в конец компонента. То есть
последним дочерним элементом контейнера Panel будет добавлена форма.
Если это состяоние неактивно, то форма отображаться не будет.
Форма создана немного лучше, чем в первой части. Вообще, использование тега mx:FormItem – это лучший способ расположить элементы формы во Flex.
Теперь нам надо получить возможность изменять состояния. Добавим в ControlBar кнопку:
<mx:Button label="Create New" id="btnCreateNew" click="currentState='Create'"/>
currentState= позволяет нам переключать состояния. currentState=’‘ переключает в исходное состояние.
Итак, запустите проект и нажмите кнопку Create New. Результат показан на рисунке:

Хорошо, но немного скучно. Но Flex позволяет вам добавлять эффекты
при переключении между состояниями. Итак, добавим эффект WideDown для
перехода между состояниями.
Добавьте следующий код после закрывающего тега mx:states:
<mx:transitions>
<mx:Transition id="createTransition" fromState="*" toState="Create">
<mx:Parallel target="{frmCreate}">
<mx:WipeDown duration="1000"/>
</mx:Parallel>
</mx:Transition>
</mx:transitions>
Простой эффект WipeDown длится 1000 мс при переходе в состояние Create.
Теперь настало время создать возможность удаления существующих рядов таблицы. Самый простой метод – выбрать ряд и нажать на кнопку удаления, которая пошлет запрос контроллеру rails. То есть мы должны добавить новый метод в ReviewController:
def delete
@review = Review.find(params[:id])
@review.destroy
render :xml => @review.to_xml
end
Этот метод ищет элемент с Id, посланным в запросе в параметре HTTP id и затем вызывает метод destroy, который удаляет соответствующий ряд из базы данных. Добавим еще один HTTPService к нашему компоненту:
<mx:HTTPService id="reviewDeleteRequest" result="reviewRequest.send();" url="http://localhost:3000/reviews/delete" useProxy="false"/>
И еще одну кнопку:
<mx:Button label="Delete" click="reviewDeleteRequest.send({id: dgReviews.selectedItem.id}); />
Методы HTTPService send() позволяют вам задавать HTTP параметры,
соответствующие свойствам объекта ActionScript. Это осуществляется с
помощью синтаксиса {id: dgReviews.selectedItem.id} (позже мы проделаем это с использованием другого синтаксиса.)
В принципе, все хорошо и правильно, но необходимо добавить
подтверждение удаления. Чтобы это осуществить, добавим следующий
ActionScript код в наш компонент:
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.CloseEvent;
private function deleteHandler(event:Event) : void
{
Alert.show("Are you sure you want to delete this item?", "Delete Item", 3, this,
function(event:CloseEvent):void
{
if (event.detail==Alert.YES)
reviewDeleteRequest.send({id: dgReviews.selectedItem.id});
});
}
]]>
</mx:Script>
И заменим событие click кнопки на вызов этой функции:
<mx:Button label="Delete" click="deleteHandler(event);"/>
Теперь при удалении элемента, мы должны сначала это подтвердить:

Теперь, чтобы создать полный CRUD (Create - создание, Read - чтение, Update - обновление, Delete
- удаление) необходимо позволить пользователям обновлять данные.
Конечно, мы можем создать другую форму, в которой можно будет
редактировать наши данные, но это будет очень просто, и мы многому не
научимся. Оставлю это в качестве вашего домашнего задания. А мы тем
временем будем использовать редактируемый DataGrid. Это немного сложнее.
Сначала добавим новое действие в контроллер Rails:
def update
@review = Review.find(params[:id])
@review.update_attributes(params[:review])
render :xml => @review.to_xml
end
Этот метод ищет ряд таблицы, определенный HTTP параметром id и затем
обновляет все атрибуты, переданные в запросе. Любые HTTP параметры,
заданные как “review[attname] ” изменяются с помощью Rails, чтобы указать на params:review. Параметрам params[:review], переданным методу update_attributes будут переданы значения, определенные в HTTP запросе с помощью “review[]”.
Нам необходим новый HTTPService, чтобы вызвать этот метод Rails:
<mx:HTTPService id="reviewUpdateRequest" result="reviewRequest.send();" url="http://localhost:3000/reviews/update" useProxy="false" method="POST"/>
Теперь нам нужен код, который сделает DataGrid редактируемой:
<mx:Script>
<![CDATA[
import mx.controls.Alert;
import mx.events.DataGridEvent
private function updateHandler(event:Event) : void
{
dgReviews.editable=true
btnUpdateData.label = "End Updates"
btnUpdateData.removeEventListener('click', updateHandler)
btnUpdateData.addEventListener('click', endUpdateHandler)
}
private function endUpdateHandler(event:Event) : void
{
dgReviews.editable=false
btnUpdateData.label = "Update Data"
btnUpdateData.removeEventListener('click', endUpdateHandler)
btnUpdateData.addEventListener('click', updateHandler)
}
public function checkUpdate(event:DataGridEvent) : void
{
if (event != null)
{
if (event.reason == DataGridEventReason.CANCELLED ||
event.reason == DataGridEventReason.OTHER )
return;
var params:Object = new Object();
params['id'] = event.currentTarget.editedItemRenderer.data['id']
params['review[' + event.dataField +']'] = TextInput(event.currentTarget.itemEditorInstance).text
reviewUpdateRequest.send(params);
}
}
]]>
</mx:Script>
А также новая кнопка:
<mx:Button label="Update Data" click="updateHandler(event)" id="btnUpdateData"/>
При нажатии на эту кнопку, DataGrid становится редактируемой и изменяется подпись кнопки на “End Updates”. При повторном нажатии, DataGrid становится вновь нередактируемой. Необходимо только вызывать метод checkUpdate по окончанию редактирования ячейки таблицы.
<mx:DataGrid id="dgReviews" width="800" dataProvider="{reviewRequest.result.reviews.review}" editable="false" itemEditEnd="checkUpdate(event);">
Теперь нажмите на кнопку Update Data и редактируйте ячейки простым нажатием на них:

Надеюсь, вам понравилось. В будущем мы будем интегрировать Flex с сессиями Rails. Думаю, будет весело.
Было несколько вопросов, касающихся размещения Flex приложение на сервере.
Если приложение запускается с того же домена, что и Rails приложение,
вы можете изменить пути в ссылках в HTTPService на относительные.
От:
<mx:HTTPService id="reviewUpdateRequest" result="reviewRequest.send();"
url="http://localhost:3000/reviews/update" useProxy="false" method="POST"/>
На:
<mx:HTTPService id="reviewUpdateRequest" result="reviewRequest.send();"
url="/reviews/update" useProxy="false" method="POST"/>
После компиляции приложения переместите файлы Flex приложения из папки bin в папку rails: public