简述
部门维护界面主要用于维护部门信息表,在HSQL数据库中其定义如下:
CREATE MEMORY TABLE DEPT( DEPT_ID VARCHAR(16) NOT NULL PRIMARY KEY, BRANCH_ID VARCHAR(16), DEPT_NAME VARCHAR(30) )
该界面代表了单表维护的典型界面展现风格,利用AJAX技术实现增删改查等四种逻辑操作。界面效果如下:
界面的基本功能描述:
- 上方的AutoForm与DataTable保持联动同步关系;
- 利用中间的DataPilot可以对页面中的数据作增删改操作,并最终通过"保存所有修改"按钮实现页面无刷新的AJAX提交;
- 表格中的过滤栏提供了数据筛选功能;
- 最下方右侧的分页导航栏实现查询结果的数据分页显示功能;
实际上该例的功能也就是dorado提供的视频教程中helloworld的一部分,现在我们自己来实现一次吧。
通过该界面的学习可以通过dorado快速地实现业务系统中各种数据表的单表维护工作。
与<<dorado 5 快速入门(一)>>的区别
在<<dorado 5 快速入门(一)>>中我们通过AutoSqlDataset快速的实现了部门信息的维护工作,在本文中我们将采用另外两种类型的dataset实现部门信息的维护,从中我们可以看到dorado开发中整个表现层的开发与dataset类型是无关的,它们只是在存取数据的持久层设计方面有所差别而以。整个dorado的表现层开发技术可以应用于任意一种dataset,并且不会影响表现层的各种展现能力。
另外我们在本文中也可以了解到dorado的listener技术。以及学会如何使listener与dataset配合工作,更好的响应客户端的AJAX请求,本文的AJAX请求的具体业务就是数据查询,分批数据下载和批量数据保存工作。
期间我们会接触到dorado的flushData以及update等核心技术。从而了解dorado中数据处理技术的关键点。
SqlDataset实现部门维护 (T1B)
创建视图模型(View)
在src/hr目录下创建manage文件夹,并在manage文件夹下新建Dept视图模型对象:
图表 6 1
创建数据集(Dataset)
在新建的视图模型中选中Datasets节点,点左边工具条上的SqlDataset按钮,系统自动生成一个SqlDataset对象:
图表 6 2
把默认生成的Dataset的id属性dataset1修改为datasetDept。
设置Dataset的keyFields属性,值为dept_id,该属性在dataset执行批量数据提交时被使用。
图表 6 3
设置pageSize属性,值为10,通过这个属性,Dataset可以按照每页10条记录来分页。
图表 6 4
设置sql属性,值为"select * from dept"。
图表 6 5
选择datasetDept节点,并利用Auto Create Fields快捷菜单自动生成datasetDept中的字段:
图表 6 6
设置各个字段的label属性如下:
字段 | label |
---|---|
DEPT_ID | 部门编号 |
BRANCH_ID | 分公司编号 |
DEPT_NAME | 部门名称 |
到这里数据集datasetDept的设置就完成了。最终dataset的属性设定如下:
图表 6 7
创建数据表格(DataTable)
Dataset设定结束之后,我们再使用数据表格展示dataset的数据。以及通过数据表格的增删改等操作dataset的数据。
在当前的视图模型中选择Controls节点,添加DataTable对象,并选中新建的DataTable对象修改其属性:
属性 | 值 |
---|---|
dataset | datasetDept |
height | 100% |
id | tableDept |
showHScrollBar | false |
width | 100% |
设计视图如下:
图表 6 8
创建分页导航条(PagePilot)
选中Controls节点,添加组件PagePilot,并修改属性如下:
属性 | 值 |
---|---|
id | pagepilotDept |
dataset | datasetDept |
设计视图如下:
图表 6 9
创建数据导航条(DataPilot)
选中Controls节点,添加组件DataPilot,并修改属性如下:
属性 | 值 |
---|---|
id | datapilotDept |
dataset | datasetDept |
设计视图如下:
图表 6 10
创建数据表单(AutoForm)
选中Controls节点,添加组件AutoForm,并修改属性如下:
属性 | 值 |
---|---|
id | formDept |
dataset | datasetDept |
设计视图如下:
图表 6 11
选择formDept节点,点左边工具栏中Auto create elements快捷按钮利用绑定的datasetDept中的字段信息自动生成表单元素。
生成表单元素后,展开formDept节点,并选中自动生成的<FormGroup>节点,设置title属性为"部门信息"。
图表 6 12
设定结束后保存视图模型的所有修改。
创建数据更新命令(UpdateCommand)
选中Controls节点,点左边工具栏中的图标(从上往下数第7个),在列表中选择UpdateCommand。生成后,修改新建命令对象的id为commandSave,并展开commandSave节点,在datasetInfos节点下添加一个新的datasetInfo节点,并设定其dataset属性为datasetDept:
图表 6 13
表示命令执行时自动的将DatasetInfos中包含的datasetDept对象提交到服务器。
创建保存按钮(Button)
设定好数据更新命令之后,由于数据更新命令是不可见对象,为了方便用户操作,我们,还需要添加一个按钮,使用户单击该按钮时自动执行commandSave的默认动作。
选中Controls节点,新增Button对象,并设置其属性如下:
属性 | 值 |
---|---|
command | commandSave |
id | buttonSave |
value | 保存 |
width | 60 |
设计视图如下:
图表 6 14
创建JSP
之后在WebRoot目录中新建manage目录,并利用当前部门维护的Dept视图模型的JSP生成向导在manage目录中生成dept.jsp页面。
并利用Layout调整dept.jsp的布局设定,最终代码如下:
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib uri="http://www.bstek.com/dorado" prefix="d" %> <html> <head> <title></title> </head> <body> <d:View config="hr.manage.Dept"> <d:Layout type="vflow" height="100%"> <d:Pane> <d:AutoForm id="formDept" /> </d:Pane> <d:Pane> <d:Layout type="Hflow"> <d:Pane> <d:DataPilot id="datapilotDept" /> </d:Pane> <d:Pane> <d:Button id="buttonSave" /> </d:Pane> </d:Layout> </d:Pane> <d:Pane height="100%"> <d:DataTable id="tableDept" /> </d:Pane> <d:Pane align="right"> <d:PagePilot id="pagepilotDept" /> </d:Pane> </d:Layout> </d:View> </body> </html>
我们在主框架界面的菜单对象初始化的时候(MainViewModel.java中的initControls方法)已经设定好了相关菜单项的链接功能,代码如下:
subItem = item.addItem("dept", "部门信息"); subItem.setPath("manage/dept.jsp");
上面的菜单项的path属性定义为"manage/dept.jsp"。
查看运行效果
下面我们通过主框架查看效果。在主框架中选择部门信息菜单项。
最终效果:
图表 6 15
现在让我们验证一下部门维护界面的基本功能:
AutoForm与表格的数据联动功能:你可以选择表格中的不同行,注意观察AutoForm中的数据是否和表格中的当前行保持一致;你也可以修改表格中的数据并注意观察AutoForm中的数据是否能保持一致;
批量数据更新:利用DataPilot对表格进行增删改的操作,并最终单击"保存所有修改"按钮完成批量提交;提交后刷新页面验证提交是否成功;
分页导航条的翻页功能;
功能改进一:分公司信息下拉框维护
当编辑分公司字段时,我们希望看到分公司的名称信息,并可以通过选择分公司的下拉列表进行维护,这样可以避免用户的误输入。如下图维护部门所属分公司时,可以通过一个分公司下拉框列表供用户选择:
图表 6 16
该处采用了dorado中的DropDown(下拉框)开发技术。通常情况下DropDown中的数据我们都可以比较方便的在View中的DropDown对象中直接配置,如:
通过第五章的学习我们已经知道DropDown对象我们也可以在视图模型中用Java代码中初始化。
下面我们学习另一种类型的DropDown对象,该对象其中的列表项来自与一个Dataset对象。使用时我们只要创建一个DatasetDropDown就可以实现这个功能,顾名思义,就是通过Dataset获取数据并作为下拉框的数据项使用。
下面我们就利用DatasetDropDown对象实现分公司名称的处理。
在我们创建DatasetDropDown之前,先创建一个SqlDataset,其基本属性的配置如下:
属性 | 值 |
---|---|
id | datasetBranch |
sql | select * from branch |
利用SqlDataset的Auto Create Fields快捷按钮自动生成字段,如下图:
图表 6 17
具体设计步骤请参考前面的datasetDept创建过程,这里不再描述。
创建好datasetBranch之后,我们选择Controls节点,再创建一个DatasetDropDown,属性设置如下:
属性 | 值 |
---|---|
dataset | datasetBranch |
labelField | BRANCH_NAME |
valueField | BRANCH_ID |
mapValue | true |
设计视图如下:
图表 6 18
最后,我们将dropdownBranch绑定到branch_id字段上。
选中datasetDept的BRANCH_ID字段,设置dropDown属性,选择dropdownBranch。
图表 6 19
好了,经过上面一系列步骤,基本完成了下拉框的设计。
保存所有的修改,并重新浏览该页面,可以看到如下的效果:
图表 6 20
现在我们已经可以利用dropdownBranch提供的下拉功能修改部门的所属分公司信息了。
功能改进二:提供查询功能
该部门维护页面中已经提供了增加,删除和修改等比较全面的维护功能。下面我们继续增强该页面的基本功能:增加查询功能。
定义监听器对象
由于采用了SqlDataset,其查询语句是由我们在Dept视图模型中的sql属性定义的,同第五章提到动态组件技术一样我们也可以通过java代码动态的设定和修改SqlDataset的属性(当然对于其他类型的dataset也一样)。
下面我们利用dataset的listener机制实现SqlDataset查询sql语句的动态修改。
Listener也叫监听器,用于监听Dataset中的各种事件,在某些书籍中的术语叫回调机制。
在AJAX应用中监听器的作用更像是一个服务器端的服务接口。随时为dataset的数据请求和数据保存等各种动作提供服务。
下面我们利用dorado studio的listener向导为datasetDept创建一个listener。
图表 6 21
选择datasetDept对象,并在左侧找到 图标,单击打开监听器创建向导:
图表 6 22
在向导视图中保持默认的Class Name名称和Package不变,同时选中Options中的beforeLoadData数据行,覆盖其中的beforeLoadData事件。自动生成的java代码如下:
package hr.manage; import com.bstek.dorado.data.*; import com.bstek.dorado.common.*; /** * Dept_datasetDeptListener */ public class Dept_datasetDeptListener extends AbstractDatasetListener { public boolean beforeLoadData(Dataset dataset) throws Exception { return true; } }
向导关闭的同时会自动的设定datasetDept的listener属性为刚才新建的Listener,注意看下图的listener属性:
图表 6 23
这样新建listener与datasetDept关联上了,在datasetDept利用其sql属性加载数据之前会自动得触发listener中的beforeLoadData方法。注意看beforeLoadData方法:
public boolean beforeLoadData(Dataset dataset){}
其中包含了执行数据加载动作的datasetDept对象,即方法中的dataset参数。则我们就可以在这儿动态修改dataset的sql属性。并在beforeLoadData方法执行结束之后,datasetDept执行查询时所用的sql就是已经改变后的sql语句了。通过这种方式我们来实现dataset的条件查询功能。
public boolean beforeLoadData(Dataset dataset) throws Exception { String deptId = dataset.parameters().getString("dept_id"); String branchId = dataset.parameters().getString("branch_id"); String deptName = dataset.parameters().getString("dept_name"); String sql = "select * from dept"; String where = ""; if (StringHelper.isNotEmpty(deptId)){ where = " (dept.dept_id like '%"+deptId"%')"; } if (StringHelper.isNotEmpty(branchId)){ where = ((StringHelper.isEmpty(where))?" ":" and ")" (dept.branch_id like '%"branchId"%')"; } if (StringHelper.isNotEmpty(deptName)){ where = ((StringHelper.isEmpty(where))?" ":" and ")" (dept.dept_name like '%"deptName"%')"; } SqlDataset sqlDataset = (SqlDataset)dataset; sqlDataset.setSql(sql + ((StringHelper.isEmpty(where))?" ":" where ") + where); return true; }
其中dataset的parameters集合为dataset的一个参数集合,在dataset执行查询的AJAX请求时,该参数的值将由客户端上传上来(下一步我们将会继续讨论这些值的来源)。我们将根据参数值动态产生一个查询SQL语句,并最终修改datasetDept的sql属性。
注意beforeLoadData方法的最后一行代码return true。这是beforeLoadData方法的一种处理机制,该方法通过返回值决定是否由dataset执行默认的数据加载工作,本例就是决定datasetDept是否还要用其sql属性执行查询。
通过以上的Listener的beforeLoadData方法的定义,我们已经可以使datasetDept支持查询处理了,可以说万事俱备只差客户端的AJAX查询请求了。
修改界面以便发出查询请求
下一步我们要做的就是给dataset的parameters赋值,并利用dataset的flushData并向服务器重新请求数据。flushData执行时会自动的将parameters中的值传递到服务器的监听器中。
flushData方法的基本说明如下:
flushData是dataset提供的客户端方法,所有dataset都具有该方法。该方法的主要作用是重新从服务器端下载数据集的数据来填充当前dataset。执行此方法时dataset中原有的记录将被全部清除。
在客户端可以直接调用flushData()方法向服务器发出请求:
/范例1:范例说明:利用dataset的flushData()方法,上传dept_id参数信息,并重新获取数据/ dataset.parameters().setValue("dept_id", "D11");dataset.flushData();
更详细的内容请参考<<dorado 5 用户指南 v1.1 .doc>>。
上述代码范例中的参数值是通过代码写在程序中的,我们的范例希望由用户可以直接在界面上输入查询条件,并单击查询按钮得到查询结果。下面我们就按照这种方式实现datasetDept的查询功能。
界面的基本风格如下:
图表 6 24
上图的最上方区域为用户输入查询条件的区域,并最终单击查询按钮实现部门信息的查询功能。
设计步骤如下:
步骤一:Dept视图模型中添加新的FormDataset,并命名为datasetCondition。同时新增三个字段属性设置如下图:
图表 6 25
步骤二:添加AutoForm对象formQuery,并绑定到datasetCondition上,如下:
图表 6 26
步骤三:新增Button对象buttonQuery,属性设置如下:
属性 | 值 |
---|---|
id | buttonQuery |
value | 查询 |
width | 60 |
另外我们要在buttonQuery的单击事件中获取datasetCondition中的查询参数值并通过dataset的parameters传递到服务器执行查询。
将当前视图切换到Events Inspector面板,并选择buttonQuery的onClick事件,在其中输入代码:
var params = datasetDept.parameters(); params.setValue("dept_id", datasetCondition.getValue("dept_id")); params.setValue("branch_id", datasetCondition.getValue("branch_id")); params.setValue("dept_name", datasetCondition.getValue("dept_name")); datasetDept.flushData();
代码实现的功能就是从datasetCondition中取出值,并储存在datasetDept的parameters中,再调用flushData方法发送到服务器。执行查询动作,这时候会触发服务器端listener的beforeLoadData方法,并最终获得查询后的数据。
步骤四:利用AutoForm的Auto create elements快捷按钮,导入datasetCondition的字段信息。并设定<FormGroup>的title属性为 "查询条件",以及利用AutoForm的Custom Element对象将buttonQuery引入到formQuery中。如下图:
图表 6 27
步骤五:在dept.jsp上修改代码,将新增的formQuery添加到JSP中:
<d:Layout type="vflow" height="100%"> |
其中的蓝色代码为新增内容
保存所有修改。
(查询设置完成)
体验查询结果
刷新页面,在查询条件中输入部门编号为"D1",并单击查询按钮:
图表 6 28
CustomDataset实现部门维护 (T1B)
创建视图模型(View)
在src/hr目录manage文件夹下新建Dept1视图模型对象:
图表 6 29
创建数据集(Dataset)
新建CustomDataset
在新建的视图模型中选中Datasets节点,点左边工具条上的CustomDataset按钮,系统自动生成一个CustomDataset对象:
图表 6 30
把默认生成的Dataset的id属性dataset1修改为datasetDept。
设置pageSize属性,值为10,通过这个属性,Dataset可以按照每页10条记录来分页。
图表 6 31
知识预备:Dataset与POJO的数据转换功能
Dorado中的各种Dataset都提供了一些方法,用于初始化dataset的数据,例如:
dataset.insertRecord(); dataset.setString("degree", "高中"); dataset.insertRecord(); dataset.setString("degree", "大专"); dataset.insertRecord(); dataset.setString("degree", "大学"); dataset.insertRecord(); dataset.setString("degree", "硕士"); dataset.insertRecord(); dataset.setString("degree", "博士"); dataset.insertRecord(); dataset.setString("degree", "其它");
以上代码就是利用dataset的insertRecord()方法插入一条空纪录,并利用set方法设置dataset中相关字段的值,set方法可以支持多种类型的数据,例如int,string,float,double,date等等。
对于CustomDataset我们就可以利用这种方法实现dataset的初始化。
但是这种使用方式比较的费劲。
因此dorado中的dataset提供了与POJO对象的转换功能,使用时我们只需要将dataset与需要转换的POJO对象设着好映射关系。再通过dataset与POJO的转换方法实现POJO数据转换为dataset的数据,同样也能实现dataset中的数据转换为POJO的数据。
一下我们就以本例的实现详细说明。
建立datasetDept与Dept.java的映射关系
在src/hr/manage目录中新增目录domain。用以存放本文开发中要使用的POJO对象。
在domain目录下我们增加一个Dept.java对象,基本的设定如下:
Dept.java |
package hr.manage.domain; public class Dept { private String deptId; private String branchId; private String deptName; public String getBranchId() { return branchId; } public void setBranchId(String branchId) { this.branchId = branchId; } public String getDeptId() { return deptId; } public void setDeptId(String deptId) { this.deptId = deptId; } public String getDeptName() { return deptName; } public void setDeptName(String deptName) { this.deptName = deptName; } }
设定datasetDept的objectClazz为"hr.manage.domain.Dept"。
并在datasetDept中新增三个字段,分别为:
deptId,属性设置如下:
属性 | 值 |
---|---|
dataType | string |
label | 部门编号 |
name | deptId |
property | deptId |
branchId,属性设置如下:
属性 | 值 |
---|---|
dataType | string |
label | 分公司编号 |
name | branchId |
property | branchId |
deptName,属性设置如下:
属性 | 值 |
---|---|
dataType | string |
label | 部门名称 |
name | deptName |
property | deptName |
其中的property属性与datasetDept的objectClazz属性对应的POJO对象的属性相对应,例如deptId字段的定义就表示它与Dept.java中的deptId属性相对应。
另外Field与POJO的属性映射还存在一种默认的约定,如以上三个字段的设定中name与Dept中的属性名称完全相同,则我们可以在Field的设置中省略property属性的设定。只有在Field的名称与POJO不一致时,我们才有必要设定Field的property属性,去处property属性设置后,datasetDept的设计视图就如下:
图表 6 32
创建数据表格(DataTable)
同6.1.2.3。
创建分页导航条(PagePilot)
同6.1.2.4。
创建数据导航条(DataPilot)
同6.1.2.5。
创建数据表单(AutoForm)
同6.1.2.6。
创建数据更新命令(UpdateCommand)
同6.1.2.7。
创建保存按钮(Button)
同6.1.2.8。
创建JSP
之后在WebRoot目录中新建manage目录,并利用当前部门维护的Dept视图模型的JSP生成向导在manage目录中生成dept1.jsp页面。
并利用Layout调整dept1.jsp的布局设定,最终代码如下:
<%@ page contentType="text/html; charset=UTF-8" %> <%@ taglib uri="http://www.bstek.com/dorado" prefix="d" %> <html> <head> <title></title> </head> <body> <d:View config="hr.manage.Dept1"> <d:Layout type="vflow" height="100%"> <d:Pane> <d:AutoForm id="formDept" /> </d:Pane> <d:Pane> <d:Layout type="Hflow"> <d:Pane> <d:DataPilot id="datapilotDept" /> </d:Pane> <d:Pane> <d:Button id="buttonSave" /> </d:Pane> </d:Layout> </d:Pane> <d:Pane height="100%"> <d:DataTable id="tableDept" /> </d:Pane> <d:Pane align="right"> <d:PagePilot id="pagepilotDept" /> </d:Pane> </d:Layout> </d:View> </body> </html>
我们在主框架界面的菜单对象初始化方法中添加新的菜单项,在MainViewModel.java的initControls方法中加入代码,代码如下:
subItem = item.addItem("dept", "部门信息"); |
上面的蓝色代码为新插入的代码,用以关联刚才新建的dept1.jsp页面。
查看运行效果
下面我们通过主框架查看效果。在主框架中选择部门信息1菜单项。
最终效果:
图表 6 33
在以上的界面中还没有看到部门数据。下一步我们将初步完善该页面的基本功能。
功能改进一:添加Listener实现数据查询,并实现查询结果Dept与datasetDept的数据转换
监听器的创建方法参考上文SqlDataset功能改进二中的办法。
在监听器的创建向导中:
图表 6 34
勾选afterLoadData方法,afterLoadData方法是Dataset执行数据加载后会自动调用的方法。该方法调用时,会将dataset作为参数传入,我们希望在该方法内部初始化datasetDept的数据。
在向导视图中保持默认的Class Name名称和Package不变。自动生成的java代码如下:
package hr.manage; import com.bstek.dorado.data.*; import com.bstek.dorado.common.*; /** * Dept1_datasetDeptListener */ public class Dept1_datasetDeptListener extends AbstractDatasetListener { public void afterLoadData(Dataset dataset) throws Exception { } }
向导关闭的同时会自动的设定datasetDept1的listener属性为刚才新建的Listener,注意看下图的listener属性:
图表 6 35
这样新建listener与datasetDept关联上了。
利用listener实现数据导入功能
注意看afterLoadData方法:
public void afterLoadData(Dataset dataset){}
其中包含了执行数据加载动作的datasetDept对象,即方法中的dataset参数。则我们就可以在这儿将查询出的Dept对象与datasetDept转换。将数据交给dataset。
public void afterLoadData(Dataset dataset) throws Exception { DeptDao dao = new DeptDao(); List deptList = dao.getAll(); dataset.fromDO(deptList); }
我们通过一个自定义的DeptDao对象获取一个部门列表信息,并通过dataset的fromDO将部门列表转化到dataset中。
Dataset的fromDO方法是dataset自动集成的方法,该方法的说明如下:
上述代码中使用的DeptDao对象的相关代码参考附录一。
现在来重新浏览刚才的JSP页面,可以看到如下的界面:
图表 6 36
利用listener实现数据保存功能
在listener中再添加一个beforeUpdate方法的声明。
public boolean beforeUpdateData(Dataset dataset) throws Exception {}
该方法在dataset执行数据更新时触发,参数dataset在本例中就是datasetDept。由于dataset是一个包含多行数据的大对象,并且在客户端用户可能对其中的数据作不同的操作,新增的记录,修改的记录,删除的记录(被删除的记录在dataset中只是做一个删除标记)。这些数据的修改都是只在用户通过UpdateCommand提交到服务器端的时候,才会把所有的信息提交到Server端。
则我们在Listener中,就需要遍历dataset中的数据,在本例子中遍历dataset中的数据之前,我们通过如下的代码先做一个过滤:
RecordIterator ri = dataset.recordIterator(); |
RecordIterator是dataset提供的一个遍历记录迭代器,由于在本例中我们只关心dataset中发生修改过的记录。因此我们在遍历dataset中的记录时,先设置过滤方式为"Dataset.FILTER_CHANGED"。该种过滤方式可以遍历到所有发生变化的记录(包含新添加的、被修改过的和已删除的)。
记录的修改,新增和删除状态我们可以通过record的getState方法加以区别,主要类型有:
记录状态 | 说明 |
---|---|
STATE_DELETED | 已删除 |
STATE_MODIFIED | 已更新 |
STATE_NEW | 新增 |
STATE_NONE | 无状态.数据没有发生任何的修改。此状态为记录的默认状态. |
通过以上的几个概念,最终我们修改beforeUpdateData方法的最终代码为:
public boolean beforeUpdateData(Dataset dataset) throws Exception { DeptDao dao = new DeptDao(); RecordIterator ri = dataset.recordIterator(); ri.setVisibility(Dataset.FILTER_CHANGED); while (ri.hasNext()) { Record record = ri.nextRecord(); Dept dept = (Dept) record.toDO(); switch (record.getState()) { case Record.STATE_NEW: dao.save(dept); break; case Record.STATE_MODIFIED: dao.update(dept); break; case Record.STATE_DELETED: dao.remove(dept); break; default: } } return true; }
代码中根据记录的不同状态,调用DeptDao的不同持久化方法实现数据保存和删除工作。
本例中DeptDao中并没有实现save(),update(),remove()方法,请读者自行实现,该部分的内容已经超过了表现层技术范围,不再详细讨论。
功能改进二:分公司信息下拉框维护
当编辑分公司字段时,我们希望看到分公司的名称信息,并可以通过选择分公司的下拉列表进行维护,这样可以避免用户的误输入。如下图维护部门所属分公司时,可以通过一个分公司下拉框列表供用户选择:
图表 6 37
该处采用了dorado中的DropDown(下拉框)开发技术。通常情况下DropDown中的数据我们都可以比较方便得在View中的DropDown对象中直接配置,如:
通过第五章的学习我们已经知道DropDown对象我们也可以在视图模型中用Java代码中初始化。
下面我们学习另一种类型的DropDown对象,该对象其中的列表项来自与一个Dataset对象。使用时我们只要创建一个DatasetDropDown就可以实现这个想法,顾名思义,就是通过Dataset获取数据并作为下拉框的数据项使用。
下面我们就利用DatasetDropDown对象实现分公司名称的处理。
在本例中我们依然采用CustomDataset实现部门下拉列表(当然你也可以使用AutoSqlDataset,SqlDataset实现,这两种方式在初级培训教程以及本手册的前面都已经有详细说明)在我们创建DatasetDropDown之前,先创建一个CustomDataset,并命名为datasetBranch。
建立datasetBranch与Branch.java的映射关系
在src/hr/manage/domain。用以存放本文开发中要使用的POJO对象。
在domain目录下我们增加一个Branch.java对象,基本的设定如下:
Branch.java |
package hr.manage.domain; public class Branch { private String branchId; private String branchName; public String getBranchId() { return branchId; } public void setBranchId(String branchId) { this.branchId = branchId; } public String getBranchName() { return branchName; } public void setBranchName(String branchName) { this.branchName = branchName; } }
设定datasetBranch的objectClazz为"hr.manage.domain.Branch"。
并在datasetBranch中新增两个字段,分别为:
branchId,属性设置如下:
属性 | 值 |
---|---|
dataType | string |
label | 分公司编号 |
name | branchId |
property | branchId |
branchName,属性设置如下:
属性 | 值 |
---|---|
dataType | string |
label | 分公司名称 |
name | branchName |
property | branchName |
其中的property属性与datasetBranch的objectClazz属性对应的POJO对象的属性相对应,例如branchId字段的定义就表示它与Branch.java中的branchId属性相对应。
另外Field与POJO的属性映射还存在一种默认的约定,如以上两个字段的设定中name与Branch中的属性名称完全相同,则我们可以在Field的设置中省略property属性的设定。只有在Field的名称与POJO不一致时,我们才有必要设定Field的property属性,去处property属性设置后,datasetBranch的设计视图就如下:
图表 6 38
建立DatasetDropDown
创建好datasetBranch之后,我们选择Controls节点,再创建一个DatasetDropDown,属性设置如下:
属性 | 值 |
---|---|
dataset | datasetBranch |
labelField | branchName |
valueField | branchId |
mapValue | true |
设计视图如下:
图表 6 39
最后,我们将dropdownBranch绑定到branch_id字段上。
选中datasetDept的branch_id字段,设置dropDown属性,选择dropdownBranch。
图表 6 40
好了,经过上面一系列步骤,基本完成了下拉框的设计。
利用Listener中实现datasetBranch的数据导入工作
利用datasetBranch的listener的向导创建功能,新增Listener:
图表 6 41
勾选其中的afterLoadData方法,确保datasetBranch的listener连接到新建的Listener:
图表 6 42
注意看afterLoadData方法:
public void afterLoadData(Dataset dataset){}
其中包含了执行数据加载动作的datasetBranch对象,即方法中的dataset参数。则我们就可以在这儿将查询出的Branch对象与datasetBranch转换。将数据交给dataset。
public void afterLoadData(Dataset dataset) throws Exception { BranchDao dao = new BranchDao(); List branchList = dao.getAll(); dataset.fromDO(branchList); }
我们通过一个自定义的BranchDao对象获取一个部门列表信息,并通过dataset的fromDO将部门列表转化到dataset中。
Dataset的fromDO方法是dataset自动集成的方法,该方法的说明如下:
上述代码中使用的BranchDao对象的相关代码参考附录一。
保存所有的修改,并重新浏览该页面,可以看到如下的效果:
图表 6 43
现在我们已经可以利用dropdownBranch提供的下拉功能修改部门的所属分公司信息了。
功能改进三:提供查询功能
该部门维护页面中已经提供了增加,删除和修改等比较全面的维护功能。下面我们继续增强该页面的基本功能:增加查询功能。
修改afterLoadData方法
在前面我们已经定义了datasetDept的listener对象,其中通过DeptDao对象执行数据加载动作。为了实现查询,我们就可以在DeptDao中添加一个新的方法,使得它可以支持数据查询。
public List getAll(ParameterSet parameters) throws Exception {}
代码如下:
public List getAll(ParameterSet parameters) throws Exception { String deptId = parameters.getString("deptId"); String branchId = parameters.getString("branchId"); String deptName = parameters.getString("deptName"); List result = new ArrayList(); DBStatement stmt = this.getStatement(DBStatement.SELECT, "dept"); try { ParameterSet p = stmt.parameters(); if (StringHelper.isNotEmpty(deptId)) { p.setString("dept_id", deptId); } if (StringHelper.isNotEmpty(branchId)) { p.setString("branch_id", branchId); } if (StringHelper.isNotEmpty(deptName)) { p.setString("dept_name", deptName); } List list = stmt.queryForList(); for (int i = 0; i < list.size(); i++) { VariantSet entity = (VariantSet) list.get; result.add(this.convert(entity)); } } finally { stmt.close(); } return result; }
修改Dept1_datasetDeptListener.java中的afterLoadData方法:
public void afterLoadData(Dataset dataset) throws Exception { DeptDao dao = new DeptDao(); List deptList = dao.getAll(dataset.parameters()); dataset.fromDO(deptList); }
其中dataset的parameters集合为dataset的一个参数集合,在dataset执行查询的AJAX请求时,该参数的值将由客户端上传上来(下一步我们将会继续讨论这些值的来源)。我们将参数传递到DeptDao的getAll方法中,并由该方法的内部负责解析和执行查询。
通过以上的Listener的afterLoadData方法的定义,我们已经可以使datasetDept支持查询处理了,可以说万事俱备只差客户端的AJAX查询请求了。
修改界面以便发出查询请求
下一步我们要做的就是给dataset的parameters赋值,并利用dataset的flushData并向服务器重新请求数据。flushData执行时会自动的将parameters中的值传递到服务器的监听器中。
flushData方法的基本说明如下:
flushData是dataset提供的客户端方法,所有dataset都具有该方法。该方法的主要作用是重新从服务器端下载数据集的数据来填充当前dataset。执行此方法时dataset中原有的记录将被全部清除。
在客户端可以直接调用flushData()方法向服务器发出请求:
/范例1:范例说明:利用dataset的flushData()方法,上传dept_id参数信息,并重新获取数据/dataset.parameters().setValue("dept_id", "D11");dataset.flushData(); |
更详细的内容请参考<<dorado 5 用户指南 v1.1 .doc>>。
上述代码范例中的参数值是通过代码写在程序中的,我们的范例希望由用户可以直接在界面上输入查询条件,并单击查询按钮得到查询结果。下面我们就按照这种方式实现datasetDept的查询功能。
界面的基本风格如下:
图表 6 44
上图的最上方区域为用户输入查询条件的区域,并最终单击查询按钮实现部门信息的查询功能。
设计步骤如下:
步骤一:Dept视图模型中添加新的FormDataset,并命名为datasetCondition。同时新增三个字段属性设置如下图:
图表 6 45
步骤二:添加AutoForm对象formQuery,并绑定到datasetCondition上,如下:
图表 6 46
步骤三:新增Button对象buttonQuery,属性设置如下:
属性 | 值 |
---|---|
id | buttonQuery |
value | 查询 |
width | 60 |
另外我们要在buttonQuery的单击事件中获取datasetCondition中的查询参数值并通过dataset的parameters传递到服务器执行查询。
按照上一节SqlDataset实现dept信息维护的做法,我们可以定义buttonQuery的onClick事件,在其中输入代码:
var params = datasetDept.parameters(); params.setValue("deptId", datasetCondition.getValue("deptId")); params.setValue("branchId", datasetCondition.getValue("branchId")); params.setValue("deptName", datasetCondition.getValue("deptName")); datasetDept.flushData();
代码实现的功能就是从datasetCondition中取出值,并储存在datasetDept的parameters中,再调用flushData方法发送到服务器。执行查询动作,这时候会触发服务器端listener的beforeLoadData方法,并最终获得查询后的数据。
不过这一次我们通过QueryCommand对象避免这些JS编程,方式如下:
新建QueryCommand对象,命名为commandQuery,并设定conditionDataset为datasetCondition。queryDataset属性设定为datasetDept。如下图:
图表 6 47
并将buttonQuery的command属性设定为commandQuery。这样我们就不必再定义buttonQuery的onClick事件了,通过commandQuery自动的帮我们完成以上的工作。
步骤四:利用AutoForm的Auto create elements快捷按钮,导入datasetCondition的字段信息。并利用AutoForm的Custom Element对象将buttonQuery引入到formQuery中。如下图:
图表 6 48
步骤五:在dept.jsp上修改代码,将新增的formQuery添加到JSP中:
<d:Layout type="vflow" height="100%"> |
其中的蓝色代码为新增内容
保存所有修改。
(查询设置完成)
体验查询结果
重新启动服务后,刷新页面,在查询条件中输入分公司编号为"D1",并单击查询按钮:
图表 6 49
功能改进四:实现查询分页
由于浏览器本身的限制以及性能上的考虑,构建与BS的系统都需要提供查询结果使用分页的方式显示数据,如下图:
图表 6 50
用户需要使用翻页的方式浏览查询结果集合。
每一次翻页时,必须明确的告诉Server当前请求的是哪一页的数据,在本文中我们称pageIndex,另外还需要告诉Server,请求pageSize信息,便于Server根据这两个参数计算出符合条件的数据并返回到client端。
关于分页处理,dorado提供的所有的dataset都提供了AJAX技术的翻页方法,用以查询和翻页处理。
如上图中我们单击pagepilotDept中的第二页时,系统底层调用的JS代码为:
datasetDept.setPageIndex(2); datasetDept.flushData();
其中的flushData()方法会自动清除dataset中已经下载的所有数据,再向server请求新的数据。
如果想保留已经下载的数据,则我们可以使用:
datasetDept.setPageIndex(2); datasetDept.loadData("end");
则datasetDept会保留已经下载到客户端的数据,并且将向Server请求第二页的数据,并附加到dataset当前所有记录的最后面。
当然我们也可以比较直接的用如下的方法去下载Server指定页的数据:
datasetDept.loadPage(2);
dataset的AJAX翻页请求发送到Server时,都会同时发送dataset的pageIndex,pageSize信息。
在初级教程的开发中,以及本例使用SqlDataset维护部门信息的开发过程中,我们只是配置了datasetDept的Sql属性和pageSize属性,组件pagePilot就能自动的计算分页,以及我们可以通过pagePilotDept对象进行数据的翻页操作。在实现这个功能时,我们并没有做什么特别的工作,但是datasetDept就会自动的计算,什么原因呢?那是它们都继承自DBDataset,DBDataset内部已经默认提供了分页处理功能,会自动的根据客户端的翻页请求,将获得的pageSize,pageIndex信息执行数据查询。
本例我们通过CustomDataset实现分页处理,借此了解dorado的分页处理技术。
第一步:在Dept1_datasetDeptListener的afterLoadData方法中获取pageIndex和pageSize的值
public void afterLoadData(Dataset dataset) throws Exception { |
其中蓝色代码中我们通过dataset的两个方法获取client端分页请求的pageSize与pageIndex属性。
我们可以利用pageIndex与pageSize计算出deptList中符合条件的数据,并转换到datasetDept中,读者你可以自行实现。为了方便开发,dorado中还提供了一个PagingHelper工具类,帮助你实现计算。以上的代码通过PagingHelper进行分页处理后,代码如下:
public void afterLoadData(Dataset dataset) throws Exception { int pageIndex = dataset.getPageIndex(); int pageSize = dataset.getPageSize(); DeptDao dao = new DeptDao(); List deptList = dao.getAll(dataset.parameters()); int rowCount = deptList.size(); // 利用PagingHelper辅助对List的分页 PagingHelper paging = new PagingHelper(pageSize, pageIndex, rowCount); List depts = deptList.subList(paging.getFromIndex(), paging.getToIndex()); dataset.fromDO(depts); // 将depts中的数据反射到Dataset中 dataset.setPageCount(paging.getPageCount()); // 设置Dataset的总页数 }
取出deptList中的部分数据,并交给datasetDept,之后还记得要设置dataset的pageCount信息。告诉dataset总页数信息。
保存所有的修改,并确保更新后的java类被重新编译过。再次刷新页面测试pagePilotDept的使用,可以发现它已经可以实现翻页操作了。你还可以调整Dept1视图模型中的datasetDept的pageSize属性,再次刷新页面后查看运行效果。
Attachments:
















































