表10-5 支持数据绑定的html元素
| html元素 | 绑定属性 |
可否更 新数据 |
可否表 格绑定 |
可否作为 html显示 |
| a | href | 不可 | 不可 | 不可 |
| applet | param | 可以 | 不可 | 不可 |
| button | innertext和innerhtml | 不可 | 不可 | 可以 |
| div | innertext和innerhtml | 不可 | 不可 | 可以 |
| frame | src | 不可 | 不可 | 不可 |
| iframe | src | 不可 | 不可 | 不可 |
| img | src | 不可 | 不可 | 不可 |
| input | checked | 可以 | 不可 | 不可 |
| type=checkobx input | value | 可以 | 不可 | 不可 |
| type=hidden input | vale | 可以 | 不可 | 不可 |
| type=label input | value | 可以 | 不可 | 不可 |
| type=password input | checked | 可以 | 不可 | 不可 |
| type=radio input | value | 可以 | 不可 | 不可 |
| type=text label | innertext和innerhtml | 不可 | 不可 | 可以 |
| legend | innertext和innerhtml | 不可 | 不可 | 不可 |
| marquee | innertext和innerhtml | 不可 | 不可 | 可以 |
| object | param | 可以 | 不可 | 不可 |
| select | 选择的<option>元素文本 | 可以 | 不可 | 不可 |
| span | innertext和innerhtml | 不可 | 不可 | 可以 |
| table | 无 | 不可 | 可以 | 不可 |
| textarea | value | 可以 | 不可 | 不可 |
2. 单个记录绑定
单个记录绑定用于只显示单行数据的情况。例如,考虑下面的代码:
id: <span datasrc="#dsodata" datafld="au_id"></span><br>
first name: <span datasrc="#dsodata" datafld="au_fname"></span><br>
last name: <span datasrc="#dsodata" datafld="au_lname"></span><br>
phone: <span datasrc="#dsodata" datafld="phone"></span><br>
address: <span datasrc="#dsodata" datafld="address"></span><br>
city: <span datasrc="#dsodata" datafld="city"></span><br>
state: <span datasrc="#dsodata" datafld="state"></span><br>
zip: <span datasrc="#dsodata" datafld="zip"></span><br>
contact: <span datasrc="#dsodata" datafld="contract"></span><br>
使用单个记录绑定时,每一个绑定的html元素都要确定数据源(datasrc)和绑定的字段(datafld)。
以上数据绑定的结果如图10-5所示:
图10-5 单个记录绑定的结果
作为一个结果来说,这已经满足要求了,但由于在html文档中忽略了空格,所以数据排列得不整齐。数据绑定使我们易于得到数据,但看上去不太美观。一个好方法是使用表格来对齐数据。
<table id="tbldata">
<tr><td>id:</td>
<td><span datasrc="#dsodata" datafld="au_id"></span></td></tr>
<tr><td>first name:</td>
<td><span datasrc="#dsodata" datafld="au_fname"></span></td></tr>
<tr><td>last name:</td>
<td><span datasrc="#dsodata" datafld="au_lname"></span></td></tr>
<tr><td>phone:</td>
<td><span datasrc="#dsodata" datafld="phone"></span></td></tr>
<tr><td>address:</td>
<td><span datasrc="#dsodata" datafld="address"></span></td></tr>
<tr><td>city:</td>
<td><span datasrc="#dsodata" datafld="city"></span></td></tr>
<tr><td>state:</td>
<td><span datasrc="#dsodata" datafld="state"></span></td></tr>
<tr><td>zip:</td>
<td><span datasrc="#dsodata" datafld="zip"></span></td></tr>
<tr><td>contact:</td>
<td><span datasrc="#dsodata" datafld="contract"></span></td></tr>
</table>
这个html文档虽然不容易阅读,但却提供了一个较好的显示结果,如图10-6所示:
图10-6 单个记录绑定的表格显示结果
注意,这个例子只显示了使用span元素来存放数据。如果想编辑数据,那么可以使用input元素来实现。例如:
<table id="tbldata">
<tr><td>id:</td>
<td>
<input type="text" datasrc="#dsodata" datafld="au_id"></input>
</td>
</tr>
…
</table>
这里使用了一个text类型的input元素。注意,数据绑定几乎是相同的,仅仅是html元素不同。结果如图10-7所示:
图10-7 单个记录绑定的编辑界面
数据导航
除非能得到其他记录,否则只显示单条记录并不理想。幸运的是数据控件有一个recordset属性,它是实际的含有数据的ado记录集。回顾第8章,应该记得记录集有移动记录的方法:
· movefirst。
· movenext。
· moveprevious。
· movelast。
举一个例子,假定想在html页面中增加一些按钮以获得记录导航的能力,如图10-8所示:
图10-8 导航按钮
可以用如下代码创建按钮:
<button id="cmdfirst" title="first record"
onclick="dsodata.recordset.movefirst()"> |< </button>
<button id="cmdprevious" title="previous record"
onclick="if (!dsodata.recordset.bof) dsodata.recordset.moveprevious()">
< </button>
<button id="cmdnext" title="next record"
onclick="if (!dsodata.recordset.eof) dsodata.recordset.movenext()">
> </button>
<button id="cmdlast" title="last record"
onclick="dsodata.recordset.movelast()"> >| </button>
这些代码仅仅利用了记录集中移动记录的方法。移到第一条和最后一条记录实现想来相当容易。只需记住数据控件有一个recordset属性,由于该属性是一个对象,所以有其自己的方法。因此,代码可以写为:
dsodata.recordset.movefirst()
以上代码只是简单地调用数据控件管理的记录集的movefirst方法。
移到上一条和下一条记录的代码看上去有一点技巧,但也很简单。
if (!dsodata.recordset.bof)
dsodata.recordset.moveprevious()
以上是向后移动记录的方法,只需在执行moveprevious方法之前,判断一下记录集的bof属性,以确定当前记录不在记录集的开始位置。
3. 表格绑定
表格绑定不同于单个记录绑定,因为不只是为对齐数据而使用表格。在表格绑定中把数据绑定到了table元素,能够一次看到多条记录,如图10-9所示:
图10-9 表格绑定的界面
这甚至比单个记录绑定更容易,实现表格绑定需要使用表格的datasrc属性,然后使用datafld属性绑定表格元素。这样就将表格与数据控件绑定起来,每一个表格单元绑定到单独的字段。
然而,看一下能够被绑定的html元素的列表,会发现表格单元元素(td)并不在其中。因为这个原因,一般为只读的表格使用span或div标记,而对于可编辑的表格则使用input标记。例如,图10-9中的表格是用下列代码创建的:
<table id="tbldata" datasrc="#dsodata">
<thead>
<tr>
<td>au_id</td>
<td>au_fname</td>
<td>au_lname</td>
<td>phone</td>
<td>address</td>
<td>city</td>
<td>state</td>
<td>zip</td>
<td>contract</td>
</tr>
</thead>
<tbody>
<tr>
<td><input type="text" datafld="au_id"></input></td>
<td><input type="text" datafld="au_fname"></input></td>
<td><input type="text" datafld="au_lname"></input></td>
<td><input type="text" datafld="phone"></input></td>
<td><input type="text" datafld="address"></input></td>
<td><input type="text" datafld="city"></input></td>
<td><input type="text" datafld="state"></input></td>
<td><input type="text" datafld="zip"></input></td>
<td><input type="text" datafld="contract"></input></td>
</tr>
</tbody>
</table>
table元素还有另外一个用于数据绑定的属性:datapagesize,决定了在表格中可以显示的记录数。
<table id="tbldata" datasrc="#dsodata" datapagesize="10">
在上面的例子中,表格一次只能含有10条记录。记录集的移动方法在这里不起作用,因为表格限制了可见的记录,所以必须使用表格的两个方法,如下所示:
<button id="cmdpreviouspage" title="previous page"
onclick="tbldata.previouspage()">previous page</button>
<button id="cmdnextpage" title="next page"
onclick="tbldata.nextpage()">next page</button>
4. 动态绑定
到目前为止,所有的例子都只显示了一个固定的记录集,绑定的字段在设计期间已经创建。但看起来大量的代码不能重用,特别是在web应用程序正给用户带来越来越强的功能的情况下,这种方式缺乏开发程序的灵活性。
解决这个难题的方法是根据数据控件中的数据动态创建表中的字段。实际上这也比较容易,依赖于客户端的脚本程序。那么,假定让用户在表authors和publishers中进行选择,如图10-10所示:
图10-10 使用rds动态数据绑定的界面
现在我们并不真想绑定两个表的所有字段,因为这会变得难以维护。如果源数据的结构改变了,或者想增加另一个表,情况将会怎样?处理这种情况的方法就是创建一个虚表,在运行期间动态地创建和绑定字段。
首先,创建数据控件。
<object classid="clsid:bd96c556-65a3-11d0-983a-00c04fc29e33"
id="dsodata" height="0" width="0"
ondatasetcomplete="createcells()">
</object>
这是rds数据控件,与前面例子唯一不同的是这里没有设置参数,代码中也是如此。唯一增加的是设置了一个在数据控件读完数据后运行的函数。
接下来,需要创建两个按钮来确定数据。
<button id="cmdauthors"
onclick="resetdata(authors)">authors</button>
<button id="cmdpublishers"
onclick="resetdata(publishers)">publishers</button>
下面创建虚表。
<table id="tbldata" border=1>
<thead><tr></tr></thead>
<tbody><tr></tr></tbody>
</table>
这充当了模板的作用。注意,表格中还没有单元格。这是因为并不知道数据有多少个字段,所以也将在运行期间创建它们。
现在编写jscript代码。首先看一下resetdata函数,该函数设置数据控件的属性并加载数据。
function resetdata(stable)
{
// reset the data
dsodata.connect = provider=sqloledb; data source= +
<%= request.servervariables("server_name") %> +
; initial catalog=pubs; user id=sa; password=;
dsodata.server = http://<%= request.servervariables("server_name") %>;
dsodata.sql = select * from + stable;
dsodata.refresh();
}
虽然这看起来比使用参数更复杂一些,但是仍然比较简单。别忘了参数名是如何映射到属性的?这里所做的就是设置那些属性,然后调用refresh方法更新数据控件。看上去,这可能比以前的例子更糟糕,因为在代码中只有不多的asp,也只是简单地在属性中填入web服务器的名字。但使用该方法可以在不修改代码的情况下将此asp页面从一个服务器移到另一个服务器。作为数据源的表名可以通过选择适当的按钮而传给函数。
一旦加载了数据,将触发数据控件的ondatasetcomplete事件,运行createcells函数。
function createcells()
{
var fldf;
var tblcell;
// delete whats there already
deletecells();
// now create the new cells
for (fldf = new enumerator(dsodata.recordset.fields);
!fldf.atend(); fldf.movenext())
{
// create a new cell for the heading
tblcell = tbldata.rows[0].insertcell();
tblcell.innerhtml = <b> + fldf.item().name + </b>;
// create a new cell for the body
tblcell = tbldata.rows[1].insertcell();
tblcell.innerhtml = <input type="text" datafld=" +
fldf.item().name + "></input>;
}
// now bind to the data source
tbldata.datasrc = #dsodata;
}
这同样也很简单。首先删除了现有的表格单元格(马上会介绍这个函数),然后遍历记录集的字段。在行头为每个字段创建一个新单元格(这个表格只有两行:第一行,即第0行,是表头;第二行,即第1行,是表体)。表格单元创建完后,将innerhtml属性设为对应的字段名。在表体中创建新单元格的过程类似,但此时使用innerhtml元件保存绑定到数据字段的input标记。当所有的字段都完成这样的操作后,这个表就与数据控件绑定了。
因为这个页面允许在两个不同的数据集之间进行切换,所以需要先删除现有的数据。
function deletecells()
{
var icell;
var icells;
// unbind the table
tbldata.datasrc = ;
// delete existing cells
icells = tbldata.rows[0].cells.length
for (icell = 0; icell < icells; ++icell)
{
tbldata.rows[0].deletecell();
tbldata.rows[1].deletecell();
}
}
这个子程序只是对表解除绑定,然后在表格中遍历所有的单元格并删除它们。等到上述程序执行完毕,表格就只剩下空的表头和表体行。
这是一个用rds和一些dhtml实现的简单例子。可以容易地把其加到一个asp包含文件中,并把该文件放到任何应用程序中,即使数据源不改变也可使用这种方法。
这个例子的全部代码——文件rdsdynamicbinding.asp以及类似的其他类型的数据控件例子,可以在wrox站点上找到。
10.2.6 更新数据
迄今为止,仅学习了在客户端如何取到数据,但还没有涉及如何更新客户端数据,和将其送回服务器。别忘了,记录集是断开连接的,那么如何更新数据呢?对数据所做的任何修改只是数据控件中本地记录的一部分,因此为了更新服务器必须发一条特殊的指令。然而这并不需做什么复杂的工作,因为rds数据控件有两个方法,允许我们要么取消最近对数据所做的任何修改,要么将所有修改送到服务器。
为了方便用户,可以为此创建一些按钮。
<button id="cmdcancelall" title="abandon all chnages"
onclick="dsodata.cnacelupdate()">cnacel</button>
<button id="cmdupdateall" title="save all changes"
onclick="dsodata.submitchanges()">save</button>
submitchanges方法只将那些改动过的记录送回服务器,而cancelupdate方法则取消在本地记录集上所做的任何修改。
更新和取消更新操作并不是唯一所需的。如果想增加新的记录或删除一条现有的记录,怎么办?可以使用记录集的addnew和delete方法。这将增加或删除记录集中的记录,然后在发送submitchanges命令后,服务器上的数据就可以被更新。
<button id="cmddelete" title="delete this record"
onclick="dsodata.recordset.delete()">delete</button>
<button id="cmdaddnew" title="add new record"
onclick="dsodata.recordset.addnew()">add</button>
