DELPHI 6 抢先研究 -- WebSnap 研究之二 - -- 如何实现 Master/Detail

在上次的例子里(见《DELPHI 6 抢先研究》-- WebSnap 研究之一 --《一个最简单的例子》), 我们看到了用 Delphi 6 的 WebSnap 做 B/S 结构的数据库应用开发是多么简单的一件事。在这次的例子里, 我们将要介绍如何用 WebSnap 实现数据库应用中很常用的 Master/Detail 类型的应用。


为了简单起见,我们将在上次的例子的基础上进行。

1.首先,将 HomePg 模块中的 Table1 的 TableName 属性设置为 customer.db ;

2.再放上一个 TQuery 控件,确定其 SessionName 属性已自动设为 Session1 的 SessionName ,将其 DatabaseName 设置为 DBDEMOS , 在其 SQL 属性中输入 SQL 语句:“SELECT * FROM "orders.db" Orders WHERE CustNo = :P_CustNo ”, 将其 Params 属性中的 P_CustNo 参数设置为:DataType=ftInteger, ParamType=ptInput, Value=1351, Type=Integer , 之所以将参数的默认值设为 1351 是因为这是一个在 orders 表中存在的 CustNo 值,若设置其为一个在 orders 表中不存在的值, 则后面的预览将只能看到表结构,而看不到数据;

3.再在些此模块中放一个 TDataSetAdapter ,将其 DataSet 属性设置为 TQuery ;

4.New|WebSnap|WebSnap Page Module (如下图),在其中(与上次的例子中的 PageOptions 对话框相同)选择 Type 为 AdapterPageProducer , Name 设为 Detail ,不选中 Published ,其它不变,然后依次确定;

5.SaveAll ,将新单元命名为: DetailPg ;

6.打开 DetailPg 模块,选择File|Use Unit... ,在弹出的对话框(如下图)中选择 HomePg 后确定;

双击 AdapterPageProducer 将打开一个页面设置窗口(与上次例子的图相同),在其左上角的树的 AdapterPageProducer 节点下增加一个 AdapterForm 节点, 再在这个 AdapterForm 节点下增加一个 AdapterGrid 节点。选取中 AdapterGrid 结点,在 ObjectInspector 中设置其 Adapter 属性为 Home.DataSetAdapter2 , 展开 Adapter 属性,在其中找到 DataSet 属性,再将其展开后找到 Active 属性,将其设为 true ,我们可以看到, 所有 CustNo = 1351 的 orders 记录都显示出来了。但这次与上次的例子不同,有一个警告信息(如下图),这是因为我们这次用了 TQuery 属性, 无法确定查询结果数据集中哪个字段是主键,所以我们必须指定一个字段作为主键;

7.双击 Query1 控件,打开字段编辑器,在其鼠标右键菜单中选取 Add all fields ,加入此 Query 的所有字段。在其中选中 OrderNo 字段, 在 ObjectInspector 中找到并展开 ProviderFlags 属性。将其 pfInKey 属性设置为 True ,表示我们设置 OrderNo 字段为主键(如下图)。 现在再看 DetailPg 中的 AdapterPageProducer 的页面,那个警告就没有了。

8.接着进行的步骤就比较关键而且复杂一些:首先在 Object TreeView 中找到 DataSetAdapter1 ,然后在其 Actions 节点下增加一个 Action 。 在 Object Inspector 中设置此 Action 的 Name 为 GetOrders 。在其 OnGetParams 事件响应中输入如下代码:

    Params.Values['CustNo'] := Table1.FieldByName( 'CustNo' ).AsString;

在其 OnExecute 事件响应中输入如下代码:

    Query1.Close;
Query1.ParamByName( 'P_CustNo' ).AsInteger := StrToInt( Params.Values['CustNo'] );
Query1.Open;

这两段代码看上去很奇怪,在 OnGetParams 事件中将主表 Table1 的 CustNo 字段值保存到 Params 参数中后, 又在 OnExecute 事件中从 Params 参数中取得主表的字段值来设置从表 Query1 的查询参数,为什么不直接把主表的字段值设置给从表的参数呢? 因为 HTTP 协议是不保存状态的,在主表页在显示完成时, Web Server Application 已经执行完成并退出,当点击主表页面的链接去打开从表页面时, 其实是重新运行了一个新的 Web Server Application 实例,此实例与显示主表运行的 Web Server Application 完全无关, 没有记录任何主表的信息,所以不能直接将主表的字段值设置给从表参数,而必须通过 Params 参数。那 Params 参数是如何实现在两个实例间传递数据的呢? 其实 Params 参数是记录在显示主表的 HTML 页面中的,这个过程就是通过 OnGetParams 事件执行的。当在主表中点击链接来显示从表时, Params 参数就随 HTTP 请求被发送到显示从表的 Web Server Application 实例中,在从表显示前执行 OnExecute 设置从表的查询参数, 这样就完成了主/从表页面间参数传递的过程。

9.接下来处理主表的显示:双击 HomePg 中的 AdapterPageProducer ,在其中的 AdapterGrid1 上右击鼠标,选择 Add columns ... 来增加要显示的字段, 如果不加则将显示所有字段,使生成的 Web 页面过宽,不方便后面的操作,所以我们在这里只加上必要的和有代表性的几个字段: CustNo、Company、City、State Zip、Country。确定后即可看到只有这几个字段显示的 Web 页面中了。然后在 AdapterGrid1 上右击鼠标, 选择 New component ...,选择 AdapterCommandColumn 即可增加一个命令列。将此 AdapterCommandColumn1 的 Caption 设置为 Orders , 将在页面的此列上显示为表头的标题。在 AdapterCommandColumn1 上右击鼠标,选择 New component ...,选择 AdapterActionButton 即可在此列中的每一行都增加一个按钮。 选取此 AdapterActionButton 后在 Object Inspector 中设置其 ActionName 为 GetOrders 以及设置其 PageName 为 Detail 即可。

10.剩下的工作便是编译链接此程序,生成一个 CGI : WSTest.exe ,在 IE 的地址栏输入 URL : http://localhost/delphi6/websnap/wstest.exe (各设置与上次的例子相同),即可显示出主表的内容,并且每一行的最后一列都有一个 GetOrders 按钮,点此按钮即可打开 Detail 页面, 并且在此页面中显示出所有 CustNo 字段值与主表相应字段相同的 Orders 记录。


怎么样,用 Delphi 6 进行 B/S 应用开发是不是很简单呢?除了设置一些属性外, 只要写四行代码就可以完成一个 Master/Detail 的 Web 应用开发。

Aug.11-01, Oct.13