Creating XForms with ColdFusion is very simple because ColdFusion both contains the tags you use to create the form in XML, and the engine that renders the XML as an HTML form, complete with JavaScript, that can be used in any Web browser. Let's look at a form and see how ColdFusion renders it in XML. The first thing to know about XForms is that they exist entirely as XML documents, and therefore you can't use standard HTML inside an XForms specification. Listing 20.1 shows the form used to update a previous actor from Chapter 19, as created in <cfform>. It's been modified to validate the actor's age, and it now requires the actor's first and last names. Listing 20.1. actorForm.cfmActor Update Form, using <cfform>[View full width] <!--- actorForm.cfm Ken Fricklas (kenf@fricklas.com) Modified: 2/21/2005 Listing 20.1 ---> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4 /loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <title>Sample Form</title> </head> <body> <!--- note: update code left out for brevity ---> <cfscript> // Create an instance of the Actor component myActor = createObject("component","19.pActor"); // Set the ID in the THIS scope myActor.ActorID = 8; // Get the details myActor.get(); </cfscript> <cfform action="#cgi.script_name#" method="post"> <h1>Edit Actor</h1> <input type="hidden" name="ActorID" value="#myActor.ActorID#"> <table border="1"> <tr> <td>First Name</td> <td><cfinput name="NameFirst" type="text" value="#myActor.NameFirst#" required="yes"></td> </tr> <tr> <td><EM>Real</EM> First Name</td> <td><cfinput name="NameFirstReal" type="text" value="#myActor.NameFirstReal#" required="no"></td> </tr> <tr> <td>Last Name</td> <td><cfinput name="NameLast" type="text" value="#myActor.NameLast#" required="yes"></td> </tr> <tr> <tr> <td>Age</td> <td><cfinput name="Age" type="text" value="#myActor.Age#" required="yes" validate="integer" range="1,110" message="Age must be a number between 1 and 110."></td> </tr> <tr> <td>A total babe?</td> <td> yes <cfinput name="isTotalBabe" type="radio" value="1" checked="#myActor.IsTotalBabe#"> no <cfinput name="isTotalBabe" type="radio" value="0" checked="#NOT myActor.IsTotalBabe#"> </td> </tr> <tr> <td>An Egomaniac?</td> <td> yes <cfinput name="isEgomaniac" type="radio" value="1" checked="#myActor.isEgomaniac#"> no <cfinput name="isEgomaniac" type="radio" value="0" checked="#NOT myActor.isEgomaniac#"> </td> </tr> </TABLE> <BR> <input name="doEdit" type="submit" value="Save Changes"> </cfform> </body> </html> This form can be seen in Figure 20.1. Figure 20.1. Actor Update Form.And here's the same form, now using an XForms XML form: Listing 20.2. actorFormXML.cfmActor Update Form, using <cfform type="XML"><!--- actorFormXML.cfm Actor Form, now using format="XML" Ken Fricklas (kenf@fricklas.com) Modified: 2/21/2005 Listing 20.2 ---> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <title>Sample Form</title> </head> <body> <cfscript> // Create an instance of the Actor component myActor = createObject("component","19.pActor"); // Set the ID in the THIS scope myActor.ActorID = 8; // Get the details myActor.get(); </cfscript> <!--- this time, format="XML" is added. We'll use the gray CSS skin. ---> <cfform action="#cgi.script_name#" method="post" format="xml" name="xActorForm" skin="gray"> <cfformitem type="html"> <H1>Edit Actor</H1> </cfformitem> <cfinput type="hidden" name="ActorID" value="#myActor.ActorID#"> <cfinput name="NameFirst" type="text" value="#myActor.NameFirst#" required="yes" label="First Name"> <cfinput name="NameFirstReal" type="text" value="#myActor.NameFirstReal#" required="no" label="Real First Name"> <cfinput name="NameLast" type="text" value="#myActor.NameLast#" required="yes" label="Last Name"> <cfinput name="Age" type="text" value="#myActor.Age#" required="yes" validate="integer" range="1,110" message="Age must be a number between 1 and 110." label="Age?"> <cfformgroup type="horizontal" label="A total babe?"> <cfinput name="isTotalBabe" type="radio" value="1" checked="#myActor.IsTotalBabe#" label="yes"> <cfinput name="isTotalBabe" type="radio" value="0" checked="#NOT myActor.IsTotalBabe#" label="no"> </cfformgroup> <cfformgroup type="horizontal" label="An Egomaniac?"> <cfinput name="isEgomaniac" type="radio" value="1" checked="#myActor.isEgomaniac#" label="yes"> <cfinput name="isEgomaniac" type="radio" value="0" checked="#NOT myActor.isEgomaniac#" label="no"> </cfformgroup> <cfinput name="doEdit" type="submit" value="Save Changes"> </cfform> </body> </html> The output of this can be seen in Figure 20.2. Since we've specified a skin that uses CSS for layout, the formatting is different, but the information is identical with the HTML form. We'll learn much more about skins in the section in the section on Skinning and Styling, later in the chapter. Figure 20.2. Actor Update Form.So what's changed? <cfform> has a new attribute, format, which we've set to XML. format is also used in Flash forms, with the value of "Flash", covered in Macromedia ColdFusion MX 7 Web Application Construction Kit. What about the rest of the tags? First of all, the <cfinput> from previous versions of ColdFusion can now accept any type of input field that you can specify in an HTML <input> tag, including checkbox, radio, submit and reset. You can't put <select> or <textarea> in an HTML <input> tag, so sure enough, you use <cfselect> for <select>, and <cftextarea> (new in ColdFusion MX 7) for <textarea>. You can also use <cftree>, <cfslider>, and <cfgrid>. NOTE While <cfslider> does work in XML forms, many of the attributes are not passed to the control, including the lookandfeel and the label, which conflicts with the XML attribute of the same nameso the value is never visible on the slider! For these reasons, I view <cfslider> is not really useful with XML forms in ColdFusion MX 7 as currently implemented. The attributes for <cfform>, when you're using it with format="XML" are listed in Table 20.1.
All controls and formatting inside ColdFusion MX 7 XML forms either be CFML tags, or contained in CFML tags that handle text data. Other HTML that is included in XML forms is discarded, which leaves us with a problem: how do we put labels and text into our forms, and how do we align things so our form looks the way we want it to? XML Forms use the same syntax as Flash forms, using <cfformgroup> to group items within a form and align them, and <cfformitem> to add formatted text and other markets to the forms. Both <cfformgroup> and <cfformitem> require closing tags, because they act as containers that operate on other text and tags inside them. In the code in Listing 20.2, I used <cfformgroup> to add a label and lay out my radio buttons: <cfformgroup type="horizontal" label="A total babe?"> <cfinput name="isTotalBabe" type="radio" value="1" checked="#myActor.IsTotalBabe#" label="yes"> <cfinput name="isTotalBabe" type="radio" value="0" checked="#NOT myActor.IsTotalBabe#" label="no"> </cfformgroup> The type attribute in <cfformgroup> can be horizontal, vertical, or fieldset. Horizontal, used previously, lays out the <cfinput> tags inside it side by side, vertical one above the other, and fieldset groups its children by drawing a box around them and putting the label (called the legend) over the upper left side of the box, as can be seen in Figure 20.3. Figure 20.3. Formatting of <cfformgroup> type="fieldset".When you are using the <cfformgroup> with the type attribute set to fieldset, this translates into an HTML <fieldset>. You can also pass a style attribute to the <cfformgroup> tag. Any information in the style attribute is passed through unchanged to the HTML. NOTE Although the documentation indicates that the width attribute can be used, it doesn't really workuse style="width:300px" (for example), instead. This is also true for <cfform>; use style instead of width and height. TIP If you want to lay the controls in a <cfformgroup> vertically, put another <cfformgroup> inside it that has no label and a type of vertical. The full syntax for <cfformgroup> is in Table 20.2.
What if we want other text in our form, or perhaps a horizontal line? We can use <cfformitem>, new in ColdFusion MX 7. <cfformitem> does several different things, depending on its type attribute. If type is set to HTML, any text inside the <cfformitem> is passed through verbatim to the resulting form. You can put other CFML tags inside the <cfformitem> and generate the HTML if desired. If type is set to text, the text inside the <cfformitem> is XML escaped, similar to the XMLFormat() function, and then passed through (for example, a left bracket '<' becomes '<') If the type is hrule, this creates an <HR> tag. Use the style attribute to specify its color, width, or anything else that applied to horizontal rules. Finally, if the type is anything else, the string will be passed through as an element to the XML interpreter; we'll use this later in this chapter to extend ColdFusion with our own type. The full syntax for <cfformitem> is in Table 20.2.
The XForms Form DefinitionSo how exactly does an XML form work? An XML form is split into separate sections, corresponding to the values, layout, and binding, which means which values connect to which form items. When ColdFusion generates a form from <cfform format="xml">, it puts the text of the XML form definition into a variable with the same name as the form. In the example in Listing 20.2, when the form is generated the form definition is put in the variable xActorForm, excerpted in Listing 20.3 (since all the input type="text" and radio button sets look pretty much the same). Listing 20.3. XML from XML Actor Form<form cf:archive="/CFIDE/classes/cfapplets.jar" cf:codebase="http://java.sun.com/products/plugin/1.3/jinstall-13- win32.cab#Version=1,3,0,0" cf:instance="1" cf:name="xActorForm" cf:scriptsrc="/books/2/449/1/html/2//CFIDE/scripts/" html:action="/ows/20/actorFormXML.cfm" html:method="post" html:name="xActorForm" xmlns:cf="http://www.macromedia.com/2004/cfform" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/XForms"> <xf:model > <xf:instance> <cf:data> <ActorID>8</ActorID> <NameFirst>Roger</NameFirst> <NameFirstReal>N.</NameFirstReal> <NameLast>Wilson</NameLast> <Age>35</Age> <isTotalBabe>1</isTotalBabe> <isEgomaniac>1</isEgomaniac> </cf:data> </xf:instance> <xf:submission action="/ows/20/actorFormXML.cfm" method="post"/> <xf:bind nodeset="//xf:model/xf:instance/cf:data/NameFirst" required="true()"> <xf:extension> <cf:attribute name="type">TEXT</cf:attribute> <cf:attribute name="onerror">_CF_onError</cf:attribute> </xf:extension> </xf:bind> <xf:bind . . . </xf:bind> <xf:bind nodeset="//xf:model/xf:instance/cf:data/Age" required="true()"> <xf:extension> <cf:attribute name="type">TEXT</cf:attribute> <cf:attribute name="onerror">_CF_onError</cf:attribute> <cf:validate type="range"> <cf:argument name="message">Age must be a number between 1 and 110.</cf:argument> <cf:argument name="min">1.0</cf:argument> <cf:argument name="max">110.0</cf:argument> <cf:trigger event="onsubmit"/> </cf:validate> <cf:validate type="integer"> <cf:argument name="message">Age must be a number between 1 and 110.</cf:argument> <cf:trigger event="onsubmit"/> </cf:validate> </xf:extension> </xf:bind> <xf:bind nodeset="//xf:model/xf:instance/cf:data/isTotalBabe" required="false()"> <xf:extension> <cf:attribute name="type">RADIO</cf:attribute> <cf:attribute name="onerror">_CF_onError</cf:attribute> </xf:extension> </xf:bind> <xf:bind nodeset="//xf:model/xf:instance/cf:data/isTotalBabe" required="false()"> <xf:extension> <cf:attribute name="type">RADIO</cf:attribute> <cf:attribute name="onerror">_CF_onError</cf:attribute> </xf:extension> </xf:bind> <xf:bind . . . </xf:bind> </xf:model> <xf:output><![CDATA[<H1>Edit Actor</H1>]]><xf:extension/> </xf:output> <xf:input bind="NameFirst" > <xf:label>First Name</xf:label> <xf:extension> <cf:attribute name="type">text</cf:attribute> </xf:extension> </xf:input> . . . <xf:input bind="Age" > <xf:label>Age?</xf:label> <xf:extension> <cf:attribute name="type">text</cf:attribute> </xf:extension> </xf:input> <xf:group appearance="horizontal"> <xf:label>A total babe?</xf:label> <xf:extension/> <xf:select1 appearance="full" bind="isTotalBabe" > <xf:extension> <cf:attribute name="type">radio</cf:attribute> </xf:extension> <xf:choices> <xf:item> <xf:label>yes</xf:label> <xf:value>1</xf:value> <xf:extension> <cf:attribute name="checked">checked</cf:attribute> </xf:extension> </xf:item> <xf:item> <xf:label>no</xf:label> <xf:value>0</xf:value> <xf:extension/> </xf:item> </xf:choices> </xf:select1> </xf:group> . . . <xf:submit submission="xActorForm"> <xf:label>Save Changes</xf:label> <xf:extension> <cf:attribute name="type">submit</cf:attribute> <cf:attribute name="name">doEdit</cf:attribute> </xf:extension> </xf:submit> </form> Let's look at that code a bit at a time. It starts off with the standard XML header: <form cf:archive="/CFIDE/classes/cfapplets.jar" cf:codebase="http://java.sun.com/products/plugin/1.3/jinstall-13- win32.cab#Version=1,3,0,0" cf:instance="1" cf:name="xActorForm" cf:scriptsrc="/books/2/449/1/html/2//CFIDE/scripts/" html:action="/ows/20/actorFormXML.cfm" html:method="post" html:name="xActorForm" xmlns:cf="http://www.macromedia.com/2004/cfform" xmlns:ev="http://www.w3.org/2001/xml-events" xmlns:html="http://www.w3.org/1999/xhtml" xmlns:xf="http://www.w3.org/2002/XForms"> This indicates that the data in the document is a form, and it's followed by some header information. The xmlns: elements define the URL's of the definitions of the XML namespaces where you can find the definitions of the elements in the document. The syntax xmlns:cf="http://www.macromedia.com/2004/cfform" indicates that anything that starts with cf: defined by http://www.macromedia.com/2004/cfform. The html: elements define the elements of an XHTML form, the xf: elements are XForms specific, and ev: elements are the XML events, which define the interactions between the elements. The rest of the document is split into two sectionsthe data definition and the display information. The data definition is surrounded by: <xf:model >...</xf:model> This tells us that what's inside will be the definition of a model called "xActorForm", which should look familiarit's the name of our form. Therefore what this document is describing is the object, or model, defined by the data in our form. Inside this are the actual definition of the data, and the bindings. The data definition looks like: <xf:instance> <cf:data> <ActorID>8</ActorID> <NameFirst>Roger</NameFirst> <NameFirstReal>N.</NameFirstReal> <NameLast>Wilson</NameLast> <Age>35</Age> <isTotalBabe>1</isTotalBabe> <isEgomaniac>1</isEgomaniac> </cf:data> </xf:instance> This indicates that we have an instance of a "xActorForm". This is where XForms gets coolthe data is defined by the attributes of our data model, instead of artificially by the way the form is laid out as in a typical form. In other words, we're defining what we've got independently from the specific way we're viewing it on this page. NOTE In XForms, this information (or any other part of the form) can be imported from another document or URL, which allows the document to be completely broken up into separate sections, perhaps with completely different developers and sources of data. This means that the form can be reused with different data sets without modification, and without custom programming to insert all the "value=" attributes into the form. The data at the URL which provides the instance data could be generated from a ColdFusion program or directly from a database. Next comes: <xf:submission action="/ows/20/actorFormXML.cfm" method="post"/> This line tells the form what to do when the form is submitted, namely post the data to the URL /ows/20/actorFormXML.cfm. The other allowed actions in an XForm are GET, corresponding to an HTML get, and PUT, which copies a file containing that instance data show above, in that format, to a specific location, possibly even the local hard disk (this isn't supported in ColdFusion MX 7 however). The glue code is next; here the document defines which of the data elements we defined are going to be displayed in which of the form controls. A typical binding from ColdFusion MX 7 is the one that attaches the NameFirst field to its data. This looks like: <xf:bind nodeset="//xf:model/xf:instance/cf:data/NameFirst" required="true()"> <xf:extension> <cf:attribute name="type">TEXT</cf:attribute> <cf:attribute name="onerror">_CF_onError</cf:attribute> </xf:extension> </xf:bind> This element first defines its name, then shows an XPath definition of which element (nodeset) it's attached to (in the XForms definition, each element can attach to more than one control). The nodeset //xf:model/xf:instance/cf:data/NameFirst indicates that any data element in any instance of our model (at any depth, since it starts with "//") should show the data from NameFirst in the model, namely "Roger". It also defines required="true()", indicating that the data element must exist, and some ColdFusion-specific extension that ColdFusion uses to pass data to the rendering engine about the error handling and display type. The rest of the bindings are similar, so let's go on to: <xf:output><![CDATA[<H1>Edit Actor</H1>]]><xf:extension/></xf:output> This is the beginning of our display output. The xf:output is followed by a CDATAcharacter datarepresentation of the HTML that we passed in our <cfforminput> tag, which is how text data gets passed through to the output. The display output continues with all the xf:input, xf:group and similar tags. These define the actual way that various data should be displayed and input, e.g. output only, one choice from a list, etc. A simple one of these is the Age text area: <xf:input bind="Age" > <xf:label>Age?</xf:label> <xf:extension> <cf:attribute name="type">text</cf:attribute> </xf:extension> </xf:input> This defines the binding as Age, the label text, and a ColdFusion specific extension to tell ColdFusion how to render it. Since the display is separate from the display engine ( in other words, the document doesn't "know" its going to be rendered as HTML), this could just as easily be rendered by a voice mail system as a voice mail prompt ("Please indicate Roger's age at the beep. No, his real age… Beep."), as by an HTML Web browser. Table 20.4 shows all the control types for XForms.
NOTE SELECT1 and SELECT can be defined as "open", which allows the user to enter a value or choose an item from the list, similar to a combo box. ColdFusion MX 7 doesn't support this type of SELECT at this time.
For more information on XForms, controls, and actions, see the XForms specification or the tutorial at http://xforms.dstc.edu.au/tutorial/. A complete listing of CFML to XML tag mappings is available in Chapter 30 of the ColdFusion Developers Guide that is part of your ColdFusion MX 7 installation, or available for download at http://www.macromedia.com/support/documentation/en/coldfusion/. Finally, the xf:group tags define items that should be grouped together, and how that should be visually represented. Skinning and Styling: Rendering the FormAs mentioned previously, ColdFusion both generates the XML, and then turns that into standard HTML that your browser can read. The <cfform> has an attribute of "SKIN" that you use to specify the skin that ColdFusion will use to generate the CSS and HTML code for the rendered version of the form. The rules for using SKIN are as follows: If it's the name of an xsl file in the cf_webroot\CFIDE\scripts\xsl directory, or a subdirectory of that directory, without the 'xsl' suffix, ColdFusion will apply the specified skin. If you specify the full qualified path to an xsl file (or use a ColdFusion administrator mapped path), it will use the xsl file you specify. If you omit the skin name, or use "default", it uses default.xsl from the scripts directory. See Table 20.5 for a more concise list of the directories. When you specify one of the standard skins, they will include a reference to the CSS style sheet files that define the classes used in the skins. These CSS files are by default contained in the directory { web root}/CFIDE/scripts/css. The CSS files are named {skin}_style.css where { skin} is the name of the corresponding XSL skin, for example blue_style.css corresponds to the blue.xsl skin. The basic_style.css is used by the basic.xsl skin. (The basiccss.xsl skin is the exception; it uses both basic2_style.css and css_layout.css.) These files contain classes to format all the control types; these files are very complete, and even contain empty classes for completeness. You can modify these style sheets to make modifications to the classes to customize your application's layout. For example, in Figure 20.2, the submit button is too narrow to contain all the text for the "Save Changes" button. Looking at the source, we see this uses the style .cfButton, defined in the {webroot}/CFIDE/scripts/css/style_lightgray.css (we used the skin "light gray") as: .cfButton{ background-color: #f7f7f7; border:1px solid #cabba9; width: 80px; color: #48585f; font-weight:bold; margin-bottom:10px; margin-right:10px; margin-top:10px; } This shows that the width of a button is only 80 pixels! By removing the width: 80px; line, or changing it to a larger value, we can fix the formatting of this submit button. Here I've reformatted the style sheet to eliminate the width, and make a more dramatic border with a dashed, 2-pixel border. .cfButton{ background-color: #f7f7f7; border:2px dashed #cabba9; color: #48585f; font-weight:bold; margin-bottom:10px; margin-right:10px; margin-top:10px; } The results of this can be seen in Figure 20.4. Figure 20.4. XML Form with Reformatted Submit Button (partial).NOTE If you define the skin attribute as none, or if ColdFusion can't find the XSL file with the given name, it won't render the page at all; this allows you to create XForms that can be rendered by external rendering engines such as those mentioned at the beginning of this chapter. "Great!" you're probably thinking, "now what's in those files?" What's in those files is XSL, the eXtensible Stylesheet Language. |