Google App Engine for java自今年4月7日开放申请,至今不足二十日,但关于GAE for java的博文已经出现了不少。继数据存储之后,这一课要研究如何通过客户端管理Greeting,即CURD的实现。
一,列表显示
在sban.flexblog.HelloWorld.java中添加一个获取所有Greeting的接口:
List<Greeting> result;
try {
result = (List<Greeting>) pm.newQuery(Greeting.class).execute();
} finally {
pm.close();
}
return result;
}
在上例中,pm.newQuery(Greeting.class)负责查询所有已存的Greeting信息。List
修改Index.mxml客户端代码,修改之后最终代码如下:
<FxApplication xmlns="http://ns.adobe.com/mxml/2009" initialize="onInit()">
<Script>
<![CDATA[
import mx.controls.Alert;
import flash.net.URLLoader;
import flash.net.URLRequest;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
import mx.rpc.AbstractOperation;
import mx.rpc.remoting.RemoteObject;
import mx.messaging.ChannelSet;
import mx.messaging.channels.AMFChannel;
import mx.collections.ArrayCollection;
import mx.managers.CursorManager;
import mx.events.IndexChangedEvent
[Bindable]
private var _greetingData : ArrayCollection;
[Bindable]
private var _greeting : Object;
private var _remotingObj : RemoteObject = new RemoteObject("GenericDestination");
private function onInit() : void
{
configRemoting();
getAllGreetings();
}
private function configRemoting() : void
{
_remotingObj.source = "sban.flexblog.HelloWorld";
_remotingObj.endpoint = "weborb.wo";
}
private function greetViaRemoting() : void
{
var op : AbstractOperation = _remotingObj.getOperation("greet2");
var handler : Function = function(event : ResultEvent) : void
{
op.removeEventListener(ResultEvent.RESULT, handler);
getAllGreetings();
Alert.show( event.result.toString() );
};
op.addEventListener(ResultEvent.RESULT, handler);
op.send(vNameTxt.text,vContentTxt.text);
}
private function getAllGreetings() : void
{
var op : AbstractOperation = _remotingObj.getOperation("getAllGreetings");
var handler : Function = function(event : ResultEvent) : void
{
op.removeEventListener(ResultEvent.RESULT, handler);
_greetingData = ArrayCollection(event.result);
};
op.addEventListener(ResultEvent.RESULT, handler);
op.send();
}
]]>
</Script>
<layout>
<BasicLayout />
</layout>
<VGroup width="100%">
<HGroup>
<Label text="user:" />
<FxTextInput id="vNameTxt" text="sban" />
</HGroup>
<HGroup>
<Label text="content:" />
<FxTextInput id="vContentTxt" text="greeting content" />
</HGroup>
<HGroup>
<FxButton id="vSendBtn" label="remoting greet"click="greetViaRemoting()" />
</HGroup>
<FxList id="vList1" dataProvider="{_greetingData}" width="60%"itemRenderer="GreetingItemRenderer">
<layout>
<VerticalLayout />
</layout>
</FxList>
</VGroup>
<TextBox text="by sban" color="gray" bottom="10" right="10" />
</FxApplication>
1,用一个Bindable的、ArrayList类型的_greetingData,用于存储Greeting数据列表。
2,getAllGreetings方法用于从远程接口sban.flexblog.HelloWorld.getAllGreetings中读取数据,其在OnInit中被调用。
3,FxList用于显示列表数据,使用了VerticalLayout布局,数据纵向依次排列。如果是HorizontalLayout布局,但是横向排列。关于布局,详见new language tag Private and layout in flex4。
4,FxList的itemRenderer,即GreetingItemRenderer,是一个自定义的ItemRenderer,是一个mxml格式的skin文件,内容如下:
<ItemRenderer xmlns="http://ns.adobe.com/mxml/2009">
<states>
<State name="normal"/>
<State name="hovered"/>
<State name="selected"/>
</states>
<TextBox text="ID:{data.id},User:{data.user},
Greeting:{data.greetingContent},
Date:{data.date}"
verticalCenter="0" left="3" right="3" top="6" bottom="4" />
</ItemRenderer>
这个文件的逻辑很简单,就是把Greeting的数据信息在一个TextBox中显示出来。有关于在flex4中编写ItemRender的skin组件件详见:use skin as dataContainer’s itemRenderer in flex4 gumbo。
运行一下,没有问题,但是FxList中内容不能点选。
这是由于我们没有在GreetingItemRenderer中定义点选的状态引起的,修改GreetingItemRenderer的代码,最终代码如下:
<states>
<State name="normal"/>
<State name="hovered"/>
<State name="selected"/>
</states>
<Rect left="0" right="0" top="0" bottom="0">
<fill>
<SolidColor color="0xffffff"
color.selected="0×666666" color.hovered="0×999999" />
</fill>
</Rect>
<TextBox color="0×000000" color.selected="0xffffff"
text="ID:{data.id},User:{data.user},
Greeting:{data.greetingContent},
Date:{data.date}"
verticalCenter="0" left="3" right="3" top="6" bottom="4" />
</ItemRenderer>
此时,FxList便可以点选了。当点选时,背景为深灰色,文字为白色,鼠标rollOver时,背景为浅灰。这个效果是由GreetingItemRenderer中的selected与hovered状态决定的。关于flex4中的新状态(State)详见:new state in flex4。运行效果如下图所示:
二,可恶的2030错误
在运行时,我发现会遇到以下Error:
这个Error是flex客户端抛出的。但是根源却在server端,在类HelloWorld.java的getAllGreetings方法内,我们使用了List
修改getAllGreetings方法,最终代码如下:
public List<Greeting> getAllGreetings() {
List<Greeting> result;
try {
result = (List<Greeting>) pm.newQuery(Greeting.class).execute();
while (result.get(result.size() – 1) == null) {
result.remove(result.size() – 1);
}
} finally {
pm.close();
}
return result;
}
@SuppressWarnings用于告诉Eclipse不要再提示unchecked警告,不然总会有一个黄色的小灯泡在哪里挂着。
sban:储如2030、2032等等这类与IO、文件等相关的IOError,在flex客户端接口调试中是最常见,也是爆错信息最简单的一类Error。这类Error多是server端代码引起的。
二,删除与更新
在sban.flexblog.HelloWorld这个类中,添加有关删除一个Greeting,删除所有Greeting,编辑单个Greeting,以及获取单个Greeting的接口方法,该类最终代码如下:
import java.util.Date;
import java.util.List;
import javax.jdo.PersistenceManager;
import sban.flexblog.managers.PMFactory;
/**
* @author sban.li
*
*/
public class HelloWorld {
public String greet(String name) {
return "Hi " + name + ", this message comes from remoting. Now:"
+ new Date().toString();
}
private PersistenceManager pm = PMFactory.getInstance()
.getPersistenceManager();
public Boolean greet2(String user, String content) {
Boolean result = true;
Greeting greeting = new Greeting(user, content, new Date());
try {
pm.makePersistent(greeting);
} catch (Exception e) {
result = false;
} finally {
pm.close();
}
return result;
}
public Greeting getGreetingById(Long id)
{
return (Greeting) pm.getObjectById(Greeting.class, id);
}
public Boolean editGreeting(Long id, String content)
{
Boolean result = true;
try {
Greeting greeting = getGreetingById(id);
greeting.setGreetingContent(content);
} catch (Exception e) {
result = false;
} finally {
pm.close();
}
return result;
}
public Boolean deleteById(Long id) {
Boolean result = true;
try {
Greeting greeting = getGreetingById(id);
pm.deletePersistent(greeting);
} catch (Exception e) {
result = false;
} finally {
pm.close();
}
return result;
}
@SuppressWarnings(value="unchecked")
public Boolean deleteAllGreetings() {
Boolean result = true;
try {
List<Greeting> greetings = (List<Greeting>) pm.newQuery(
Greeting.class).execute();
pm.deletePersistentAll(greetings);
} catch (Exception e) {
result = false;
} finally {
pm.close();
}
return result;
}
@SuppressWarnings(value="unchecked")
public List<Greeting> getAllGreetings() {
List<Greeting> result;
try {
result = (List<Greeting>) pm.newQuery(Greeting.class).execute();
while (result.get(result.size() – 1) == null) {
result.remove(result.size() – 1);
}
} finally {
pm.close();
}
return result;
}
}
1,pm.deletePersistent用于删除单个对象
2,pm.deletePersistentAll用于删除一个集合
3,pm.getObjectById用于获取单个对象
4,更新Greeting对象时,只需设置该对象属性,在pm.close之前,数据会自动存储。需在sban.flexblog.Greeting中,添加如下setter:
修改Index.mxml客户端代码,最终如下:
<FxApplication xmlns="http://ns.adobe.com/mxml/2009" initialize="onInit()">
<Script>
<![CDATA[
import mx.controls.Alert;
import flash.net.URLLoader;
import flash.net.URLRequest;
import mx.rpc.events.ResultEvent;
import mx.rpc.events.FaultEvent;
import mx.rpc.AbstractOperation;
import mx.rpc.remoting.RemoteObject;
import mx.messaging.ChannelSet;
import mx.messaging.channels.AMFChannel;
import mx.collections.ArrayCollection;
import mx.managers.CursorManager;
import mx.events.IndexChangedEvent
[Bindable]
private var _greetingData : ArrayCollection;
[Bindable]
private var _greeting : Object;
private var _remotingObj : RemoteObject = new RemoteObject("GenericDestination");
private function onInit() : void
{
configRemoting();
getAllGreetings();
}
private function configRemoting() : void
{
_remotingObj.source = "sban.flexblog.HelloWorld";
_remotingObj.endpoint = "weborb.wo";
}
private function greetViaRemoting() : void
{
var op : AbstractOperation = _remotingObj.getOperation("greet2");
var handler : Function = function(event : ResultEvent) : void
{
op.removeEventListener(ResultEvent.RESULT, handler);
getAllGreetings();
Alert.show( event.result.toString() );
};
op.addEventListener(ResultEvent.RESULT, handler);
op.send(vNameTxt.text,vContentTxt.text);
}
private function getAllGreetings() : void
{
var op : AbstractOperation = _remotingObj.getOperation("getAllGreetings");
var handler : Function = function(event : ResultEvent) : void
{
op.removeEventListener(ResultEvent.RESULT, handler);
_greetingData = ArrayCollection(event.result);
};
op.addEventListener(ResultEvent.RESULT, handler);
op.send();
}
private function deleteAllGreetings() : void
{
var op : AbstractOperation = _remotingObj.getOperation("deleteAllGreetings");
var handler : Function = function(event : ResultEvent) : void
{
op.removeEventListener(ResultEvent.RESULT, handler);
getAllGreetings();
//Alert.show(event.result.toString());
};
op.addEventListener(ResultEvent.RESULT, handler);
op.send();
}
private function deleteItem() : void
{
if(vList1.selectedItem)
{
var itemId : Number = vList1.selectedItem.id;
var op : AbstractOperation = _remotingObj.getOperation("deleteById");
var handler : Function = function(event : ResultEvent) : void
{
op.removeEventListener(ResultEvent.RESULT, handler);
getAllGreetings();
//Alert.show(event.result.toString());
};
op.addEventListener(ResultEvent.RESULT, handler);
op.send(itemId);
}
}
private function selectionChangedHandler(event : IndexChangedEvent) : void
{
var itemId : Number = vList1.selectedItem.id;
var op : AbstractOperation = _remotingObj.getOperation("getGreetingById");
var handler : Function = function(event : ResultEvent) : void
{
op.removeEventListener(ResultEvent.RESULT, handler);
_greeting = event.result;
};
op.addEventListener(ResultEvent.RESULT, handler);
op.send(itemId);
}
private function edit() : void
{
var content : String = vContentEditTxt.text;
var op : AbstractOperation = _remotingObj.getOperation("editGreeting");
var handler : Function = function(event : ResultEvent) : void
{
op.removeEventListener(ResultEvent.RESULT, handler);
getAllGreetings();
};
op.addEventListener(ResultEvent.RESULT, handler);
op.send(_greeting.id, content);
}
]]>
</Script>
<layout>
<BasicLayout />
</layout>
<VGroup width="100%">
<HGroup>
<Label text="user:" />
<FxTextInput id="vNameTxt" text="sban" />
</HGroup>
<HGroup>
<Label text="content:" />
<FxTextInput id="vContentTxt" text="greeting content" />
</HGroup>
<HGroup>
<FxButton id="vSendBtn" label="remoting greet"click="greetViaRemoting()" />
<FxButton id="vDelAllBtn" label="deoete all greetings"click="deleteAllGreetings()" />
<FxButton id="vdelItemBtn" label="delete selected item"click="deleteItem()" />
</HGroup>
<FxList id="vList1" selectionChanged="selectionChangedHandler(event)"
dataProvider="{_greetingData}" width="60%"itemRenderer="GreetingItemRenderer">
<layout>
<VerticalLayout />
</layout>
</FxList>
<Form borderStyle="solid">
<FormItem label="ID:">
<Label text="{_greeting.id}" />
</FormItem>
<FormItem label="User:">
<Label text="{_greeting.user}" />
</FormItem>
<FormItem label="Content:">
<FxTextInput id="vContentEditTxt"text="{_greeting.greetingContent}" />
</FormItem>
<ControlBar>
<FxButton id="vSubmitBtn" label="submit" click="edit()" />
</ControlBar>
</Form>
</VGroup>
<TextBox text="by sban" color="gray" bottom="10" right="10" />
</FxApplication>
为了让逻辑简单,在编辑时,只充许修改Greeting的内容。这块逻辑比较简要,不再一一述说。读者们可自行下载源码运行查看。我把Index.mxml改名为Greeting.mxml,布署到了http://flex-blog.appspot.com/Greeting.html。附运行截图一张:
布署时要删除war/WEB-INF/appengine-generated目录下的文件,否则可能导致上传不成功。
本课最终源码:flex4-lessons4-5.zip
sban 2009/4/26 于北京
本文为sban原创,作者保留所有权利,如需转载请保留作者及原文链接。
flex选修课系列:基于flex4技术从零开发flex博客系统