Once the data has been retrieved and cached locally, we can use it to populate HTML controls in a Web page (a process called
data binding), or we can work with it directly using ADO code within the page. In fact, even when using data binding, we'll often still implement ADO code in the page. This is particularly the case when we want to update the data and submit the changes back to the server.
Data binding uses the
Data Binding Agent object that is part of Internet Explorer 4 and higher. When programming in a language like Visual Basic or C++, the special controls that are part of that environment are used to implement data binding instead.
The Web-based Data Binding Agent can provide two types of data binding, either
tabular data binding or single record data binding (often called current record data binding). All DSO controls can take part in tabular data binding or single/current record data binding. We'll briefly summarize the HTML controls that are used in Web pages next, then go on to look at the two types of data binding where they can be used.
Controls that can be bound to a DSO recognize special HTML attributes that provide the connection information they need. These are:
DATASRC
- the ID of the DSO that will supply the data, prefixed by a '#' hash character.
DATAFLD
- the name of the field in the DSO's recordset to bind this control to.
DATAFORMATAS
- Either 'TEXT' (the default if omitted) to display the field value as plain text, or 'HTML' to specify that the browser should render any HTML content within the value.
The full list of controls that can participate in data binding in Internet Explorer 4 and onwards is:
HTML Element
Bound property
Update data?
Tabular binding?
Display as HTML
A
href
No
No
No
APPLET
param
Yes
No
No
BUTTON
innerText
andinnerHTML
No
No
Yes
DIV
innerText
andinnerHTML
No
No
Yes
FRAME
src
No
No
No
IFRAME
src
No
No
No
IMG
src
No
No
No
INPUT TYPE=CHECKBOX
checked
Yes
No
No
INPUT TYPE=HIDDEN
value
Yes
No
No
INPUT TYPE=LABEL
value
Yes
No
No
INPUT TYPE=PASSWORD
value
Yes
No
No
INPUT TYPE=RADIO
checked
Yes
No
No
INPUT TYPE=TEXT
value
Yes
No
No
LABEL
innerText
andinnerHTML
No
No
Yes
MARQUEE
innerText
andinnerHTML
No
No
Yes
OBJECT
param
Yes
No
No
SELECT
text of selected
option
Yes
No
No
SPAN
innerText
andinnerHTML
No
No
Yes
TABLE
none
No
Yes
No
TEXTAREA
value
Yes
No
No
So, as an example, we could bind a
SPAN element to the value in a field named tTitle, which is in a recordset exposed by a DSO that has the ID of dsoBookList, using:
tTitle field in the current record in the recordset would then be displayed in the page within the SPAN element as plain text (the default). If the tTitle field contains HTML formatting within the value, we can cause the browser to render it as such using:
We can also use client-side script to set up or change the bindings once the page has loaded, by changing the
dataSrc, dataFld and dataFormatAs properties of the appropriate elements. To remove the bindings, set the properties to an empty string. To change the binding of elements within a bound table, we must remove the binding of the table first (in the <TABLE> tag), change the bindings of the elements in the table, and then reset the binding of the table.
Tabular Data Binding
Tabular data binding depends on the ability of the
<TABLE> element to repeat the contents of the <TBODY> section once for each record. It's important to recognize that the use of the word tabular - in the sense of the way the data is bound to controls - is entirely unconnected with the name of the Tabular Data Control.
The data source object is identified within the opening
<TABLE> tag, and the column or field name for each bound control is identified within each table cell. Note that the <TD> element itself does not take part in the data binding process. Instead, a bound element is placed within each cell. This could be a <SPAN> or a <DIV> element, or one of the other HTML controls. For example:
... 'definition of a DSO named dsoBookList is elsewhere in the page ... ...
<TABLE DATASRC="#dsoBookList">
<THEAD>
<TR>
<TH>Code</TH>
<TH>Category</TH>
<TH>Release Date</TH>
<TH>Title</TH>
<TH>Sales</TH>
</TR>
</THEAD>
<TBODY>
<TR>
<TD><SPAN DATAFLD="tCode"></SPAN></TD>
<TD><I><SPAN DATAFLD="tCategory"></I></SPAN></TD>
<TD><SPAN DATAFLD="dReleaseDate"></SPAN></TD>
<TD><B><SPAN DATAFLD="tTitle"></SPAN></B></TD>
<TD><SPAN DATAFLD="nSales"></SPAN></TD>
</TR>
</TBODY>
</TABLE>
...
Table Paging with Tabular Data Binding
ADO recordsets support paging, as we discovered in Chapter 5 when we looked at the
Recordset object. When tabular data binding is used, the <TABLE> element is bound to the DSO that provides the source recordset, and displays the records in that recordset. The DSO properties are exposed through the bound <TABLE> element, and this includes the dataPageSize property. In IE4 and above, this property is mapped to the DATAPAGESIZE attribute of the table as well:
DATAPAGESIZE
- sets the maximum number of records that will be displayed within the body of a table.
By setting this attribute in the opening HTML
<TABLE> tag, we can create a table that displays only a specified number of records:
<TABLE DATASRC="#dsoBookList" DATAPAGESIZE=10>
Then, we can move through the recordset by using the
nextPage and previousPage methods. The bound table also exposes the recordNumber property of the underlying data set for each element within the table.
Single Record Data Binding
Tabular display is fine for displaying data, but for editing it we really need to be able to display the values from one record at a time within HTML controls. This is particularly true if we want to allow the user to update the source data.
As soon as a recordset is created by a DSO, the first record becomes the
currentrecord, in exactly the same way as when we create a recordset using ADO directly (if the recordset is empty, the EOF and BOF properties are both True at this point). We can display the values from the current record in any of the bindable HTML elements listed earlier by setting their DATASRC and DATAFLD properties. For example:
... 'definition of a DSO named dsoBookList is elsewhere in the page ... ...
Code: <INPUT TYPE="TEXT" DATASRC="#dsoBookList" DATAFLD="tCode" SIZE=6><P>
Title: <INPUT TYPE="TEXT" DATASRC="#dsoBookList" DATAFLD="tTitle" SIZE=20><P>
Category: <SELECT DATASRC="#dsoBookList" DATAFLD="tTitle" SIZE=1>
<OPTION>HTML
<OPTION>Scripting
<OPTION>ASP
</SELECT><P>
Release date: <SPAN DATASRC="#dsoBookList" DATAFLD="dReleaseDate"><P>
Sales to date: <SPAN DATASRC="#dsoBookList" DATAFLD="nSales"><P>
...
This produces the page we first saw earlier in this chapter, and the screenshot is repeated here. The buttons at the bottom of the page allow users to move around the recordset, and they work by calling the appropriate
move methods of the underlying recordset object-as described below.
Moving Around and Updating the Data
If we display a single record, we need a way to move to another record. We also need a way to update the source data (if this is appropriate, depending on the DSO we are using) by adding, deleting and editing records. And we will probably also need to cancel updates, and refresh the data displayed in the controls at some stage. All these tasks are accomplished using the standard methods of the DSO that are exposed via ADO. The most common methods are:
Method
Description
move, moveFirst,
moveLast, moveNext,movePrevious
move the current record pointer within the cached recordset
cancelUpdate
cancels all changes made to cached records
refresh
re-queries the data source and reloads the recordset data cache
delete
removes the current record from the cached recordset
addNew
adds a new record to the cached recordset
submitChanges
updates the source data with all the changes made to the cached recordset
To move to the next record, as shown in the previous screenshot, we can use a normal HTML
BUTTON control to call the exposed methods of the DSO:
See Appendix F for a full list of the properties, methods and events for the ADO
recordset object.
Events Raised by Internet Explorer 4/5 and the RDS DSO
Both a DSO embedded within the page, and the browser itself, raise events that can be trapped and used in script on the client. We'll look at these events briefly next. They can be divided into two groups: those raised by the browser or the controls on the page (when the user navigates to another page or edits the data in the HTML controls), and those raised by a DSO as the user edits the data it exposes.
Events Raised by HTML Elements and the Browser
When a page containing a DSO is unloaded, or when the user edits the data in HTML controls that are bound to a DSO, various events are raised. Some can be cancelled by returning the value
false from the event handler routine:
Event
Cancel?
Description
onbeforeupdate
Yes
Occurs before the data in the control is passed to the DSO.
onafterupdate
No
Occurs after the data in the control has been passed to the DSO.
onerrorupdate
Yes
Occurs if an error prevents the data being passed to the DSO.
onbeforeunload
No
Occurs before the current page is unloaded.
The
onbeforeunload is raised by the window object, while the remainder are raised by the HTML controls on the page. With the exception of the onbeforeunload event, all events bubble up through the document hierarchy. So, we can display a message when the user changes the value in a control with:
<INPUT ID="txtTitle" DATASRC="#dsoBookList" DATAFLD="tTitle">
...
<SCRIPT LANGUAGE="JavaScript">
function txtTitle.onbeforeupdate() {
return confirm("Are you sure you want to change this value ?");
}
</SCRIPT>
Events Raised by the RDS Data Source Object
The RDS Data Source Object itself raises events as various actions take place. Most are concerned with indicating the current state of the DSO as it loads the data. However, the first two are fired when the 'current record' changes as the user moves through the recordset. This could be by clicking on a tabular data-bound table, or when the code in a page that uses single-record data binding calls one of the recordset
move methods:
Event
Cancel?
Description
onrowenter
No
Occurs for a record when it becomes the current one during navigating the recordset.
onrowexit
Yes
Occurs for a record before another record becomes the current one during navigating the recordset.
ondataavailable
No
Occurs periodically while data is arriving from the data source.
ondatasetcomplete
No
Occurs when all the data has arrived from the data source.
ondatasetchanged
No
Occurs when the data set changes, such as when a filter is applied.
onreadystatechange
No
Occurs when the
readyState property of the DSO changes.
Only the
onrowexit event can be canceled by returning false from the event handler routine. All events bubble up through the document hierarchy. It's usual to take advantage of the ondatasetcomplete event for any script that you want to run once the data has arrived:
<INPUT ID="txtStatus" VALUE="Initializing, please wait ...">
...
<SCRIPT LANGUAGE="JavaScript">
function dsoBookList.ondatasetcomplete() {
txtStatus.value = "Data arrived OK";
}
</SCRIPT>
Manipulating Data Directly with ADO
It's important to realize that each data source object is itself a
DataControl object, and as such is an OLE-DB data provider. This has to be the case for ADO to be able to access the data exposed by the DSOs-remember that ADO is a way of communicating with an OLE-DB data provider (or an ODBC driver that exposes an OLE-DB interface).
The only difference here is that the data store is now a locally cached recordset, and so the
DataControl object or DSO takes the place of the server-side OLE-DB data provider. In fact, ADO continues this terminology by referring to bound controls on a Web page as data consumers.
Filling a SELECT List
Each DSO exposes the cached recordset to code running on the client through its
recordset property. We can create a reference to this and work with the contents of the recordset. So, once we've got the data into a client-side cached recordset, we can use it just like we would in ADO on the server. For example we can use it to populate a <SELECT> list by iterating through the recordset. Note that we do it in the ondatasetcomplete event handler, where we know that the data has arrived:
<OBJECT CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83"
ID="dsoBookList" WIDTH=0 HEIGHT=0>
<PARAM NAME="DataURL" VALUE="booklist.txt">
<PARAM NAME="FieldDelim" VALUE=";">
<PARAM NAME="UseHeader" VALUE="true">
</OBJECT>
<SCRIPT LANGUAGE="JavaScript">
function dsoBookList.ondatasetcomplete() {
objListBox = document.all("MyList");
// get reference to SELECT list objListBox.options[0].text = "Select a book..."; // change current OPTION text recBooks = document.dsoBookList.recordset; // the DSO's recordset while (!recBooks.EOF) { // add titles to SELECT list strTitle = '' + recBooks("tTitle");
objListBox.options.length += 1;
objListBox.options[objListBox.options.length - 1].text = recBooks("tTitle"); recBooks.MoveNext();
}
}
</SCRIPT>
Here's the result, showing the list box contents after the
ondatasetcomplete event has fired, and the values have been inserted into the list by creating new <OPTION> elements within it:
In some circumstances, the RDS control can fail to initiate the data transfer from server to client as there are no bound controls on the page. You can get round this by placing a HIDDEN-type control on the page and binding it to one field in the recordset, for example: <INPUT TYPE="HIDDEN" DATASRC="#dsoBookList" DATAFLD="tTitle">
Using the Recordset with Internet Explorer Dynamic HTML
As another example of using the recordset directly, we can create output in the page using Dynamic HTML-here to place values from the records into a
<H3> heading element previously defined in the page:
<OBJECT CLASSID="clsid:333C7BC4-460F-11D0-BC04-0080C7055A83"
ID="dsoBookList" WIDTH=0 HEIGHT=0>
<PARAM NAME="DataURL" VALUE="booklist.txt">
<PARAM NAME="FieldDelim" VALUE=";">
<PARAM NAME="UseHeader" VALUE="true">
</OBJECT>
<SCRIPT LANGUAGE="JavaScript">
var strList = 'List of books:<P>';
function dsoBookList.ondatasetcomplete() {
recBooks = document.dsoBookList.recordset;
// the DSO's recordset while (!recBooks.EOF) { // add titles to strList string strTitle = '' + recBooks("tTitle") + '<BR>';
strList += strTitle;
recBooks.MoveNext();
}
document.all("MyHeading").innerHTML = strList; // put string into H3 element
}
</SCRIPT>
Both of these examples use similar code to get a reference to the client-side ADO recordset object, and then iterate through it extracting values from the records. You can use the values in more creative ways of course, and these two simple examples are designed just to show the basic technique.
The samples for this book contain several examples that uses the various DSOs we've discussed. You can download the samples, and even run some of them directly, from our Web site at