Dorado 5 : 1.模型及其实现类 (T1A)

使用dorado技术,第一个要接触的概念就是视图模型。视图模型是用于配置页面逻辑的一个对象,通常情况下它都是以一个xml文件的形式存在。页面逻辑这个词可能不太好理解,下面我们就从一个dorado页面开始来解释视图模型的作用。如下一个浏览器中的界面:

这是一个典型的合同管理界面,以及页面各个按钮所提供的功能:

  1. 新增合同按钮单击时可以打开合同新增页面;
  2. 编辑合同按钮单击时,打开表格当前行对应的合同详细信息页面;
  3. 删除合同按钮单击时会删除表格中当前行的数据;
  4. 合同号字段提供了超链接功能;
  5. 表格上双击时能打开合同详细信息页面;

我们也可以从另一个角度来阐述,该界面的"业务功能"是合同管理,包括:

  1. 根据条件查询合同功能;
  2. 新增合同功能;
  3. 编辑合同功能;
  4. 删除合同功能;
  5. 查看合同的详细信息功能;

该页面的"业务动作"是:

  1. 当用户点击查询按钮时执行查询合同业务动作;
  2. 当用户点击新增合同按钮执行新增合同业务动作;
  3. 当用户点击编辑合同按钮时执行编辑合同业务动作;
  4. 当用户点击删除合同按钮时执行删除合同业务动作;
  5. 当用户点击合同号的超级链接或在表格上双击合同信息时执行显示合同详细信息业务动作;

通常业务功能对应一批数据和操作这些数据的实现代码,这些代码是在用户触发某个业务动作时执行,所以业务逻辑是由业务功能与业务动作共同组成。
实现了以上功能的JSP源码是什么呢?看如下代码:

<%@ page contentType="text/html; charset=UTF-8" %> 
 <%@ taglib uri="http://www.bstek.com/dorado" prefix="d" %> 
 <html> 
 <head> 
 <title></title> 
 </head> 
 <body style="overflow: hidden"> 
 <d:View config="sample.complex.contract.ContractQuery"> 
 <d:Layout type="vflow" height="100%"> 
 <d:Pane> 
 <d:AutoForm id="formCondition" /> 
 </d:Pane> 
 <d:Pane align="right"> 
 <d:Layout type="Hflow"> 
 <d:Pane> 
 <d:Button id="buttonQuery" /> 
 </d:Pane> 
 <d:Pane> 
 <d:Button id="buttonAdd" /> 
 </d:Pane> 
 <d:Pane> 
 <d:Button id="buttonEdit" /> 
 </d:Pane> 
 <d:Pane> 
 <d:Button id="buttonDelete" /> 
 </d:Pane> 
 </d:Layout> 
 </d:Pane> 
 <d:Pane height="100%"> 
 <d:DataTable id="tableContract" /> 
 </d:Pane> 
 </d:Layout> 
 </d:View> 
 </body> 
</html>

可以在该JSP页面中看到标签库的申明,以及页面内容中充斥着各种标签对象。从这个JSP中我们很显然可以看出JSP的功能就是负责将各个标签放在合适的位置,简单的说就是实现标签的布局工作。
而在该合同管理界面中所具有的功能并没有在JSP的代码中有所体现,这些工作在dorado开发中都被统一在视图模型中描述,除了以上列表出来的功能点之外,还包括表格中包含多少个数据列,每一个数据列对应的数据是后台数据库中的什么字段等,这些都是在视图模型中定义的,通常情况下视图模型都表现为一个xml文件。而JSP与视图模型的关联关系可以通过<d:View config="XXX">中的config属性设定,如本例中的:

<d:View config="sample.complex.contract.ContractQuery">

视图模型通常情况下都以一个xml文件形式存在,如下图就是在Eclipse中看到的视图模型的可视化设计视图:

通过dorado提供的Eclipse插件可以将这个XML文件一种友好的方式可视化的展现给开发人员,开发人员可以基于此设计和定义视图模型。
如下的ViewModel中的dataset基本属性的定义视图:

响应AJAX请求的Java方法,如图中commandRpc的method属性:

可以定义表格中需要显示的数据列:

定义页面操作的各种事件,如表格的双击事件(onRecordDBClick事件):

事件代码中为JS代码,你只要了解JavaScript语法,就可以很容易的编写dorado组件的事件。
创建视图模型的办法如下:
首先确保Eclipse的处在dorado开发视图下,如下图我们需要选中Dorado Studio,将当前开发环境的开发视图调整为Dorado Studio:

选中Dorado Studio后,在一个dorado项目中,在文件目录树中点右键可以看到如下的菜单:

选择菜单中的View File菜单项就可以新建视图模型,由于dorado中视图模型的管理方式与java class是一样的(参考前面JSP代码中的<d:View config="XXX.XXX">,该处的配置风格就可以看出,视图模型是与package有关的)因此我们创建视图模型时,也将它放在工程的source目录中。
视图模型除了可以定义页面的功能之外,它还可以充当AJAX请求时的后台服务提供者角色。通常情况下我们都是通过一个视图模型的实现类,由实现类充当客户端AJAX请求的服务提供者。定义和使用实现类的方法如下:

<d:View clazz="sample.complex.contract.ContractQueryViewModel" config="sample.complex.contract.ContractQuery">

实现类可以通过视图模型的标签直接指定,或者通过视图模型xml配置文件的clazz设定,如下:

<?xml version="1.0" encoding="UTF-8"?> 
 <view clazz="sample.complex.contract.ContractQueryViewModel"> 
 <Datasets> 
 </Datasets> 
 <Controls> 
 </Controls> 
 <Properties /> 
</view>

一般情况下的定义方式如下:
在视图模型可视化设计视图中,选择视图模型属性结构的跟节点View。并单击右键:

选择Open implementation菜单项,系统会自动出现实现类生成向导:

生成新的Java类后,系统会自动的在视图模型的class属性填充新增java类的申明,如下图当我们选择左侧树中的根节点,可以看到它的属性列表中的clazz属性已经被自动填充:

Clazz属性中定义得值就是视图模型的实现类,实现类范例如下:

package ajax; 
import com.bstek.dorado.view.DefaultViewModel; 
public class MasterLink2ViewModel extends DefaultViewModel { 
}

默认的实现类继承自com.bstek.dorado.view.DefaultViewModel。在实现类中提供了以下的系统默认方法支持客户端的AJAX调用:
void doLoadData(ViewDataset dataset): 响应客户端dataset的分批数据下载请求,该请求在后面的动态数据下载一节会详细说明;
void doUpdateData(ParameterSet parameters),
void doUpdateData(ParameterSet parameters, ParameterSet outParameters):响应客户端的远程方法调用,该方法调用会在本书后续章节中的远程方法调用中详细说明;
除了响应客户端的AJAX请求之外,视图模型实现类编程的过程中,还提供了一些方法协助程序员访问和创建视图模型中的基本对象的功能,通过这些方法可以协助我们完成视图模型的开发工作以及视图模型中内部基本对象的管理工作,如:
Dataset getDataset(String id):获取视图模型中的一个dataset;
Control getControl(String id):获取视图模型中的一个Control;
Dataset createDataset(String id):利用实现类创建Dataset对象;
Control createControl(String id):利用实现类创建Controle对象;
由于视图模型中存在的最基本的对象就是dataset与control,因此上面提供的方法也是关于dataset与control的创建和获取。
另外实现类还提供一些事件监听类型的方法,如:
init(int state):视图模型的初始化事件;
initDataset(ViewDataset dataset):视图模型初始化dataset时触发的事件;
initControl(Control control):视图模型创建control时触发的事件;
由于视图模型要响应各种页面请求与AJAX请求,视图模型为了区别这些请求,分别用不同的状态来加以区别,如页面初始化的状态就是ViewMoel.VIEW_STATE,AJAX分批数据下载请求的状态就是ViewModel.SERVICE_STATE,不同状态下初始化视图模型都会引起一些dataset或组件的创建,则我们就可以通过如上的几个函数在这些对象创建与初始化的时候,加入业务逻辑调用代码并获取视图模型中的相关信息,完成项目的基本设计需求。
最后我们归纳视图模型的定义:
ViewModel是一种用于封装界面逻辑和操作逻辑的对象。即视图中包含哪些数据、这些数据以什么方式展现、视图中包含哪些控件、这些控件会激发什么操作等等。我们可以把ViewModel看作是Dataset和各种控件的容器。
ViewModel一般不用于定义各种控件最终在显示的布局,控件布局应通过其它方式进行定义。在通常情况下,我们利用JSP来完成对ViewModel布局的定义。
ViewModel不会在服务端进行缓存。我们也可以把ViewModel看成是服务端同客户端进行信息交换的接口。当用户执行页面请求、局部数据刷新、远程方法调用等操作时服务端都会创建相应的ViewModel 实例。我们可以通过其state属性来判断当前ViewModel在何种情况下被创建。state有下列几种取值:
ViewModel.STATE_VIEW 打开视图状态,即用户请求一个新的视图时的状态。在此状态下ViewModel在创建时会自动创建所有已声明的Dataset,并自动根据每个Dataset的配置来装载数据。所有的控件(Control)将以懒装载的方式被创建。
ViewModel.STATE_SERVICE 服务状态。当一个已打开的视图在执行局部数据刷新、远程方法调用等操作时的状态。在此状态下ViewModel中所有的Dataset和控件(Control)将以懒装载的方式被创建。
ViewModel.STATE_DESIGN 设计时状态。该状态只有在为Dorado Studio提供服务时有效。
在多数情况下我们建议使用ViewModel的实现类作为后台业务逻辑的调用接口。
详细说明请参考<<dorado 5 用户指南.doc>>。

视图模型的状态

为了更好的为客户端提供服务,我们需要了解ViewModel在提供各种服务时的行为。

打开视图状态STATE_VIEW

Dorado为客户端提供视图服务。即当用户通过浏览器直接访问一个View(JSP)时其中ViewModel所处的状态。此时ViewModel进行了如下图的动作:

上图说明以下几个动作:

  1. 将JSP、View配置文件、ViewModel联系起来。 JSP的d:View标签的config属性指向了一个View配置文件,该文件的clazz属性指向了一个ViewModel。
  2. ViewModel初始化。 2.1创建并初始化Dataset。 根据View配置文件中的每个Dataset节点创建相应的Dataset,并且调用该Dataset的初始化方法,如果配置了DataListener则触发相关的方法,例如onInit。然后触发ViewModel的initDataset事件。 当创建了全部的Dataset后触发ViewModel的initDatasets事件。2.2创建并初始化Control。 根据View配置文件中的每个Control节点创建相应的Control,并调用该Control的初始化方法,然后触发ViewModel的initControl事件。 当创建了全部的Control后触发ViewModel的initControls事件。 注:由于Dorado使用了懒加载组件的优化策略,所以此时仅仅初始化不可见组件,例如:DropDown、Menu、Command等。
  3. ViewModel为Dataset加载数据。 ViewModel遍历其中的Dataset列表,对每个Dataset执行doLoadData方法,如果该Dataset的autoLoadData属性为true则真正的加载数据,如果配置了DataListener则触发相关的方法,包括:beforLoadData、afterLoadData;如果autoLoaData属性为false则不会加载数据。
  4. ViewModel初始化指定的Control。 在解析JSP时每个d:Control标签会强制初始化其代表的可见组件,例如AutoForm、DataTable,此时会触发ViewModel的initControl事件。

服务状态STATE_SERVICE

Dorado为客户端提供远程服务。在客户端向服务器端发出为dataset加载数据的Ajax请求时ViewModel处于服务状态,例如在合同管理页面中用户点击了查询按钮。

其中为用户提供填写查询条件的AutoForm为formCondition,对应的Dataset为datasetConditon;显示合同列表的DataTable为tableContract,对应的Dataset为datasetContract;用于查询的QueryCommand为commandQuery。
下图代表了此时客户端与服务器端的交互过程。

客户端的动作:

  1. 将Conditon Control(formCondition)的内容同步到Codition Dataset(datasetCondition)中。
  2. 将Codition Dataset(datasetCondition)的当前记录同步到Query Dataset(datasetContract)的parameters中。
  3. Query Dataset(datasetContract)通过客户端引擎向服务器端Dorado引擎发出数据加载的AJAX请求。

服务器端的动作:

  1. 服务器端Dorado引擎将该请求委派给相应的ViewModel。
  2. ViewModel进行初始化工作,其状态为STATE_SERVICE,并触发initDatasets事件。默认情况下此时ViewModel中没有任何Dataset存在。
  3. 根据客户端请求创建需要加载数据的Dataset,并触发initDataset事件。
  4. 为这个Dataset加载数据,如果配置了DataListener则触发相关的方法,例如:onInit、beforeLoadData、afterLoadData。

客户端将返回的数据展现在Control中。
另外,在客户端的RpcCommand执行远程方法调用时ViewModel的状态也是STATE_SERVICE,不过除了initDatasets事件会被触发外没有调用任何与Dataset和Control相关的方法。

更新状态STATE_UPDATING

Dorado为客户端提供更新服务。在客户端向服务器端发出提交Dataset的Ajax请求时ViewModel处于更新状态,此时客户端与服务器端的交互如下图:

dorado提供了UpdateCommand允许客户端向服务器端提交Dataset,此时客户端的动作为:

  1. Control将数据同步到Dataset中。
  2. UpdateCommand将Dataset的字段定义与数据通过客户端引擎提交给服务器端Dorado引擎。

此时服务器端的动作为:

  1. Dorado引擎接收到客户端提交数据的请求,该请求被委派给相应的ViewModel。
  2. ViewModel执行初始化动作,触发initDatasets事件。
  3. ViewModel根据客户端提交的信息创建Dataset触发initDataset事件,并填充数据。
  4. 执行更新方法(默认为doUpdateData)。执行每个Dataset的update方法,如果配置了DataLisener则触发相关方法,例如:onInit、beforeUpdateData、afterUpdateData。

客户端将返回的数据展现在Control中。
另,此时允许ViewModel修改或删除Dataset中的记录,但不允许添加记录。