在本系列的第一篇文章中,我们对 SmartGWT 进行了全面但概括的介绍。其中提到,作为面向企业级 Web2.0应用的开发框架,SmartGWT 不仅仅是“又一个 Web 控件库”,它最大的特色在于提供了整合界面和服务器端数据的框架。在 SmartGWT中,将 UI 组件和 SmartGWT 数据源联系到一起称为“数据绑定”(Data Binding)。而将 SmartGWT数据源和后台服务器数据服务结合的过程称为“数据集成”(DataIntegration)。本文只介绍数据绑定部分,而数据集成部分将在本系列的第三篇文章中介绍。
SmartGWT 的数据集成功能扎根于它所封装的 SmartClient Ajax 框架的支持。大多数的 Ajax框架都只关注如何表现只读数据,而 SmartClient在设计之初就将服务器端的数据绑定作为它内置的功能,从而能将用户做的数据改变传达到前端页面。在 SmartClient 中,通过实现客户端MVC(Model-View-Controller,模型 - 视图 - 控制器)模式的框架,所有的表示层职责和所有 HTML的生成都在浏览器中完成。HTML 的生成或表示层职责都不需要通过服务器来处理。而服务器端提供了安全数据服务的集合。一旦 SmartClient加载完成,在浏览器与服务器之间只有数据在传输。因此,SmartGWT中的数据源能够提供类似数据库的功能,是所有业务数据更新的中间媒介。当数据源与服务器交互时,用户对数据做的操作能自动地被保存到服务器端,并且同时显示到其它的组件上。比如,在实际的业务环境中,用户不仅能从服务器端的业务对象中读取数据,在客户端的任意组件中显示,而且在 UI组件上做的任何增删改操作都能保存到数据模型中,并且将更新传达到其它的基于相同数据源的 UI 组件上。
下面我们将通过员工信息系统的实例来展示如何进行数据和界面的轻松绑定。读者将看到怎样利用 SmartGWT中的数据绑定功能,快速开发出界面友好,功能齐全的 Web 应用。
在企业的经营生产中,员工信息已成为企业经营管理中不可或缺的一部分,为管理者进行管理决策和进行各种人事调配活动提供了重要的依据,在生产分配安排中发挥了越来越重要的作用。员工信息系统能够为用户提供充足的信息,用户通过 Web 客户端便捷地进行员工数据操作能大大提高工作效率。
企业员工信息管理系统的基本功能是能实现员工信息的查询、录入、修改和删除等功能。下面我们将开发一个基于 SmartGWT的员工信息系统,用户能通过 Web 客户端方便地查询和操作存储在数据库中的员工信息。
该系统的基本功能如下:
由于本文的重点在客户端的页面显示,因此服务器端的数据存储只做简要描述。数据库中的员工信息表的结构表 1 所示:
字段名 | 类型 | 主键 | 可否为空 |
---|---|---|---|
EmployeeId | Integer | 是 | 否 |
ReportsTo | Integer | 否 | 否 |
Name | String | 否 | 否 |
Job | String | 否 | 可 |
OrgUnit | String | 否 | 可 |
OnBoard | Date | 否 | 可 |
Salary | float | 否 | 可 |
其中,ReportsTo 字段是指向 EmployeeId字段的外键。其值表示该员工直接汇报的经理的员工号。综上,该表结构表示了基本的员工信息,是一个典型的关系数据库表。
数据源(DataSource)是整个数据集成中的核心类,用户通过数据源来定义数据模型和访问方式。用数据源来定义数据模型的方式和用SQL 语句描述数据库 schema的方式非常近似。在数据源中,每一条数据称为一个记录(Record)。对记录的各个字段(Field),用户能定义它们的类型和约束条件,能定义父子关系,能声明指向另一个数据源的外部关键码(foreignkey)。由此可见,数据源不只是个简单的二维数据存储,更是用户的数据模型和关系的丰富表现。一旦用户定义好数据源,数据就能通过多种方式读取到数据源中,比如本地数组数据、XML、JSON 以及来自服务器端的数据等。数据源内置四种数据访问方式:FETCH,ADD,UPDATE 和REMOVE。
SmartGWT 中的 DataSource,Record,Field 这些概念和常用的数据存储模型可以直接对应,如表 2 所示:
SmartGWT | 关系数据库 | Enterprise Java Beans (EJB) | 实体 / 关系 模型 | OO/UML | XML Schema/WSDL | LDAP |
---|---|---|---|---|---|---|
DataSource | Table | EJB class | Entity | Class | Element Schema (ComplexType) | Objectclass |
Record | Row | EJB instance | Entity instance | Class instance/Object | Element instance (ComplexType) | Entry |
Field | Column | Property | Attribute | Property/Attribute | Attribute or Element (SimpleType) | Attribute |
我们的员工数据库是一个关系数据库。但是对于 Web 程序,我们需要用 Http 请求来得到这些数据。在此假设,我们有一个 Web服务,可以提供访问数据库的方法,并且它返回的是 XML。该 XML 的格式举例如下:
<List> <employee> <EmployeeId>4</EmployeeId> <ReportsTo>1</ReportsTo> <Name> 刘备 </Name> <Job>CEO</Job> <OrgUnit> 总部 </OrgUnit> <OnBoard>2004/01/01</OnBoard> <Salary>26200.00</Salary> </employee> ... |
下面我们来看怎样创建员工数据库的 DataSource。SmartGWT 提供了可直接使用的具体化的 DataSource类。但为了清晰起见,推荐的做法是为该数据源创建一个 DataSource 的子类。每个 DataSource 包含一系列的DataSourceField。
我们创建 EmployeeXmlDS 类,它继承DataSource,并且是一个单子(Singleton)类。在构造函数中,我们定义了 ID 和 RecordXPath,SmartGWT能自动识别出这是一个 XML 数据源。DataURL 指明了数据访问的 URL。这里我们使用一个 XML 来测试它,所以是一个静态的URL。ClientOnly 表明我们目前只测试客户端数据绑定,不涉及服务器端的访问。
import com.smartgwt.client.data.DataSource; public class EmployeeXmlDS extends DataSource { private static EmployeeXmlDS instance = null; public static EmployeeXmlDS getInstance() { if(instance == null) { instance = new EmployeeXmlDS("employeesDS"); } return instance; } public EmployeeXmlDS(String id) { setID(id); setRecordXPath("/List/employee"); setDataURL("ds/employees.data.xml"); setClientOnly(true); } } |
接下来为该数据源定义字段。首先要创建 DataSourceField 对象,然后将它们设置到数据源中。对于不同类型的字段,我们需要创建不同的DataSourceField 子类对象,如 DataSourceTextField,DataSourceIntegerField等。对每个字段对象,可以设置相关属性。代码比较直观,就不多做解释了。
DataSourceIntegerField employeeIdField = new DataSourceIntegerField("EmployeeId", "员工号"); employeeIdField.setPrimaryKey(true); employeeIdField.setRequired(true); DataSourceIntegerField reportsToField = new DataSourceIntegerField("ReportsTo", "管理者"); reportsToField.setRequired(true); reportsToField.setForeignKey(id + ".EmployeeId"); reportsToField.setRootValue("1"); DataSourceTextField nameField = new DataSourceTextField("Name", "姓名", 128); nameField.setRequired(true); DataSourceTextField jobField = new DataSourceTextField("Job", "职位", 128); DataSourceDateField emailField = new DataSourceDateField("OnBoard", "入职日", 128); DataSourceFloatField salaryField = new DataSourceFloatField("Salary", "薪水"); DataSourceTextField orgField = new DataSourceTextField("OrgUnit", "部门", 128); setFields(nameField, employeeIdField, reportsToField, jobField, emailField, salaryField, orgField); |
到这里,针对员工信息的 SmartGWT 数据源已经定义完成。下面我们需要创建相应的 UI 组件。
SmartGWT 提供了丰富的 Web 组件,我们需要根据需要选择一个适合该项目的组件。
用 TreeGrid 显示员工信息
由于员工数据库是一个关系数据库,我们可以用一个表格来显示它。同时,员工之间的领导与被领导的关系,需要通过树形组件来显示出层级效果。这二者的结合就是 TreeGrid 组件。TreeGrid 是结合了表格和树的 UI组件。本质上它是一个表格。但是我们可以将其中的一列指定为有层级关系的列,让它以该列为基准,显示成为树形。
要生成 TreeGrid,需要为它设置各种属性,并定义一系列的TreeGridField,代码如下所示。对于需要被设置成为树形结构的字段,将 TreeField 属性设置为 True。
TreeGridField fieldName = new TreeGridField("Name","姓名", 150); fieldName.setTreeField(true); TreeGridField fieldJob = new TreeGridField("Job","职位", 150); TreeGridField fieldSalary = new TreeGridField("Salary","薪水",90); TreeGridField orgField = new TreeGridField("OrgUnit", "部门", 150); employeeTree = new TreeGrid(); employeeTree.setAutoFetchData(true); employeeTree.setLoadDataOnDemand(true); employeeTree.setCanEdit(false); employeeTree.setCanReorderRecords(true); employeeTree.setCanAcceptDroppedRecords(false); employeeTree.setClosedIconSuffix(""); employeeTree.setFields(fieldName, fieldJob, orgField,fieldSalary); employeeTree.setTreeFieldTitle("Name"); |
用 DynamicForm 编辑员工信息
为了编辑详细信息,需要有一个表单。SmartGWT 提供了 DynamicForm组件。它可以根据配置好的数据源来自动生成相应的表单,并提供一定的数据校验功能。DynamicForm创建非常简单,只要设置给它相应的数据源就可以。然后通过一系列方法即可编辑相应的来自于同一数据源的 ListGrid 或者 TreeGrid的数据。链接同一数据源与不同视图,正是 MVC 的魔力。
DynamicForm form = new DynamicForm(); |
在上面两节里,我们通过 DataSource 定义了数据模型,选定了 UI组件。现在我们需要将它们结合起来,即数据绑定的过程。由于 SmartGWT提供的组件都遵循统一的数据绑定模型,能给组件直接指定相应的数据源,因此数据绑定即把数据源直接设置给相应的 UI 组件即可,非常简单。
employeeTree.setDataSource(EmployeeXmlDS.getInstance()); form.setDataSource(datasource); |
为了将员工信息清单和详细信息表单联系起来,需要给 TreeGrid 增加一个事件处理机制。
employeeTree.addRecordClickHandler(new RecordClickHandler() { public void onRecordClick(RecordClickEvent event) { form.reset(); form.editSelectedData(employeeTree); } }); |
为了新建、保存和删除数据,我们增加了三个按钮,newButton,saveButton 和removeButton。在这些按钮的事件处理函数里我们只需要调用相应 UI组件的数据处理功能即可。它们会自动调用数据源来处理后台数据。注意,这里只是在内存中的存储,并没有持久化到后台数据库中。
employeeTree.addRecordClickHandler(new RecordClickHandler() { public void onRecordClick(RecordClickEvent event) { form.reset(); form.editSelectedData(employeeTree); } }); |
至此,我们的员工信息系统已经开发完成,漂亮的页面如图 1 所示:
相关的功能已经可以进行测试。在右边进行的新建,保存,删除操作会及时的反应在左边的表格中。但是因为并没有后台服务器真正的数据库支持,这些改动只在当前客户端中保持。一旦重新启动该应用,将会回到初始状态。真正实现和服务器端数据库的交互,是数据集成要做的事。
检查 Host 服务器输出已经 web.xml 文件,查看是否有多余的 servlet 配置。
检查在入口 html 文件中,是否设置了 isomorphicDir 全局变量。
检查 isomorphicDir 是否在其它 <script> 标签前设置。
本文通过实例介绍了 SmartGWT中的数据绑定功能。这里的数据源只是存在于客户端内存中,没有连接到后台持久化服务。我们将在本系列的下一篇文章中扩展该实例,代之以真正的数据库数据源,以此来介绍 SmartGWT 提供的高级数据功能—数据集成。
描述 | 名字 | 大小 | 下载方法 |
---|---|---|---|
示例代码 | EmployeeInfo.zip | 16KB | HTTP |
联系客服