Input Controls

Input widgets such as buttons, combo boxes, and text fields are essential to UIs. We couldn't do much without them! All development environments provide their own sets of widgets. The HTML specification includes support for many of these controls. Unfortunately, the widget set that HTML provides is not nearly as rich as those found in thick-client applications.

Fortunately (or unfortunately, as the case may be), browser vendors have ventured beyond the W3C specification in their support for UI controls. Microsoft has added support for drag and drop, "What-You-See-Is-What-You-Get" (WYSIWYG) editing, right-click context menus and a host of other enhancements to its Internet Explorer browser. Netscape has embarked on an XML UI project of its own, called XML User-Interface Language (XUL). XUL was the toolset used to create the Mozilla browser's UI. Hooks to these widgets were added to the browser's API to allow developers to take advantage of them.

While these enhancements certainly allow for a richer browser UI, they can be confusing, if not problematic, to the application developer trying to sort through them. With all these toolsets available, does it make sense to roll your own? Perhaps. This section will explore the benefits of creating your own UI schema and will describe XML models and XSLTs for simple HTML form input controls. We will also learn how to extend these controls to take advantage of some browser-specific features.

Consistency

The internal adoption of your own XML schema to UI control design will enforce visual consistency within your application. For example, select boxes, radio buttons, and check boxes are often used interchangeably. How many times have you seen a Web page that looks like the one in Figure 6-8?

Figure 6-8 Why do these questions need to use different input controls?

All three questions in this page limit the user to binary responses. However, page designers, when left alone, will often mix and match these controls. Sometimes they even do it intentionally! How many times have you thought "I haven't used a check box control in several pages. Why don't I throw one in here"?

XML will rid your application of the "Let's be kind to the check box" syndrome. Imagine programming to a more generic interface when gathering input. For example, the following XML could have been used to clean up the page in Figure 6-8.

 <property description="What is your gender?"> <choice name="gender" allowmultiple="no"> <option value="F">Female</option> <option value="M">Male</option> </choice> </property> <property description="Are you a vegetarian?"> <choice name="vegie" allowmultiple="no"> <option value="Y">Yes</option> <option value="N">No</option> </choice> </property> <property description="Which of these vegetables do you like?"> <choice name="favoriteFoods" allowmultiple="yes"> <option value="turnips">Turnips</option> <option value="asparagus">Asparagus</option> <option value="carrots">Carrots</option> <option value="celery">Celery</option> </choice> </property> 

The XSLT rules will govern what input control to use in a given context. The rule might be "Check boxes for two or more items, select boxes for all others," or it might be "Radio buttons all the time." Regardless, the UI will be consistent. This approach makes everyone's life easier. The page developer needs only remember a single API, and users doesn't need to suffer through three different ways of answering a "Yes/No" question.

You might also note in the previous example that the input fields are encapsulated by a property element. As we saw in previous examples, the property element makes the page developer's life much easier. Nearly every HTML form you encounter is a table with one column of prompts and another column of input controls. By including the prompt in the XML schema you save the developer from having to build new table rows and table cells for each question. The developer doesn't need to worry about the positioning of attributes or any other layout issues either. These are resolved by the XSLT layout manager.

The <form> Element

The first element we need to add to our schema is the <form> element, to indicate the presence of an input form. This element will look like the HTML <form> element with which you are already familiar. It will have only one essential attribute, the action attribute. Here is an example:

 <form action="controller.asp?view=processresults"/> 

This element, when transformed, will produce an HTML form that submits its contents to a Web page called controller.asp. Our first attempt to transform this element will be relatively straightforward.

 <xsl:template match="form"> <form action="{@action}" method="post"> <xsl:for-each select="@*[name() != 'action']"> <xsl:copy><xsl:value-of select="."/></xsl:copy> </xsl:for-each> <xsl:apply-templates/> &#160;&#160;<input  type="submit"/> </form> </xsl:template> 

Now that we have a form, we need something to put in it. We will need to define wrapper elements for each of the HTML form controls we will implement.

Use the "{expression}" shortcut when embedding XPath expressions inside element attributes. This is much less cumbersome than the <xsl:attribute> element.

The Text <input> Element

The first control we will implement is the standard text input control. This element will also be a subset of the HTML control. The element will be wrapped by a <property> element to control layout and formatting. An example of this element follows.

 <property description="Enter the CD's description"> <input type="text" size="30" value="Revolver" name="description"/> </property> 

The transformation for this element is simple. Notice that this transformation, like the form transformation before, iterates over the <input> element's attribute list and propagates them through to the output document. This open approach to the schema allows the page developer to pass through other attributes that the XSLT doesn't need to worry about, such as event handlers, to the result document.

 <xsl:template match="input[@type='text']"> <input type="text" name="{@name}" value="{@value}"  > <xsl:for-each select="@*[name() != 'name' and name() != `value']"> <xsl:copy><xsl:value-of select="."/></xsl:copy> </xsl:for-each> </input> </xsl:template> 

The controls we have defined thus far provide enough ammunition to build a simple example. The example will be a CD input page that Noverant employees can use to edit albums in their inventory. The example in Figure 6-9 shows the new form controls.

Figure 6-9 A simple CD edit example showing off new form controls.

 <document> <header> <title>The Beatles: Revolver</title> <icon>CD.gif</icon> </header> <form action="cdinput.asp"> <section> <header> <title>General Information</title> <icon>world.gif</icon> </header> <view> <properties> <property description="Artist"> <input type="text" name="artist" value="The Beatles"/> </property> <property description="Album"> <input type="text" name="description" value="Revolver"/> </property> <property description="Label"> <input type="text" name="label" value="EMI Records Ltd"/> </property> <property description="Year Released"> <input type="text" name="releaseDate" value="1966"/> </property> <property description="Price"> <input type="text" name="price" value="13.99"/> </property> </properties> </view> </section> </form> </document> 

The TextArea <input> Element

TextAreas are input controls familiar to Web form developers. They are more cumbersome to use than text input tags but are still necessary to capture longer streams of unstructured data. Our XML implementation will modify this element in three ways.

  • It will alter its syntax slightly, making it more consistent with regular text input tags. The HTML 4.01 and XHTML 1.1 specifications defined the textarea syntax to be <textarea>. . .</textarea>. This implementation will change the usage to look like <input type="textarea">. . .</input> for simplicity's sake. Changing the syntax also helps the content developer remember that he or she is programming to a schema that you have defined.
  • It will provide broad height and width defaults so that content developers do not need to worry about them. This will help make the application UI more consistent.
  • It will provide support for WYSIWYG editing to those users who have Internet Explorer 5.5 or later. This is a fantastic feature that opens up drag-and-drop and rich-text formatting to Web users.

The syntax for the textarea <input> element is exactly like its simple text counterpart, with the exception of a single attribute. Figure 6-10 shows an implementation of the textarea control.

Figure 6-10 An implementation of the textarea control.

Here is an example of its use:

 <property description="Enter the CD's description"> <input type="textarea" value="" name="notes"/> </property> 

The XSLT template to build an HTML textarea is as follows:

 <xsl:template match="input[@type='textarea']"> <textarea  wrap="{@word_wrap}" name="{@name}"> <xsl:choose> <xsl:when test="@rows!=''"> <xsl:attribute name="rows"><xsl:value-of select="@rows"/> </xsl:attribute> </xsl:when> <xsl:otherwise> <xsl:attribute name="rows">8</xsl:attribute> </xsl:otherwise> </xsl:choose> <xsl:choose> <xsl:when test="@cols!=''"> <xsl:attribute name="cols"><xsl:value-of select="@cols"/> </xsl:attribute> </xsl:when> <xsl:otherwise> <xsl:attribute name="cols">56</xsl:attribute> </xsl:otherwise> </xsl:choose> <xsl:value-of select="@value"/> </textarea> <xsl:if test="@mandatory = 'yes'"> <font >&#160;*</font> </xsl:if> </xsl:template>. 

Field Validation

Almost all applications need to validate the data that a user has entered before saving it. Some fields might be mandatory, while others, such as numeric fields, expect their values in a certain format. XML can help developers implement client-side form validation with little pain. All that is necessary is to add two new attributes, mandatory and datatype, to the <input> element and to update the XSLT.

For example, the "price" field on the CD edit form example should be mandatory and numeric. The new input field will look like this under the new schema:

 <property description="Price"> <input type="text" name="price" value="13.99" mandatory="yes" datatype="numeric"/> </property> 

The W3C's XForms specification also seeks to solve the client-side data validation problem. See this specification for more interesting ideas on abstract forms. It can be found at http://www.w3.org/MarkUp/Forms/.

Nothing more is required of the page developer to indicate restrictions on a particular input field. The bulk of the validation work will be done by the XSLT. It will be modified to include two new functions: hasValue(obj, obj_type) and isOfType(val, data_type). These scripts will be placed in a separate .js file that is included by the document template. The contents of Listing 6-6 follow.

Listing 6-6 formValidate.js: Contains the functions used for client-side mandatory field checking and datatype validation.

 function hasValue(obj, obj_type) { if (obj_type == "text" || obj_type == "password") { if (obj.value.length == 0)  return false; else  return true; } else if (obj_type == "select") { for (i=0; i < obj.length; i++) { if (obj.options[i].selected) return true; }  return false; } else if (obj_type == "single_radio" || obj_type == "single_checkbox") { if (obj.checked) return true; else  return false; } else if (obj_type == "radio" || obj_type == "checkbox") { for (i=0; i < obj.length; i++) { if (obj[i].checked) return true; }  return false; } } function isoftype(the_val, data_type) { if (data_type == "date") { if (isNaN(Date.parse(the_val))) { return false; } else { return true; } } if (data_type == "time") { if (isNaN(Date.parse("01/01/1970 "+the_val))) { return false; } else { return true; } } if (data_type == "monetary" || data_type == "numeric") { if (isNaN(the_val)) { return false; } else { return true; } } } 

The XSLT must be altered both to include this JavaScript file and to change the form submission process to check for mandatory and restricted fields. First the following line needs to be added to the head of the result document:

 <script language="javaScript" src="/books/4/456/1/html/2/js/formvalidate.js"/> 

Second the XSLT form template must be modified to build a dynamic JavaScript function that validates its contents. This function will be called during the form's onSubmit( ) event.

Because each validateForm( ) function is unique to the contents of a particular form, its name must be unique to prevent collisions with other forms that might exist on the same page. The XSL function generate-id( ) is used for this purpose.

 <xsl:template match="form"> <script language="JavaScript"> function validateForm_<xsl:value-of select="generate-id()"/> (thisForm) { 

The first part of the function iterates over all input elements in the XML document with the mandatory attribute set to "yes". The function then determines, for each mandatory field, the type of input control (for example, text box, textarea, and so forth), and then makes an appropriate call to the hasValue( ) function. If hasValue( ) returns false, the validateForm( ) function displays an error message, attempts to set the page focus to the field, and aborts the form submission.

 <xsl:for-each select="//*[@mandatory='yes']"> <xsl:choose> <xsl:when test="@type = 'text' or @type='textarea'"> if (!hasValue(thisForm.<xsl:value-of select="@name"/>
,"text")) { alert("<xsl:value-of select="@name"/> is mandatory."); var the_field = thisForm.<xsl:value-of select="@name"/>; if(the_field.style.display != "none" &amp; !the_field.disabled &amp; !the_field.readOnly &amp; !the_field.editableDiv) the_field.focus(); return false; } </xsl:when> </xsl:choose> </xsl:for-each>

Next the function needs to check any datatype restrictions mentioned in the XML source. This algorithm is similar to the one used to perform mandatory value checking. The script iterates over all input controls with a datatype attribute specified. For each attribute the script makes a call to the isoftype( ) function to match the format of the data entered by the user to the required format. If the isoftype( ) function call returns false, the validateForm( ) script reports the error to the user and aborts the form submission.

 <xsl:for-each select="//input[@datatype]"> if (!isoftype(thisForm.<xsl:value-of select="@name" />.value,'<xsl:value-of select="@datatype"/>')) { alert("<xsl:value-of select="@name"/> is not in <xsl:value-of select="@datatype"/> format"); return false; } </xsl:for-each> 

Finally the validateForm( ) function must inspect the form's onSubmit( ) event handler to see if further post processing is required. If the page developer specified an onSubmit( ) function to be called, the validateForm( ) function will call it before completing.

 <xsl:choose> <xsl:when test="@onSubmit"> var rtnval = <xsl:value-of select="@onSubmit"/>; return rtnval; </xsl:when> <xsl:otherwise> return true; </xsl:otherwise> </xsl:choose> } </script> 

The <form> HTML element must now be altered by the template to call the new formValidate( ) function when it is submitted, which is achieved by setting its onSubmit attribute.

 <form action="{@action}" method="post" onSubmit="return 
validateForm_{generate-id()}(this)"> <xsl:for-each select="@*[name() != 'action']"> <xsl:copy><xsl:value-of select="."/></xsl:copy> </xsl:for-each> <xsl:apply-templates/> &#160;&#160;<input type="submit"/> </form> </xsl:template>

WYSIWYG Editing

With the release of Internet Explorer 5.5, Microsoft made the browser's WYSIWYG HTML editing control available for use in Web pages. This control provides many useful options, such as:

  • Cut and Paste to and from other Windows applications, such as Microsoft Word
  • Embedded images
  • Rich-text formatting including bold, italics, underline, left, center, and right justification
  • User-defined hyperlinks
  • Text range formatting
  • Undo/Redo functionality

Developers can use this feature by setting a division's contentEditable attribute. Unfortunately, Internet Explorer does not implement this attribute on standard HTML input controls. For example, <textarea contentEditable = "true".../> will not have the desired effect. Instead, we must accomplish our goal through a bit of trickery. The good news is that the XSLT will handle all of the dirty work for us without any change to our forms XML object model. This is an excellent illustration of how XML and XSLT facilitate application extensibility.

The Microsoft WYSIWYG editing control does a remarkable job of creating well-formed HTML code. Well-formed HTML is much easier to incorporate into XML documents.

The trick to adding WYSIWYG editing to a text box is not to use a text box at all. We will use a combination of divisions and hidden variables to pull this off. To begin, the textarea XSLT template will change to insert a <div> element instead of a <TEXTAREA> element into the result document. The <div> element will have its contentEditable attribute set to "true" to enable WYSIWYG editing. You might also note that the template has added support for a new size attribute in the textarea <input> element. This attribute accepts values of "small", "medium", and "large" so that content developers can control the relative size of the input box without worrying about specifying an exact pixel height and width.

 <xsl:template match="input[@type='textarea']"> <div  contentEditable="true" > <xsl:choose> <xsl:when test="@size='small'"><xsl:attribute name="style"> display:inline;overflow:scroll;width=350;border:solid; border-style:ridge;border-width:2;background-color:white;height:150 </xsl:attribute></xsl:when> <xsl:when test="@size='medium'"><xsl:attribute name="style"> display:inline;overflow:scroll;width=350;border:solid; border-style:ridge;border-width:2;background-color:white;height:250 </xsl:attribute></xsl:when> <xsl:when test="@size='large'"><xsl:attribute name="style"> display:inline;overflow:scroll;width=350;border:solid; border-style:ridge;border-width:2;background-color:white;height:350 </xsl:attribute></xsl:when> <xsl:otherwise><xsl:attribute name="style">display:inline; overflow:scroll;width=350;border:solid;border-style:ridge; border-width:2;background-color:white;height:350</xsl:attribute> </xsl:otherwise> </xsl:choose> <xsl:value-of disable-output-escaping="yes" select="value"/> </div> <input type="hidden" editableDiv="yes" name="{@name}" value="{@value}"/> <xsl:if test="@mandatory = 'yes'"> <div style="display:inline"><font >&#160;*</font> </div> </xsl:if> </xsl:template> 

This code will insert a WYSIWYG-editable division into the Web page. How do we move the contents of this division into a form variable to be submitted with the form? The answer lies in the form validation function. We need to insert logic into this function that will create hidden form variables with the same names as the textareas to be submitted with the form.

 function validateForm_<xsl:value-of select="generate-id()"/>(thisForm) { <xsl:for-each select="//input[@type='textarea']"> thisForm.<xsl:value-of select="@name"/>.value =  document.getElementById('d<xsl:value-of select="generate-id()" />').innerHTML; </xsl:for-each> // rest of the validateForm() function goes here } 

Now, like magic, users can edit your textareas just as if they were editing a Word document. Figure 6-11 shows the XML document with WYSIWYG editing enabled.

Dynamic XSL Transformations

WYSIWYG editing is fantastic for users with capable browsers, but what about support for older or nonconformant browsers? Our current textarea transformation would produce a division that could not be edited by any browser that does not support WYSIWYG (for example, Internet Explorer 5.0 or later). Certainly, this is a problem.

One solution might be to create a second textarea <input> XML element that looked like <input type="richtextarea" ...>. The ASP page developer could check the user's browser at runtime and use the plain textarea <input> element to support older versions. While this scheme might work, it casts the burden of worrying about presentation onto the page developer. This is contrary to XML's philosophy of separating content from presentation and should be avoided if possible.

Figure 6-11 Same XML document as in Figure 6-10, with WYSIWYG editing enabled.

Another solution might be to create multiple XSLT templates, one for each class of Web browser. The Internet Explorer 5.0 template would use the regular textarea control while the Internet Explorer 5.5 and later templates would take advantage of the rich-text version. The page controller would determine the user's browser and select the correct template for rendering the page results. This approach has merits because it removes the presentation responsibility from the page developer, which is good. The approach is also extensible. Whenever a new browser hits the market, the XSLT designer need only create a new transformation geared to the new software and drop it into the XSLT library.

The downside to this solution is that it yields a proliferation of slightly different skins, all of which must be maintained. This situation becomes particularly problematic when changes are introduced into the application's UI schema. All of the transformations must be updated whenever this happens. While a transformation skin library might be useful for separating wildly different classes of browsers (for example, separating Netscape 4.71 from Internet Explorer 6.0), it behooves the XSLT developer to keep the number of transformations in this library under control.

Instead of creating a new skin for each Web browser, the XSLT developer can make use of another technique, known as "dynamic XSLT," to manage subtle differences between browsers. Dynamic XSLT is simple to explain and understand although its application is limitless (and sometimes mind-bending!). The idea is simple: make your XSLTs ASPs. No rule says XSLT pages need to be static. The plain vs. rich text textarea problem can be solved by a single ASP transformation. The logic in the page might look something like the following:

 <% If browser = "MSIE" and broserVersion >= 5.5 Then %> <xsl:template match="input[@type='textarea']"> <div  contentEditable="true" > <xsl:choose> <xsl:when test="@size='small'"><xsl:attribute name="style"> display:inline;overflow:scroll;width=350;border:solid; border-style:ridge;border-width:2;background-color:white;height:150 </xsl:attribute></xsl:when> <xsl:when test="@size='medium'"><xsl:attribute name="style"> display:inline;overflow:scroll;width=350;border:solid; border-style:ridge;border-width:2;background-color:white;height:250 </xsl:attribute></xsl:when> <xsl:when test="@size='large'"><xsl:attribute name="style"> display:inline;overflow:scroll;width=350;border:solid; border-style:ridge;border-width:2;background-color:white;height:350 </xsl:attribute></xsl:when> <xsl:otherwise><xsl:attribute name="style">display:inline; overflow:scroll;width=350;border:solid;border-style:ridge; border-width:2;background-color:white;height:350</xsl:attribute> </xsl:otherwise> </xsl:choose> <xsl:value-of disable-output-escaping="yes" select="value"/> </div> <input type="hidden" editableDiv="yes" name="{@name}" value="{@value}"/> <xsl:if test="@mandatory = 'yes'"> <div style="display:inline"><font >&#160;*</font> </div> </xsl:if> </xsl:template> <% End Else %> <xsl:template match="input[@type='textarea']"> <textarea  wrap="{@word_wrap}" name="{@name}"> <xsl:choose> <xsl:when test="@rows!=''"> <xsl:attribute name="rows"><xsl:value-of select="@rows"/> </xsl:attribute> </xsl:when> <xsl:otherwise> <xsl:attribute name="rows">8</xsl:attribute> </xsl:otherwise> </xsl:choose> <xsl:choose> <xsl:when test="@cols!=''"> <xsl:attribute name="cols"><xsl:value-of select="@cols"/> </xsl:attribute> </xsl:when> <xsl:otherwise> <xsl:attribute name="cols">56</xsl:attribute> </xsl:otherwise> </xsl:choose> <xsl:value-of select="@value"/> </textarea> <xsl:if test="@mandatory = 'yes'"> <font >&#160;*</font> </xsl:if> </xsl:template> <% End %> 

XSLT documents are well-formed XML and can be transformed by XSLT. Imagine using XSLT to transform another XSLT document to transform a source XML document. Not only is this possible, it is often good design.

Dynamic XSLT offers an ideal solution to tricky differences between otherwise similar browsers. Again, a skin library should be used to isolate presentation rules across different browser classes. However, this technique is very useful, particularly as newer browsers approach the panacea of full W3C specification support.

The <choice> Element

The <choice> element is used to automate the creation of HTML select boxes, radio buttons, and check boxes. Let's re-examine the example supplied at the beginning of the section.

 <property description="What is your gender?"> <choice name="gender" allowmultiple="no"> <option value="F">Female</option> <option value="M">Male</option> </choice> </property> <property description="Are you a vegetarian?"> <choice name="vegie" allowmultiple="no"> <option value="Y">Yes</option> <option value="N">No</option> </choice> </property> <property description="Which of these vegetables do you like?"> <choice name="favoriteFoods" allowmultiple="yes"> <option value="turnips">Turnips</option> <option value="asparagus">Asparagus</option> <option value="carrots">Carrots</option> <option value="celery">Celery</option> </choice> </property> 

The XSLT for this control should incorporate presentation rules to determine how the choices should be rendered. The extreme cases are easy to Figure out. A choice with two hundred options is probably best displayed inside a <select> combo box. At the other end of the spectrum a choice offering two options with the allowmultiple attribute turned on is best shown as two check boxes. Our transformation will use five options as an arbitrary cutoff between select boxes and check boxes/radio buttons. Your presentation rules might differ or change over time.

In the transformation you will first notice the test of the option count. If the count is greater than five the template creates a select box, setting its multiple and size attributes to yes and 5, respectively, if the user is allowed to select more than one option. In the case of small choices, where the option count is at most five, the template creates either a series of check boxes or a series of radio buttons with the same name. Check boxes are used when the allowmultple attribute is set to yes; radio buttons are used when it is not.

 <xsl:template match="choice"> <xsl:choose> <xsl:when test="count(option) > 5"> <select > <xsl:for-each select="@*[name() != 'mandatory']"> <xsl:copy><xsl:value-of select="."/></xsl:copy> </xsl:for-each> <xsl:if test="@allowmultiple = 'yes'"> <xsl:attribute name="multiple">yes</xsl:attribute> <xsl:attribute name="size">5</xsl:attribute> </xsl:if> <option value="">Please Choose Below</option> <xsl:for-each select="option"> <option> <xsl:attribute name="value"><xsl:value-of select="@value"/>
</xsl:attribute> <xsl:if test="@selected='yes'"> <xsl:attribute name="selected"><xsl:value-of select="@selected"/></xsl:attribute> </xsl:if> <xsl:value-of select="."/> </option> </xsl:for-each> </select> </xsl:when> <xsl:otherwise> <div style="display:inline"> <xsl:for-each select="option"> <xsl:choose> <xsl:when test="../@allowmultiple = 'yes'"> <input type="checkbox" name="{../@name}"
value="{@value}"/>&#160;<xsl:value-of select="."/><br/> </xsl:when> <xsl:otherwise> <input type="radio" name="{../@name}" value="{@value}"/> &#160;<xsl:value-of select="."/><br/> </xsl:otherwise> </xsl:choose> </xsl:for-each> </div> </xsl:otherwise> </xsl:choose> <xsl:if test="@mandatory = 'yes'"> <div style="display:inline"><font >&#160;*</font> </div> </xsl:if> </xsl:template>

Figure 6-12 and Figure 6-13 show how the result page differs when the number of select options climbs above five.

Figure 6-12 Smaller versus larger option sets (smaller set).

That's it for multichoice input controls. The formValidate( ) function will need to be updated to mimic your presentation rules when checking for mandatory fields.



XML Programming
XML Programming Bible
ISBN: 0764538292
EAN: 2147483647
Year: 2002
Pages: 134

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