现在讨论flow页面上的动作如何关联到flow定义文件中的transition.
因为我们这个应用是liferay portlet 应用,和绝大多数portlet一样,页面上的action,一般都会使用一个portlet action url 来标识。
比如,我们想要在defineApp 这个view-state,当点击"Next" 按钮时,要触发到一个flow 的transition:
首先,我们定义一个portlet action url:
- <portlet:actionURL var="nextStep">
- <portlet:param name="execution" value="${flowExecutionKey}" />
- <portlet:param name="_eventId" value="next" />
- </portlet:actionURL>
这里可以看到,我们要用若干个参数来定义这个actionURL,因为要给spring web flow使用,所以有一个必不可少的参数是execution,它的值永远是 ${flowExecutionKey} ,我们并不需要具体研究这个值是干嘛的,它是用spring web flow框架提供的,就是表明获取flow执行的key,因为这个页面属于某个特定的flow的view-state,所以这个值是唯一的,并且和flow有关。
第二个参数_eventId也是必不可少的,它用于表示flow执行的事件Id,这个事件Id会被对应到状态的<transition>的on属性,表明基于某个事件来进行transition.
当然了,除去execution 和_eventId之外,还可以有任意多个普通参数,这些普通参数都用<portlet:param>的名字-值对来表示,也作为transition的参数,这些参数可选的,和业务需求有关。
然后,我们就去看flow定义文件,因为当前页面是defineApp.jsp,所以我们找到了defineApp这个view-state,看它的transition:
- <view-state id="defineApp" >
- <transition on="next" to="defineProject">
- <evaluate expression="defineAppService.createAppInfo(requestParameters.appInfoJSONData)"
- result="flowScope.appInfo" />
- <set name="flowScope.appInfoJSONData" value="requestParameters.appInfoJSONData" type="string"/>
- <set name="flowScope.hasAppInfoData" value="true" type="boolean"/>
- </transition>
- ...
- </view-state>
从这里可以看出,我们有个元素叫<transition>并且属性为on,这个on 刚好匹配这个页面上actionURL传递过来的_eventId, 而to 则表示了页面跳转到的目标页面,所以这里可以看出,当触发这个action URL时候,flow会从当前的defineApp.jsp跳转到defineProject.jsp
哦,不,等下,跳转不是白跳的,肯定会有些数据的交互和保存,所以这里可以看到2个子元素,一个是<evaluate>,这个元素表示执行某个方法,因为我们已经初始化了defineAppService这个bean,所以它会调用相应的方法,并且吧结果存到流的flowScope域上,同时,它还存放了其他2个变量到flowScope上,于是这些变量在flow跳转的任何地方都是可用的。
最后,我们就要吧这个页面上的portlet actionURL绑定到页面元素上了,因为这个action的触发总是用户与页面的交互事件来触发的:
我们的前端用的是ext-js,所以,我们先定义了一个ext-js的button,然后渲染到defineApp.jsp上:
- items:[
- {
- xtype:'button',
- name:'btnCancel',
- margin:'0 10 0 0',
- width:100,
- text:'Cancel'
- },{
- xtype:'button',
- name:'btnNextInDefineApp',
- width:100,
- text:'Next'
- }
- ]
在控制器里面我们定义了这个事件触发:
- this.control({
- 'button[name=btnNextInDefineApp]':{
- click:function(btn){
- var form = btn.up('[name=sbumitform]');
- if(form.getForm().isValid()){
- var paraObj = form.getValues();
- var appInfoJSONData = {};
- for(var index in paraObj){
- if(index !='title' && index !='url'){
- appInfoJSONData[index] = paraObj[index];
- }
- }
- var titleArr = paraObj.title;
- var urlArr =paraObj.url;
- var docInfos = [];
- if(Ext.isArray(titleArr)&&Ext.isArray(urlArr)){
- Ext.each(titleArr,function(val,i){
- var _doc = {title:val,url:urlArr[i]};
- docInfos.push(_doc);
- });
- }else{
- docInfos.push({title:titleArr,url:urlArr});
- }
- appInfoJSONData.docInfos = docInfos;
- var url = nextStep+'&_envprovisioningportlet_WAR_envprovisioningportlet_appInfoJSONData=' +Ext.encode(appInfoJSONData);
- window.location.href = url;
- }
- }
- },
这里我们可以看到,当点击btnNextInDefineApp这个按钮时候,它会提交所有的数据到url为var url = nextStep+'&_envprovisioningportlet_WAR_envprovisioningportlet_appInfoJSONData=' +Ext.encode(appInfoJSONData);
而这个url的起始值为变量名nextStep ,它刚好匹配我们一开始的portlet action url 我们给它分配的var的名字:
- <portlet:actionURL var="nextStep">
- ...
- </portlet:actionURL>
所以现在当用户点击按钮,我们就携带了数据,然后流就可以处理相应的数据了。
我们携带的数据的名字叫appInfoJSONData ,见拼接的url,所以,在flow里面就可以从requestParameters里面获取相应的值并且进行业务计算了(见我加粗部分):
- <evaluate expression="defineAppService.createAppInfo(requestParameters.appInfoJSONData)"
- result="flowScope.appInfo" />