• home
  • forum
  • my
  • kt
  • download
  • Business Services Components

    Author: 2007-08-27 15:56:36 From:

    As mentioned in tutorial 10, business services components encapsulate the business rules of a system. Since there are many different types of business rules, there are also many different types of business services components. When we work with XML, the two types of business rules we'll be most concerned with are the rules that validate user input in user services components and the rules that define how XML data will be returned to the client from the server. These two types of rules require two completely different types of business services components.

    The components that define how XML data is returned to the client run on a server machine. In a Web application, these components usually run on the Web server. Using the DOM objects, we can create and retrieve data in either XML format or in other formats that can be easily transformed to XML. The business services components can be called by an Active Server Page (ASP) that can then return the data to the client. For example, you could create a Microsoft Visual Basic business services component that retrieves data from a database, manipulates the data and converts it to an XML data island, builds the appropriate DHTML script, and embeds the XML data in the DHTML script. This Visual Basic component can then be used by an ASP to return the data to the client. Because we have already discussed the DOM at great length in tutorial 11, we will not discuss these types of business services components here.

    The business services components that we will discuss in this tutorial are the components that validate the user's input. This type of component should be placed on the client whenever possible so that the user's input can be validated immediately. As we mentioned in tutorial 10, if these business services components are placed on the server, a delay in the application can occur every time a request is sent to the server for validation. These delays can affect performance.

    When you build a standard nonbrowser based application, it can be relatively easy to create business services components that validate the user's input. For example, you can create a wide range of components in Visual Basic that can perform the validation. Visual Basic has a special type of object called a data source class that can be used to create objects that automatically bind to objects on a form such as text boxes and grid controls. If you are working with a Web-based application, you could also use programming languages such as Visual Basic to build business services components, but there is another option. You could use HTML Components (HTC) to create an application that is completely Web based. At the moment, HTCs are supported only by Internet Explorer 5, but it's likely that in the future they will be supported by other browsers in different environments. An HTC can also be used to write code for the events that are raised by the business services components in the browser. Since these events are raised by the user's input, an HTC can be ideal for validating the user's input. Let's look at how to build business services components using the HTC.

    If you know that the client machines you'll be working with have Internet Explorer 5 installed on them, you will be able to build extremely powerful Web applications for these client machines using HTC and DHTML. The example code we created in Chapter 13 performed only a few simple tasks, but the DHTML code in these examples was becoming fairly long and complex. If we want to add more functionality to our Web-based applications without making the code too long and complicated, we need to remove the DHTML code from the HTML page and place it in a separate component that can be referenced by an HTML page. This removal also makes the HTML document easier to read and update.

    Let's take a look at how to extend the capabilities of our Web-based application from Chapter 13 so that it allows the user to edit and review the XML data. If we allow the user to change the values of the XML data, the new values will need to be validated. If the data is validated on the server, this validation can be done as each field is typed in or after all the data has been input. Performing server-side validation as data is input often creates time delays on the client as each field is validated. On the other hand, sending data to the server when all the information is input makes it difficult for the user to find the exact fields that were incorrect. The best solution is to create business services components that run on the client that can validate the data after it has been entered.

    NOTE
    If you validate data on the client, you would still have to validate the data on the server a second time for security reasons, as a malicious hacker could submit invalid data.

    HTC enables Web developers to implement behaviors that expose properties, methods, and custom events. As you will see in the example later in this chapter, properties placed inside an HTC will allow us to perform validation whenever the value of a property is changed.

    Using an HTC for our validation code offers us several advantages. To begin with, the code we will use to validate a particular XML document could be reused in every Web application that uses XML based on the same schema or DTD. Also, the HTC will be located on the client, so this validation will not require any trips to the server. Finally, if the validation rules change, only the HTC needs to be changed, not the HTML application that uses the HTC.

    Before we create our example, let's look at the different elements that can be used in an HTC document. These elements are listed in the following table:

    HTC Elements

    ElementDescription
    COMPONENTDefines the document as being an HTC document; is the root element of an HTC document (HTC can also be used.)
    ATTACHAttaches an event in the source document to a function in the component
    METHODDefines a method that can be called by the source document
    EVENTDefines an event that can be raised by the HTC
    PROPERTYDefines a property that can be accessed by the source document
    GETDefines the function that will be called when retrieving the value of a property
    PARAMETERDefines a parameter for a function
    PUTDefines the function that will be called when setting the value of a property

    The example we will create saves the values of each of the fields in properties in an HTC. The put functions associated with the properties in the HTC will validate each new value. Thus, we will use the HTC to validate any changes to a field. If the change is invalid, we will reset the value of the field back to its original value by using the value that is currently in the property. We'll prefix the elements with the public namespace to identify the properties in the HTC as public properties. Public properties can be get or put by external documents. Let's begin by creating a document called ValidatePO.htc that includes the following code:


      <public:COMPONENT>
         <public:PROPERTY NAME="PartNo"  PUT="putPartNo" 
         GET="getPartNo"/>
         <public:PROPERTY NAME="Quantity"  PUT="putQuantity" 
         GET="getQuantity"/>
         <public:PROPERTY NAME="UOM"  PUT="putUOM" GET="getUOM"/>
         <public:PROPERTY NAME="UnitPrice"  PUT="putUnitPrice"
         GET="getUnitPrice"/>
         <public:PROPERTY NAME="Discount"  PUT="putDiscount"
         GET="getDiscount"/>
         <public:PROPERTY NAME="Total"  PUT="putTotal" GET="getTotal"/>
         <public:PROPERTY NAME="Error"  PUT="putError" GET="getError"/>
      <script language="VBScript">
         Dim strPartNo
         Dim strQuantity
         Dim strUOM
         Dim strDiscount
         Dim strTotal
         Dim strUnitPrice
         Dim strError
    
         Function putPartNo (newValue)
            If newValue = "" Then
               strError = "The part number is invalid."
            Else
               strPartNo = newValue
            End If
         End function
         Function putQuantity (newValue)
            If newValue = "" Then
               strError = "The quantity is invalid."
            Else
               If IsNumeric(newValue ) Then
                  strQuantity = newValue
               Else
                  strError = "The quantity must be numeric."
               End If 
            End If
         End function
         Function putUnitPrice (newValue)
            If newValue = "" Then
               strError = "The unit price is invalid."
            Else
               If IsNumeric(newValue) Then
                  strUnitPrice = newValue
               Else
                  strError = "The unit price must be numeric."
               End If
            End If
         End function
         Function putUOM (newValue)
            If newValue = "" Then
               strError = "The UOM is invalid."
            Else
               If (LCase(newValue) = "each") or _
                  (LCase(newValue) = "case") Then
                  strUOM = newValue
               Else
                  strError = "The UOM can only be each or case."
               End If
            End If
         End function
         Function putDiscount (newValue)
            If newValue = "" Then 
               strError = "The discount is invalid."
            Else
               If IsNumeric (newValue) Then
                  strDiscount = newValue
               Else
                  strError = "The discount must be numeric."
               End If
            End If
         End function
         Function putTotal (newValue)
            If newValue = "" Then
               strError = "The total is invalid."
            Else
               If IsNumeric (newValue) Then
                  strTotal = newValue
               Else
                  StrError = "The total must be numeric."
               End If
            End If
         End function
         Function putError (newValue)
            StrError = newValue
         End function
    
         Function getPartNo ()
            getPartNo = strPartNo
         End function
         Function getQuantity ()
            getQuantity = strQuantity
         End function
         Function getUOM ()
            getUOM = strUOM 
         End function
         Function getDiscount ()
            getDiscount = strDiscount
         End function
         Function getTotal ()
            getTotal = strTotal
         End function
         Function getUnitPrice ()
            getUnitPrice = strUnitPrice
         End function
         Function getError ()
            getError = strError
         End function
      </script>
      </public:COMPONENT>
      

    As you can see, public properties are used in the document. The advantage of using properties is they allow us to validate values when a user attempts to change the value of the property. If the value is invalid, we do not allow the property's value to change and raise an error. In the case of the HTC above, we do not actually raise an error but instead set the error property to a string that explains the error.

    The put functions in this code are used to check that the new values are valid. If they are not valid, the functions set strError to a value that explains the error. If the value is valid, the functions set a private variable to the value. Each put function is associated with the PUT attribute of the corresponding property. For example, the putPartNo function is associated with the PUT attribute of the PartNo property. When you assign a value to the PartNo property, the function putPartNo is called.

    The get functions will be used when a certain value is set equal to the property. For example, when you use the partNo property, the getPartNo function will be called. PartNo will return the private variable. Although we do not perform any validation in the get functions, you can add validation to these functions, too.

    In the source document that will use the HTC, we will not use data binding for our text boxes. Instead, we will use DHTML to give us full control of the form as we did in the example in >Chapter 13. We will write script to fill the text boxes with user input, to move through the records, and to set and get the properties in the HTC document. Here is the first part of the HTML document, showing the FillText function and the ondatasetcomplete event:


      <html>
      <head>
      <xml src="NorthwindPO2.xml" id="NorthwindDSO"></xml>
      <style type="text/css">
         .FieldName  {font-family:Arial,sans-serif; font-size:12px;
                      font-weight:normal}
         .POValidate {behavior:url (ValidatePO.htc);}
      </style>
      <script language="JScript">
      function FillText()
         {
         txtPartNo.value=NorthwindDSO.recordset.fields("partno").value;
         txtQuantity.value=NorthwindDSO.recordset.fields("qty").value;
         txtUOM.value=NorthwindDSO.recordset.fields("uom").value;
         txtDiscount.value=NorthwindDSO.recordset.fields
            ("discount").value;
         txtUnitPrice.value=NorthwindDSO.recordset.fields
            ("unitPrice").value;
         txtTotal.value=NorthwindDSO.recordset.fields
            ("totalAmount").value;
         }
    
      function NorthwindDSO.ondatasetcomplete()
         {   
         htcSpan.PartNo=NorthwindDSO.recordset.fields("partno").value;
         if (!htcSpan.Error=="")
            {
            alert(htcSpan.Error);
            }
         htcSpan.Quantity=NorthwindDSO.recordset.fields("qty").value;
         if (!htcSpan.Error=="")
            {
            alert(htcSpan.Error);
            }
         htcSpan.UOM=NorthwindDSO.recordset.fields("uom").value;
         if (!htcSpan.Error=="")
            {
            alert(htcSpan.Error);
            }
         htcSpan.UnitPrice=NorthwindDSO.recordset.fields
            ("unitPrice").value;
         if (!htcSpan.Error=="")
            {
            alert(htcSpan.Error);
            }
         htcSpan.Discount=NorthwindDSO.recordset.fields
            ("discount").value;
         if (!htcSpan.Error=="")
            {
            alert(htcSpan.Error);
            }
         htcSpan.Total=NorthwindDSO.recordset.fields
            ("totalAmount").value;
         if (!htcSpan.Error=="")
            {
            alert(htcSpan.Error);
            }
            htcSpan.Error="";
            FillText();
         }
      

    The FillText function is used to place the values of the recordset into the textboxes. It is called when the data has been loaded and when the user moves to another row. The DSO object raises the ondatasetcomplete event when all the data has arrived on the client. When this event is raised, we first set all the properties equal to the field values. Because we are setting the properties, the put functions in the HTC are used. As mentioned previously, the put functions also validate the new values. If there is an error, the property isn't set to the new value and the user gets an error message. This code doesn't fix the error, it only alerts the user to the problem.

    To use the HTC document, we need to bind it to a tag on the form. We use the span tag called htcSpan to bind the HTC document. The htcSpan is bound by setting its class attribute to POValidate, the style linked to the HTC document. Once the span tag is bound to the HTC document, it inherits the properties and methods contained in the HTC document. Thus, the properties can be referenced by using the name for the tag followed by a dot followed by the name of the property. For example, htcSpan.PartNo refers to the PartNo property in the document.

    In the second part of the HTML document, we'll use functions that will be associated with the onBlur event of all the text boxes. When the user tabs out of a text box, the onBlur event will be called. If the value in the text box is different from the value of the field in the recordset, the user has changed the value. If the value has been changed, we will set the field to this new value. Changing the value of the field will result in the oncellchange event being raised by the DSO object. We will validate the change in the oncellchange event.


      function partNoChange()
         {
         if(NorthwindDSO.recordset.fields("partno").value!=
            txtPartNo.value)
            {
            NorthwindDSO.recordset.fields("partno").value=
               txtPartNo.value;
            }
         }
      function QuantityChange()
         {
         if (NorthwindDSO.recordset.fields("qty").value!=
            txtQuantity.value)
            { 
            NorthwindDSO.recordset.fields("qty").value=
               txtQuantity.value;
            }
         }
      function UOMChange()
         {
         if (NorthwindDSO.recordset.fields("uom").value!=txtUOM.value)
            {
            NorthwindDSO.recordset.fields("uom").value=txtUOM.value;
            }
         }
      function UnitPriceChange()
         {
         if (NorthwindDSO.recordset.fields("unitPrice").value!=
            txtUnitPrice.value)
            {
            NorthwindDSO.recordset.fields("unitPrice").value=
               txtUnitPrice.value;
            }
         }
      function DiscountChange()
         {
         if (NorthwindDSO.recordset.fields("discount").value!=
            txtDiscount.value)
            {
            NorthwindDSO.recordset.fields("discount").value=
               txtDiscount.value;
            }
         }
      function TotalChange()
         {
         if (NorthwindDSO.recordset.fields("totalAmount").value!=
            txtTotal.value)
            {
            NorthwindDSO.recordset.fields("totalAmount").value=
               txtTotal.value;
            }
         }
      

    The third part of the HTML document shows how to use the oncellchange event that is fired whenever a field is changed. We will use the dataFld property of the event object to get the name of the field that has changed. Using a switch statement, we will find which field has been changed and set the property for that field equal to the new value. If the change is valid, the property will be set to the new value. If the value is not valid, the property will not be changed and the error property will be set to a string describing the error. If the change is invalid, we will also reset the value in the text box to the original value that is still stored in the property and then use the select method of the text box to move focus back to the field that was in error.


      function NorthwindDSO.oncellchange()
         {
         switch (event.dataFld)
            {
            case "partno":
               htcSpan.PartNo=txtPartNo.value;
               if (!htcSpan.Error=="")
                  {
                  txtPartNo.value=htcSpan.PartNo;
                  txtPartNo.select();
                  }
            break;
            case "qty":
               htcSpan.Quantity=txtQuantity.value;
               if (!htcSpan.Error=="")
                  {
                  txtQuantity.value=htcSpan.Quantity;
                  txtQuantity.select();
                  }
            break;          
            case "uom":
               htcSpan.UOM=txtUOM.value;
               if (!htcSpan.Error=="")
                  {
                  txtUOM.value=htcSpan.UOM;
                  txtUOM.select();
                  }
            break;
            case "unitPrice":
               htcSpan.UnitPrice=txtUnitPrice.value;
               if (!htcSpan.Error=="")
                  {
                  txtUnitPrice.value=htcSpan.UnitPrice;
                  txtUnitPrice.select();
                  }
            break;
            case "discount":
               htcSpan.Discount=txtDiscount.value;
               if (!htcSpan.Error=="")
                  {
                  txtDiscount.value=htcSpan.Discount;
                  txtDiscount.select();
                  }
            break;
            case "totalAmount":
               htcSpan.Total=txtTotal.value;
               if (!htcSpan.Error=="")
                  {
                  txtTotal.value=htcSpan.Total;
                  txtTotal.select();
                  }
                   break;
            default:
               htcSpan.Error = "Invalid element text";
            }
            if (!htcSpan.Error=="")
               {
               alert (htcSpan.Error);
               htcSpan.Error="";
               }
         }
      

    The last part of the HTML document shows the move functions and the rest of the HTML code. We will code the move functions using JScript instead of using VBScript as we did in Chapter 13 so that we can see how to code in both scripting languages. First the DSO object is moved, and then the FillText function is called to set the values in the text boxes.


      function MoveNext()
         {
         NorthwindDSO.recordset.moveNext();
         if (NorthwindDSO.recordset.eof)
            {
            NorthwindDSO.recordset.moveFirst();
            }
         FillText();
         }
      function MovePrevious()
         {
         NorthwindDSO.recordset.movePrevious();
         if (NorthwindDSO.recordset.bof)
            {
            NorthwindDSO.recordset.MoveLast();
            }
         FillText();
         }
      function MoveLast()
         {
         if (!NorthwindDSO.recordset.eof && !NorthwindDSO.recordset.bof)
            {
            NorthwindDSO.recordset.moveLast();
            FillText();
            }
         }
      function MoveFirst()
         {
         if (!NorthwindDSO.recordset.eof && !NorthwindDSO.recordset.bof)
            {
            NorthwindDSO.recordset.moveFirst();
            FillText();
            }
         }
      </script>
      </head>
      <body>
         <!--This is the span element that gives the reference to the
            HTC component.-->
            <span id="htcSpan" class="POValidate"> </span>
            <!--We place the values in a table to make them look
               neater.-->
            <table  cellpadding="5">
            <tr>
               <td>
               <div class="FieldName">Part No</div>
               </td>
               <td>
               <!--The onBlur event is associated with the partNoChange
                  listed above.-->
               <div class="FieldName"><input type="text" id="txtPartNo"
                  onBlur="partNoChange()"></div>
               </td>
            </tr>
            <tr>
               <td>
               <div class="FieldName">Quantity</div>
               </td>
               <td>
               <div class="FieldName"><input id=txtQuantity 
                  name=txtQuantity onBlur="QuantityChange()"></div>
               </td>
            </tr>
            <tr>
               <td>
               <div class="FieldName">UOM</div>
               </td>
               <td>
               <div class="FieldName"><input id=txtUOM name=txtUOM onBlur="UOMChange()"></div>
               </td>
            </tr>
            <tr>
               <td>
               <div class="FieldName">Unit Price</div>
               </td>
               <td>
               <div class="FieldName"><input id=txtUnitPrice 
                  name=txtUnitPrice onBlur="UnitPriceChange()"></div>
               </td>
            </tr>
            <tr>
               <td>
               <div class="FieldName">Discount</div>
               </td>
               <td>      
               <div class="FieldName"><input id=txtDiscount 
                  name=txtDiscount onBlur="DiscountChange()"></div>
               </td>
            </tr>
            <tr>
               <td>
               <div class="FieldName">Total</div>
               </td>
               <td>
               <div class="FieldName"><input id=txtTotal name=txtTotal
                  onBlur="TotalChange()"></div>
               </td>
            </tr>
            </table>
            <!--The buttons are also placed in a table so that
               they appear neater in the document.-->
            <table>
            <td><input id=cmdMoveNext name=cmdMoveNext type=button
               value="Move Next" onClick="MoveNext()"></input></td>
            <td><input id=cmdMovePrevious name=cmdMovePrevious 
               type=button value="Move Previous"
               onClick="MovePrevious()"></input></td>
            <td><input id=cmdMoveFirst name=cmdMoveFirst type=button
               value="Move First" onClick="MoveFirst()"></input></td>
            <td><input id=cmdMoveLast name=cmdMoveNext type=button
               value="Move Last" onClick="MoveLast()"></input></td>
            </table>
      </body>
      </html>
      

    This example will prompt the user if he or she types in a new value that is invalid. If we used this example in a real application, we would need some way to submit the changes back to the server, where the new XML data is processed. As you can see, creating a fully working application using a browser interface is still a good deal of work.

    We could have also used a method in our example to validate the fields. To do this, we could have added the following method to the HTC document:


      <public:METHOD NAME="CheckValidity">
         <PARAMETER name="FieldName" />
         <PARAMETER name="NewValue" />
      </public:METHOD>
      

    The CheckValidity function written in VBScript would look as follows:


      Function CheckValidity (FieldName, NewValue)
         Select Case FieldName 
         Case "PartNo"
            If NewValue = "" Then
               strError = "The PartNo is invalid."
            End If
         Case "Quantity"
            If Not IsNumeric (NewValue) Then
               strError = "The Quantity is invalid."
            End If
            End Select
      End Function
      

    You would have to use the select case statement to test the FieldName and verify whether the NewValue is valid for that field. You can write the complete code for this function as an exercise.

    In addition to writing business services components that use HTC, we could write business services components in Visual Basic, Java, or C++ and create compiled objects that are used by the HTML page. (It is also possible to create a compiled HTC, but this can only be done with C++, and the process is extremely complex.)

    For example, you could create a business services component using a Visual Basic object that uses the DOM object to work with the XML data. We will not demonstrate how to do this here as the code we used to work with the DOM in Chapter 11 can be used to make either client or server components.

    A compiled business services component might run more quickly than an HTC, but a compiled component might need to be installed on the client computer. In a controlled environment where you can be certain that a compiled component will easily install on every client, a compiled component may be the better choice. In an environment in which every client has Internet Explorer 5 installed, but there are many different types of clients, it might be better to use HTC.


    In this chapter, we have created a business services component using an HTC running on the client that will validate the values of XML data located in a data island. This component will immediately tell users whether they have entered an invalid field and reset the text box to its previous value. We have also used the DSO to create an application that can allow the user to view the data contained within a data island. Using the DSO, we can create a Web-based user services component that can interact with the user using an HTC that encapsulates the business rules for the application.


    discuss this topic to forum

    relation tutorial

    No relevant information

    Category

      Authoring (2)
      Book Samples (1)
      Database Related (2)
      Development (7)
      Introduction to XML (10)
      Java and XML (1)
      Miscellaneous (5)
      Parsing (2)
      PHP and XML (0)
      Style Sheets (8)
      Web Services (5)

    New

    Hot