11.4 Enhancing the RecordSet Class for Interactivity


Chapter 3 showed an enhancement to the RecordSet class that facilitated a user interface showing only one record at a time ”a common way of displaying resultset data to the end user . The enhancement added the concept of a current record and provided methods to move to specific records (first, previous, next , last, and record number). To augment this functionality, let's implement a feature to associate a field in a recordset with user interface controls and other elements ( DataGlue style).

11.4.1 The Current Record Functionality

The current record functionality was described in Chapter 3, but we'll add a few new methods to implement the gluing of components to individual RecordSet fields. The basic functionality that we will begin with is shown in Example 11-7.

Example 11-7. Adding current record functionality to a RecordSet
 // Initialize the current record number RecordSet.prototype.currentRecord = 0; // Return the current record RecordSet.prototype.getCurrentRecord = function ( ) {   return this.getItemAt(this.currentRecord-1); }; // Return the current record number RecordSet.prototype.getCurrentRecordNum = function ( ) {   return this.currentRecord }; RecordSet.prototype.move = function (direction) {   switch (direction.toLowerCase( )) {     case "first":       this.currentRecord = 1;       break;     case "previous":       if (--this.currentRecord < 1) this.currentRecord = 1;       break;     case "next":       if (++this.currentRecord > this.getLength( ))         this.currentRecord = this.getLength( );       break;     case "last":       this.currentRecord = this.getLength( );       break;     default:       // Not a direction: must be a number       this.currentRecord = direction;   }  this.recordChanged( );  }; 

The code is identical to what was shown in Chapter 3 under Section 3.6.3, with one exception: we added a call to this.recordChanged( ) in the move( ) method. The recordChanged( ) method will be described in the next few sections.

Remember, the currentRecord property contains numbers from 1 to the length of the recordset. However, recordsets use a zero-relative index, so we'll add or replace records based on the currentRecord - 1 .

11.4.2 Adding the glue( ) and recordChanged( ) Functionality

DataGlue effectively binds a RecordSet object to a ComboBox or a ListBox. In those cases, you are populating the component with the entire recordset (or specific fields of a recordset). This is possible because ComboBoxes and ListBoxes display items in rows. But what about components or objects that don't support multiple individual items, such as a CheckBox or a text field? These types of objects come in handy when you're displaying only one record from a resultset. The recordChanged( ) method that we will create will change the components that are bound to the RecordSet object, but first we have to bind the fields. We'll add another custom method to the RecordSet class, called glue( ) . This is the method that effectively binds a recordset field to a component or text field. The glue( ) method is shown in Example 11-8, along with the uiFields property that holds the fields to be glued from the RecordSet .

Example 11-8. The glue( ) method binds the component to the field
 // Set up an array of UI components to bind to fields RecordSet.prototype.uiFields = new Array( ); // The glue( ) method binds a control of   controlType   to a field //   controlTypes   supported:   //  "text"   //  "combobox"   //  "checkbox"   //  "radiobutton" RecordSet.prototype.glue = function (control, field, controlType) {   // Create the   uiField   member as an object   var controlObj = {};   controlObj.control = control;   controlObj.field = field;   controlObj.controlType = controlType;   // Replace the field if it is already defined.   for (var i=0; i<this.uiFields.length; i++) {     if (this.uiFields[i].control == controlObj.control) {       uiFields[i] = controlObj;       return;     }   }   // If the field is not bound yet, add it to the array   this.uiFields.push(controlObj); }; 

You call the glue( ) method like this, passing the component (or text field), the name of the database field you want to glue to the component, and the component type (" text ", " combobox ", or " checkbox "):

   RecordSetname   .glue(   component   ,   databaseField   ,   componentType   ); 

Typical calls to glue( ) look like this:

 // Glue the   CategoryID   field to the   categories_cb   ComboBox. Products_rs.glue(categories_cb, "CategoryID", "combobox"); // Glue the   ProductName   field to the   ProductName_txt   TextField. Products_rs.glue(ProductName_txt, "ProductName", "text"); 

The glue( ) method is one piece of the puzzle. After the field is glued to the component, you have to be able to update the component in the UI. This is handled by a recordChanged( ) method, shown in Example 11-9.

Example 11-9. The recordChanged( ) method
 RecordSet.prototype.recordChanged = function ( ) {   // Define variables to hold current field, component, and type of component   var theField, theControl, theControlType;   // The current record to be changed   var record = this.getCurrentRecord( );   // The   uiFields   property is an array of   controlObj   objects set up in   glue( )   var tempLength = this.uiFields.length;   for (var i=0;i < tempLength; i++) {     theField = this.uiFields[i].field;     theControl = this.uiFields[i].control;     theControlType = this.uiFields[i].controlType;     switch (theControlType) {     // What kind of control is it?       case "text":                // Text fields have the   text   property set         theControl.text = record[theField];         break;       case "combobox":            // ComboBoxes use the custom   pickValue( )   method         theControl.pickValue(record[theField]);         break;       case "checkbox":            // CheckBoxes have the value set   true   or   false   theControl.setValue(record[theField]);         break;       default:                    // Other components not supported at this time         trace(theControlType + " not supported")     }   } }; 

The recordChanged( ) method cycles through the uiFields array, updating each UI component to display the data that is in the glued field of the current record. The methods have been implemented for TextFields, ComboBoxes, and CheckBoxes, but you can add functionality for any type of UI component.

Now, components and text fields can be glued to a field in a recordset, and the field will change when the current record changes. Next, the setCurrentRecord( ) method will update the current record in the recordset, based on what is in the fields that are glued to it.

11.4.3 The setCurrentRecord( ) Method

The UI is now capable of being updated as the user pages through the recordset. But TextFields, ComboBoxes, and other UI components might be changed by the user as well. We need a method to update the current displayed record directly from the fields that are glued in the recordset. The setCurrentRecord( ) method, shown in Example 11-10, accomplishes this.

Example 11-10. The setCurrentRecord( ) method updates the recordset
 // Update the current record based on values in the glued components RecordSet.prototype.setCurrentRecord = function ( ) {   // Define variables to hold the current field, component, and type of component   var theField, theControl, theControlType;   // The current record to be changed   var record = this.getCurrentRecord( );   // The   uiFields   property is an array of   controlObj   objects set up in   glue( )   var tempLength = this.uiFields.length;   for (var i=0;i < tempLength; i++) {     theField = this.uiFields[i].field;     theControl = this.uiFields[i].control;     theControlType = this.uiFields[i].controlType;     switch (theControlType) { // What kind of control is it?       case "text":            // The TextField uses the text property         record[theField] = theControl.text;         break;       case "combobox": /      // ComboBoxes use the data value of the selected item         record[theField] = theControl.getSelectedItem( ).data;         break;       case "checkbox":        // CheckBoxes use the value   true   or   false   record[theField] = theControl.getValue( );         break;       default:               // Other components not supported at this time         trace(theControlType + " not supported")     }   } }; 

The setCurrentRecord( ) method operates in place on each component or TextField that is glued to the recordset. Whatever is currently displayed in the UI is written to the client-side recordset that the UI component or text field is glued to.

11.4.4 Putting It Together

With the glue( ) functionality in place, you can now simplify the process of building a rich interface. The ProductsAdmin.fla code from Example 5-14 can be simplified, including using a checkbox to display the Discontinued status of the product. The interface fields that are glued include TextFields, ComboBoxes, and a CheckBox.

The administrative interface that is bound to a recordset is shown in Figure 11-4.

Figure 11-4. The administrative interface for the Products table of Northwind
figs/frdg_1104.gif

The code is shown in Example 11-11. The completed .fla file showing the interface can be downloaded from the online Code Depot.

Example 11-11. Using the glue( ) functionality simplifies the ActionScript code
 #include "NetServices.as" #include "DataGlue.as" #include "NetDebug.as" #include "com/oreilly/frdg/DataFriendlyCombo.as" #include "com/oreilly/frdg/RecordSetPlus.as" // General error handler for authoring environment function errorHandler(error) {   trace(error.description); } // Responder objects //   SearchResult( )   takes one optional argument:   recNum   // When inserting or updating a record, the   recNum   can be specified // to move the user interface to that record; otherwise, use   "first"   function SearchResult(recNum) {   if (recNum) {     this.recNum = recNum;   } else {     this.recNum = "first";   } } // The   SearchResult   responder object handles the gluing of the UI SearchResult.prototype.onResult = function (result_rs) {   Products_rs = result_rs;   // Use the   glue( )   method to bind the UI to the recordset   Products_rs.glue(ProductName_txt, "ProductName", "text");   Products_rs.glue(categories_cb, "CategoryID", "combobox");   Products_rs.glue(UnitPrice_txt, "UnitPrice", "text");   Products_rs.glue(QuantityPerUnit_txt, "QuantityPerUnit", "text");   Products_rs.glue(suppliers_cb, "SupplierID", "combobox");   Products_rs.glue(test_ch, "Discontinued", "checkbox");   results_txt.text = "There were " + Products_rs.getLength( )+ " records returned.";   Products_rs.move(this.recNum); }; SearchResult.prototype.onStatus = errorHandler; // Set up a responder object to handle recordsets for ComboBoxes // This responder assumes that data is coming in with // ID column in [0] position and description column // in the [1] position function ComboBoxResponder (cbName) {   this.onResult = function (result_rs) {     var fields = result_rs.getColumnNames( );     var idField = '#' + fields[0] + '#';     var descField = '#' + fields[1] + '#';     DataGlue.bindFormatStrings(cbName, result_rs, descField,idField);     cbName.setDescriptor("--Choose One--", 0);     cbName.setDefaultValue(0);   };   this.onStatus = errorHandler; } // Main responder for the   Update   ,   Insert   , and   Delete   functions // Display is to the Output window only function MainServiceResponder( ) {} MainServiceResponder.prototype.onResult = function (result) {   trace(result); }; MainServiceResponder.prototype.onStatus = errorHandler; // Initialization code if (connected == null) {   connected = true;   NetServices.setDefaultGatewayUrl("http://localhost/flashservices/gateway");   var my_conn = NetServices.createGatewayConnection( );   my_conn.onStatus = errorHandler;   var myService = my_conn.getService("com.oreilly.frdg.admin.ProductsAdmin");   var Products_rs = null;     // Main   RecordSet   object for product list   // Set up the two ComboBoxes   myService.getCategories(new ComboBoxResponder(categories_cb));   myService.getSuppliers(new ComboBoxResponder(suppliers_cb)); } // Set up event handlers for buttons submit_pb.setClickHandler("searchProducts"); // Move buttons moveFirst.setClickHandler("moveTo"); movePrevious.setClickHandler("moveTo"); moveNext.setClickHandler("moveTo"); moveLast.setClickHandler("moveTo"); // Insert, Update, and Delete buttons insert_pb.setClickHandler("insertRecord"); update_pb.setClickHandler("updateRecord"); delete_pb.setClickHandler("deleteRecord"); // Event handlers for buttons //   submit_pb   click handler function searchProducts ( ) {   getRecordset( ); } //   moveFirst( )   ,   movePrevious( )   ,   moveNext( )   , and   moveLast( )   click handler function moveTo (button) {   // The label of the button indicates the direction to move the recordset:   // "first", "previous", "next", "last"   Products_rs.move(button.label);   navStatus_txt.text =       "Rec. No. " + (Products_rs.getCurrentRecordNum( )) + " of " +       Products_rs.getLength( ); } //   update_pb   click handler function updateRecord ( ) {   myService.updateProduct(new MainServiceResponder( ), getUpdatedRecord( ));   var tempRec = Products_rs.getCurrentRecordNum( );   getRecordset(tempRec); } //   insert_pb   click handler function insertRecord ( ) {   if (insert_pb.getLabel( ) == "Add New Product") {     Products_rs.addItem(getNewRecord( ));   Products_rs.move("last");     insert_pb.setLabel("Insert To Database");     insert_txt.text = "Click again to insert to database";   } else {     insert_pb.setLabel("Add New Product");     myService.addProduct(new MainServiceResponder( ), getUpdatedRecord( ));     getRecordset("last");     insert_txt.text = "";   } } //   delete_pb   click handler function deleteRecord ( ) {   var productID = Products_rs.getCurrentRecord( ).ProductID;   myService.deleteProducts(new MainServiceResponder( ), ProductID);   getRecordset( ); } // Utility functions function getRecordset (recNum) {   // Call the remote method using a responder object with optional record number   //   to move the recordset to   myService.getSearchResult(new SearchResult(recNum), search); } // Pack the updated record from the display into the   RecordSet   object // and return the record to the caller function getUpdatedRecord ( ) {   Products_rs.setCurrentRecord( );   return Products_rs.getCurrentRecord( ); } // Get a blank record function getNewRecord ( ) {   var theRecord = { ProductName:''                    ,UnitPrice:''                    ,QuantityPerUnit:''                    ,CategoryID:0                    ,SupplierID:0                    ,ProductID:''                   };   return theRecord; } 

In Example 11-11, the onResult( ) method updates the UI as the record changes and updates the client-side recordset if the user changes the UI:

 // Use the   glue( )   method to bind the UI to the recordset   Products_rs.glue(ProductName_txt, "ProductName", "text");   Products_rs.glue(categories_cb, "CategoryID", "combobox");   Products_rs.glue(UnitPrice_txt, "UnitPrice", "text");   Products_rs.glue(QuantityPerUnit_txt, "QuantityPerUnit", "text");   Products_rs.glue(suppliers_cb, "SupplierID", "combobox");   Products_rs.glue(test_ch, "Discontinued", "checkbox"); 


Flash Remoting
Flash Remoting: The Definitive Guide
ISBN: 059600401X
EAN: 2147483647
Year: 2003
Pages: 239
Authors: Tom Muck

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net