The Visual InterDev 6.0 Scripting Object Model Event Sequence

Visual Studio 6.0
 

Steve Millet
Microsoft Corporation

September 1998


Summary: Explains the execution and event sequencing of the Microsoft Visual InterDev version 6.0 scripting object model. (9 printed pages) Includes:

  • Descriptions of the object construction, object initialization, page entry, dispatch, content rendering, page exit, and destruction phases of the scripting object model event sequence.
  • A brief description of the sample .asp files that accompany the article.
  • A code summary of the execution of the scripting object model.

Contents

Introduction
Execution and Eventing Sequence
Samples
Scripting Object Model Execution Model Summary

Introduction

The scripting object model in Microsoft® Visual InterDev® 6.0 provides an event-based execution model for Active Server Pages (ASP). While a scripting object model–based page is executed serially as any other ASP page, the scripting object model provides a framework for programming to an event-based model.

At a high level, the execution and event sequence is similar to the DHTML browser execution sequence. Objects on the page are constructed and initialized. An onenter event is fired to indicate the page is loaded and all objects are ready to be scripted (equivalent to the window onload event in DHTML). Implementing an event handler for the onenter event is ideal for initializing variables and objects. After the onenter event is fired, execution is transferred to an entry point or event handler, by default invoking the show entry point. In many cases, execution will transfer to a server-side handler as a result of a button click or some other user action that takes place in the client browser. An onshow event is fired just prior to executing the content portion of the page. Implementing an event handler for the onshow event is ideal for setting the state of any objects just prior to rendering their HTML output. After the content portion of the page is executed, an onexit event is fired to indicate the page is unloading (equivalent to the window onunload event in DHTML). Finally, all of the objects on the page will be destroyed and page execution is complete.

Execution and Eventing Sequence

Object Construction Phase

Execution begins by constructing each Script object on the page. This is accomplished by executing all Microsoft JScript® functions that conform to the following naming convention: "xxxx_ctor". The design-time controls (DTCs) provided in Visual InterDev 6.0 will generate a _ctor function for the run-time object they represent. The current implementation will execute each _ctor sequentially, in the order it appears on the page, but this behavior should not be assumed. An object constructor should be self-contained and should not expect any other object to have been constructed prior to its constructor.

Construction of the "thisPage" object

The first object constructed will be a scripting object named thisPage. This object provides properties, methods, and events that comprise the scripting object model. Other objects will use the thisPage object for state services, event notification, and other purposes.

Registering an initialization function

The constructor function of each object will register an initialization function with the thisPage object via an advise method. The initialization functions will be invoked during the initialization phase, after all object constructors have been invoked. A pointer to the initialization function is provided as a parameter to the constructor, and the initialization function implementation is generated by a DTC based on the properties set at design-time. A Recordset object will register its initialization function with a higher priority than other objects. This action ensures that the Recordset objects will be initialized before any other objects that may depend on the recordset for databinding.

Registering default event handlers

The constructor function for most objects will register any default event handler for the object. A default event handler is a method that conforms to the naming convention "object_event." The Recordset object will defer registration of its default event handlers until the end of the initialization phase (with the exception of the onbeforeopen event). This action ensures that the default Recordset event handlers will not be fired until all other objects have been both constructed and initialized.

Object Initialization Phase

The initialization functions that were registered in the constructors will be invoked in this phase of the execution. The functions are invoked in priority order, with functions of the same priority being invoked in the order registered.

Recordset initialization

The Recordset initialization functions are registered with a high priority, such that all Recordset objects will be initialized before any other objects. The initialization function of the recordset will set up the connection and command for the recordset based upon settings chosen via the Recordset DTC property pages at design time. If the recordset is set to "autoopen," or if the recordset was left open on the previous roundtrip to this same page, the recordset will automatically be opened in its initialization function. To prevent a recordset from being opened during this initialization phase, the "autoopen" flag should be disabled in the DTC property page, and the recordset should be explicitly closed prior to exiting the page. The thisPage_onexit handler is an ideal place for closing recordsets prior to exiting the page.

Object initialization

Object initialization functions are registered with a neutral priority and will be invoked after high-priority initialization functions, such as those for the recordset. Objects that are set to databind to a recordset will use an advise method of the appropriate recordset to register a handler on the onrowenter event and possibly the onbeforeupdate event. The onrowenter handler will be used to bind data from the recordset into the object. The onbeforeupdate handler will be used to bind data from the object into the recordset.

If the recordset is already open when an object registers an onrowenter handler, that handler will be invoked immediately in order to bind the object to the current row of the recordset.

Following the registration of databinding handlers, the object will restore any state that has been roundtripped back to this page. This means that any data that may have been bound to an open recordset during this initialization phase will be overwritten with the restored state of the object that was roundtripped back to the page. This behavior is required to support the data update scenario, because it ensures that a subsequent update to the recordset will be bound with the data that was returned from the browser and restored into the databound object.

Recordset default event handler registration

The default event handlers for the recordset will not be registered until after all of the other objects on the page have been initialized (with the exception of the onbeforeopen event). This ensures that all other objects, in particular databound objects, have been both constructed and initialized. The programmer will be guaranteed that all objects that need to bind to a recordset have done so before the default event handler is invoked.

Because the registration of the default event handlers has been deferred, several of the default handlers will be invoked immediately if the recordset has already been opened. These default handlers are for the onrowenter, ondatasetchanged, and ondatasetcomplete events. These events are fired when the recordset is opened, but because registration of the default handlers was deferred until after all the objects have been initialized, they will be both registered and invoked at this point.

Page Entry Phase

At this point in the execution, all objects have been constructed and initialized. In addition, all databound objects have registered handlers with a recordset, and the Recordset objects may have already been opened and fired the events associated with opening (onbeforeopen, onrowenter, ondatasetchanged, and ondatasetcomplete).

Now the page will fire the onenter event. The default event handler, thisPage_onenter, will be invoked first, followed by any other handlers that have been registered via an advise with the thisPage object. The onenter event handler is an ideal location for the programmer to provide initialization code for manipulating the objects in the page. While the DTC property page can be used to set up an initial state for each object, it is often useful to dynamically alter the state of an object based upon programmatic conditions and for subsequent roundtrips to the page.

The thisPage object exposes a property that is especially useful when implementing a handler for the onenter event. This is the firstEntered property. The programmer can use the thisPage.firstEntered property to determine whether this page was entered from some other page, or entered as a result of a roundtrip back to itself. This is necessary in determining whether the objects and the page are in an initial state or in a restored state.

Here is an example of a default onenter event handler using the thisPage.firstEntered property:

function thisPage_onenter()
{
   if (!thisPage.firstEntered)
   { // initialize Listbox1 with colors
      Listbox1.addItem('red');
      Listbox1.addItem('blue');
      Listbox1.addItem('yellow');
   }

   // otherwise Listbox1 state already restored
   // do not want to add colors again
}

Dispatch Phase

After the onenter event has been fired, the scripting object model will determine if an entry point has been provided in the QueryString or Request header. If an entry point has been provided, execution flow will be dispatched to that method. The details of how the entry point is defined in the QueryString or Request header is not relevant to the programmer using the scripting object model and Script objects.

From the scripting object model, an entry point may be defined in one of three ways:

  1. As an event handler of a Script object

    An object will map a client event back to an entry point on the server using a default event handler naming convention of object_event()

  2. As a navigation method

    The Page object can be used to export an entry point on the server as a navigation method on the client using the naming convention page.navigate.method()

  3. As a default show method

    When an explicit entry point is not provided in the QueryString or Request header, the programmer may provide a default entry point by implementing a method on the server named show().

    If an entry point is not provided in the QueryString or Request header and the default show entry point is not implemented, the dispatch phase will do nothing.

Content Rendering Phase

Having dispatched and returned from the designated entry point, the page is now ready to be rendered. This is where the traditional content portion of the ASP page will be executed. The programmer may have placed inline script within the content, but more likely the content will contain Script objects that will render themselves based upon the state they have been initialized and programmed to in the prior execution phases.

Just prior to executing the content portion of the ASP, the Page object will fire the onshow event. The default event handler, thisPage_onshow, will be invoked first, followed by any other handlers that have been registered via an advise with the thisPage object. The onshow event provides the programmer one last hook to program the objects on the page before it is rendered. So, this event provides a place to execute code after the entry point has been dispatched to, but before the page has been rendered. After the onshow event is fired, the content portion of the page is executed, rendering the contents of the page.

Page Exit Phase

Having completed the rendering phase, the page will fire the onexit event. The default event handler, thisPage_onexit, will be invoked first, followed by any other handlers that have been registered via an advise with the thisPage object. This allows the programmer to modify Page state or the state of any objects prior to the state persistence and destruction phase. One typical usage is to close any open Recordset objects in the thisPage_onexit handler so they will not be automatically opened on a subsequent roundtrip back to the page. This behavior is often required when doing parameterized queries based on some user preferences sent back from the client.

Destruction Phase

Finally, the execution ends by destroying each object on the page. This is accomplished by executing all JScript functions that conform to the following naming convention: "xxxx_dtor". A DTC may generate a _dtor function for the run-time object it represents. The current implementation will execute each _dtor sequentially, in the order it appears on the page, but this behavior should not be assumed.

The DTC for the Recordset object generates a destructor for bookmarking the current record (if bookmarking options were chosen at design time) and persisting the current state of the recordset. This will allow the recordset to be automatically opened to the current record on a subsequent roundtrip back to the page.

Samples

  1. Simple (simple.asp)

    This is a simple example demonstrating the use of the scripting object model with several Script objects. It does not include a recordset or databinding. It demonstrates use of the page onenter and onshow events, as well as handling an onclick event of a button and an onchange event of a list box.

  2. Parameterized databinding (databind1.asp)

    This example demonstrates the use of a recordset and binding controls to that recordset. In this example, the data in the recordset simply displayed in a table, where paging and navigation through the recordset are not allowed. It does allow a filter to be applied to modify the view on the recordset.

    This is a good example of when to use the onshow event to open a recordset and the onexit event to close a recordset.

  3. Parameterized databinding with navigation (databind2.asp)

    This example is similar to databind1.asp, but supports paging and navigation through the recordset being displayed in the table. It still supports the ability to apply a filter to the recordset. This is a good example of when to leave a recordset open such that it will maintain and automatically reopen the current recordset and bookmark upon a subsequent roundtrip to the page.

  4. Master/Detail databinding (masterdetail.asp)

    This is a more complicated databinding example, where a detail recordset will be parameterized based on a field in the current record of a master recordset. The master recordset supports navigation and paging, while the detail recordset is simply displayed.

Installation Instructions for the Samples

Note   Samples 2, 3, and 4 require the Northwind database (nwind.mdb). This database is included with Microsoft Access and with Microsoft Visual Basic®. To work with these sample .asp pages, nwind.mdb must be available on a server.
  1. Add the four sample .asp files to the root of an existing project or new project.
  2. Add to the project a data connection named NWIND that references a Northwind database.
  3. View sample pages in the browser.

Scripting Object Model Execution Model Summary

Construction phase

if (thisPage.hasState())   // restore state for thisPage
   thisPage.restoreState()
Object_ctor()         // construct all objects in sequence

Initialization phase ( oninit event )

Recordset_init()
{   // recordset objects are initialized first
   if (autoOpen || wasOpen())
   {   // recordset object may be opened during initialization
      Recordset.open()
            {   // opening a recordset fires a sequence of events
               fireEvent(onbeforeopen)
               rsADO.open()
               fireEvent(onrowenter)
               fireEvent(ondatasetchanged)
               fireEvent(ondatasetcomplete)
            }
   }
}

Object_init()   
{   // remaining objects are initialized in sequence       
   if (object is databound)
   {   // databound objects bind to a recordset object
      Recordset.advise(onrowenter)
      if (Recordset.isOpen())
            bind object to current record
   }
   if (hasState())
         restoreState()   // will overwrite bound data
}
      
Recordset_adviseDefaultHandlers()
{      
   Recordset.adviseDefaultHandler(onrowenter)
   {   // if recordset is already open, invoke default handler
      if (Recordset.isOpen())   Recordset_onrowenter()
   }
   Recordset.adviseDefaultHandler(ondatasetchanged)
   {   // if recordset is already open, invoke default handler
      if (Recordset.isOpen())   Recordset_ondatasetchanged()
   }
   Recordset.adviseDefaultHandler(ondatasetcomplete)
   {   // if recordset is already open, invoke default handler
      if (Recordset.isOpen())   Recordset_ondatasetcomplete()
   }
   Recordset.adviseDefaultHandler(onrowexit)
   Recordset.adviseDefaultHandler(onbeforeupdate)
   Recordset.adviseDefaultHandler(onafterupdate)
}      

Page entry phase ( onenter event )

fireEvent(onenter)
{
   thisPage_onenter()      // default onenter invoked
   // invoke any other handlers that advised for onenter event
}

Dispatch phase

if (entry point for ObjectEvent)
   fireEvent(object_event)
else if (entry point for navigate method)
   invoke thisPage.navigate.method
else if (typeof(show) == 'function')
   invoke thisPage.navigate.show

Content rendering phase ( onshow event )

fireEvent(onshow)
{
   thisPage_onshow()      // default onshow invoked
   // invoke any other handlers that advised for onshow event
}
//serially execute content portion of page

Page exit phase ( onexit event )

fireEvent(onexit)
{
   thisPage_onexit()      // default onexit invoked
   // invoke any other handlers that advised for onexit event
}

Destruction phase

Object_dtor()      
{   // destruct all objects in sequence
   if (maintainState == true)
         preserveState()   // preserve state of object
}
thisPage.preserveState()   // preserve state of thisPage

Recordset Event Model Summary

Recordset.open()
{   // opening a recordset fires a sequence of events
      fireEvent(onbeforeopen)
      rsADO.open()      // open ADO recordset
      fireEvent(onrowenter)
      fireEvent(ondatasetchange)
      fireEvent(ondatasetcomplete)
}

Notes

  1. The Recordset_onbeforeopen() handler is an ideal place to centralize the setting of the SQL text just prior to opening the ADO recordset.
  2. The Recordset_DEFAULT handlers will not be registered until AFTER all other objects have been initialized, with the exception of the Recordset_onbeforeopen handler.
  3. Databound objects advise to the Recordset onrowenter event to bind the data from the recordset into the control.
  4. Databound objects advise to the Recordset onbeforeupdate event to bind data from the control into the recordset.
  5. The default Recordset_onrowenter handler will be invoked AFTER all databound objects have been bound.
  6. The default Recordset_onbeforeupdate handler will be invoked AFTER all databound objects have been bound. This is an ideal place to do validation logic and possibly cancel an update.
Show: