DHTML Event Model Support for Data Binding

The DHTML Object Model exposes an extensive set of scriptable events that Web authors can use to customize the behavior of objects on an HTML page. The data binding architecture leverages this model by exposing an additional set of events that are of interest to authors creating data-driven pages. This article discusses the following topics:

  • Events That Apply to the DSO
    • ondatasetchanged
    • ondataavailable
    • ondatasetcomplete
    • onreadystatechange
    • onrowenter
    • onrowexit
    • onrowsdelete
    • onrowsinserted
    • oncellchange
  • Events That Apply to Data Consumers
    • onbeforeupdate and onafterupdate
    • onerrorupdate
  • Events That Apply to the Page
  • Putting It All Together with the Data Binding Event Tracker

Web authors handle these events to maintain a finer grain of control over pages, especially those that support updating. Events occur as the data is loaded and is manipulated by the data source object (DSO), as the user interacts with the data, and as the page unloads. The events allow the Web author to monitor changes in the state of the DSO, to perform field and record-level validation, and to execute cleanup processing as the page is unloaded.

Events That Apply to the DSO

The DSO sends notifications when the data set has changed, as data becomes available, when the data is completely available, and before and after a record supplied by the DSO is committed to the data cache.

ondatasetchanged

The ondatasetchanged event fires under two circumstances:

  • After a new data set is requested.
  • When the current data set has been altered (for example, filtered or sorted).

When the event occurs, data might not be available, but the recordset can be used to obtain the metadata for the data set. Metadata includes the list of fields and their types. Web authors can create truly dynamic pages using metadata.

The following example code adds the list of fields provided by a DSO to a drop-down list, cboSort. When the user selects an item in the drop-down list, the data is sorted by the selected field.

This code handles the ondatasetchanged event for the DSO name DSO Composers.

<SCRIPT FOR=<tla rid="tla_dso"/>Composers EVENT=ondatasetchanged>
    FillSortDropDownWithFields(cboSort, this.recordset);
</SCRIPT>

The following code loops through the collection of fields provided by the ADO recordset and adds each field name to the options collection.

<SCRIPT>
// Add specified value/text to the dropdown list
function AddItemToDropDown(oDropDown, cValue, cText)
{
    oOption = document.createElement('OPTION');
    oOption.value = cValue;
    oOption.text = cText;   
    oDropDown.add(oOption);
}
// Fill dropdown with field names from the ADO RecordSet
function FillSortDropDownWithFields(oDropDown, oRecordSet)
{
// only fill once or let the caller clear list first
    if (oDropDown.options.length > 0)
        return;
    AddItemToDropDown(oDropDown, "None", "None"); // default
    // add each of the columns in the data set to the drop-down
    for (i = 0; i < oRecordSet.fields.count; i++)
    {
        oField = oRecordSet.fields(i);
        AddItemToDropDown(oDropDown, oField.name, oField.name); 
    }
    cboSort.selectedIndex = 0;
}
</SCRIPT>

When the user selects an item from the drop-down list, the following code sets the DSO sort property equal to the appropriate filter, or to the empty string if "None" was selected. Note that while the previous code samples work with any DSO, the following code is specific to the Tabular Data Control (TDC).

<SCRIPT FOR=cboSort EVENT=onchange>
    cValue = this.options[this.selectedIndex].value;
    tdcComposers.object.Sort = (cValue == 'None' ? '' : cValue);
    tdcComposers.Reset();
</SCRIPT>

ondataavailable

DSOs typically fire the ondataavailable event when they provide their data asynchronously, indicating that additional records are accessible. Web authors should not rely upon this event to fire but might instead use it to indicate progress to the user. The event might fire zero or more times.

ondatasetcomplete

The ondatasetcomplete event fires when a DSO has cached all its data on the client. The reason property of the event object indicates success (0), abort (1), or failure (2) of the download. A failure might result from an error or from an explicit user action, such as clicking the Stop button. If reason indicates success, all the data is programmatically accessible through the ADO Recordset object. See the Microsoft Active Data Objects (ADO) for details.

onreadystatechange

While onreadystatechange is not specific to a DSO, understanding the state of an object can be useful. When this event fires, the event-handling code can retrieve the current value of the readyState property. It is typically safe to access the properties of an object when it reaches a ready state of complete.

<SCRIPT FOR=tdcComposer EVENT=onreadystatechange>
if (this.readyState == 'complete')
{
    // perform some action
}
</SCRIPT>

onrowenter

The onrowenter event fires when the current record pointer has changed, for example, through ADO Recordset navigation. Web authors can use this event to preprocess the data in the current record.

onrowexit

The onrowexit event fires before the current record pointer changes. This might occur for the following reasons:

  • Calling a method on the DSO
  • Deleting the current record
  • Leaving the page

The Web author can perform record-level validation prior to moving to the next record. By returning FALSE from this event handler, the Web author can prevent the user from moving to the next record.

The following code performs a simple validation to ensure that the data indicates that the composer's birthday occurred before his death.

<SCRIPT FOR=<tla rid="tla_dso"/>Composers EVENT=onrowexit>
if (txtBorn.value > txtDied.value)
{
    alert("Birth date must be less than or equal to deceased dates");
    return false;
}
</SCRIPT>

onrowsdelete

The onrowsdelete event fires when rows are about to be deleted from the current recordset. Web authors can use this event to view the records that are about to be deleted. The bookmarks collection can be used to loop through the affected records.

onrowsinserted

The onrowsinserted event fires after rows are inserted into the current recordset. Web authors can use this event to set default values of data fields as soon as the record is created. The bookmarks collection can be used to gain access to the row that has been inserted.

oncellchange

The oncellchange event fires whenever data changes in the data provider. The dataFld property can be used to determine which field in the recordset has changed.

Events That Apply to Data Consumers

Bound elements fire events that allow page authors to perform field-level validation and to handle errors that occur during an update.

onbeforeupdate and onafterupdate

The onbeforeupdate event fires when the data in an element has been changed and that element is about to lose the focus. An element loses focus when the user tabs to the next or previous field in the tab order, uses the mouse to select a different control, or unloads the page. The events do not fire if the value of a bound control is set programmatically. Should the validation code associated with the event detect invalid data, an author can return FALSE to prevent the user from leaving the field.

The following code performs some basic validation on the value entered into a text box. If the value is nonnumeric or outside a specified range, the user is alerted and prevented from leaving the field.

<SCRIPT FOR=txtBorn EVENT=onbeforeupdate>
    dToday = new Date();
    fRet = ValidateDate(parseInt(this.value), 0, dToday.getFullYear());
    event.cancelBubble = true;
    return fRet;

// Perform some basic validation on the date
function ValidateDate(nValue, nMin, nMax)
{
    if (isNaN(nValue))
    {
        alert("Year required");
        return false;
    }
    if (nValue < nMin || nValue > nMax)
    {
        alert("Invalid year");
        return false;
    }
    return true;
}</SCRIPT>

Both the original and the modified data can be obtained while handling the onbeforeupdate event because, while the control's value has been updated, the data has not been committed to the DSO. Use the ADO recordset supplied by the DSO to get the original value. Use the appropriate control property to obtain the current value as entered by the user. Here's an example that works for any text box.

<SCRIPT FOR=txtBorn EVENT=onbeforeupdate>
    curValue = this.value;
    origValue = dsoComposers.recordset.fields(this.dataFld).value;
</SCRIPT>

If the onbeforeupdate event is not canceled, onafterupdate fires after data is transferred from the consumer to the data provider.

onerrorupdate

The onerrorupdate event fires when an error occurs while transferring data from the data consumer to the data source object through some user interaction. By canceling this event, any system-provided error dialog boxes are suppressed.

Events That Apply to the Page

In addition to the events specified above for data bound elements and data source controls, the onbeforeunload event allows the Web author to save data changed on the page that has not been committed to the location from which the DSO obtains its data. The following user actions are among those that cause onbeforeunload to fire:

  • Hyperlink navigation
  • Clicking the Forward or Back button
  • Selecting a URL from Favorites
  • Clicking a Submit button
  • Refreshing the page

In addition to these user actions, a script that causes the page to unload will also trigger this event.

window.location.href = "https://www.microsoft.com/ie";

While the onbeforeunload event is not cancelable, a script writer can return a string from the event through the returnValue property. The string is displayed along with an informative message giving the user the opportunity to cancel the navigation. For example, to warn the user of potential data loss, handle the event as follows:

<SCRIPT FOR=window EVENT=onbeforeunload>
    if (g_fFieldsChanged > 0)
        event.returnValue = "Warning: Modified data has not been saved.";
</SCRIPT>

The g_fFieldsChanged flag used in the example indicates that the page has changed. Page authors can track changes to fields by handling the onafterupdate event as follows:

<SCRIPT FOR=document EVENT=onafterupdate>
    g_fFieldsChanged = g_fFieldsChanged + 1
</SCRIPT>

Putting It All Together with the Data Binding Event Tracker

Click the Show Me button to launch a sample that demonstrates many of the events and concepts discussed in this section.

Code example: http://samples.msdn.microsoft.com/workshop/samples/author/databind/dbevts.htm

The sample uses the TDC, so that no data will be committed back to the data file. Data can be modified in the local cache, however. By modifying the values within the elements on the page, navigating through the records using the navigation buttons, and changing the filter and sort order through the drop-down lists, the majority of the events will fire. Observe the events and the order in which they occur as they are logged to the textArea on the right half of the sample page. Clear the event log at any time by clicking the Clear Log button.