ADVANCED macromedia COLDFUSION MX 7 Application Development [Electronic resources]

Ben Forta, Dain Anderson, Brian Baxter, Jeffrey Bouley, Raymond Camden, Adam Churvis, David Churvis, Ken Fricklas, Paul Hastings, Sam Neff, Robi Sen

نسخه متنی -صفحه : 240/ 143
نمايش فراداده

Creating XForms in CFML

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 a227 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 standar217 inside an XForms specification.

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
--->
<!DOCTYP218 PUBLIC "-//W3C//DT217 4.01 Transitional//EN" "http://www.w3.org/T2314
/loose.dtd">
&l233>
<head>
<meta http-equiv="Content-Type" content="tex233; 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#">
&nbsp;&nbsp;
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#">
&nbsp;&nbsp;
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>
<l>

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
--->
<!DOCTYP218 PUBLIC "-//W3C//DT217 4.01 Transitional//EN"
"http://www.w3.org/T2314/loose.dtd">
&l233>
<head>
<meta http-equiv="Content-Type" content="tex233; 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=l">
<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>
<l>

The output of this can be seen in

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 a227

Table 20.5. Skin Format and XSL File Locations

FORMAT OF <cfform skin=">

FILE LOCATION

skin="basic"

Default directory or its subdirectories

skin="c:\OWS\Skins\mySkin.xsl"

Absolute path specified

skin="mySkin.xsl"

Current template directory

skin="xsl/mySkin.xsl"

Relative path; xsl subdirectory

skin="http://www.macromedia.com/skins/mySkin.xsl"

URL

skin="default" or not specified

default.xsl from default scripts/xsl directory

skin="none"

Not rendered

All controls and formatting inside ColdFusion MX 7 XML forms either be CFML tags, or contained in CFML tags that handle text data. Othe231 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".

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 <> any text inside the <cfformitem> is passed through verbatim to the resulting form. You can put other CFML tags inside the <cfformitem> and generate th218 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 '&lt;')

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

See previous paragraphs for more information

style

CSS Text

Passed through t228 as style

The XForms Form Definition

So 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="/CFIDE/scripts/"
html:action="/ows/20/actorFormXML.cfm"l:method="post"
html:name="xActorForm"
xmlns:cf="http://www.macromedia.com/2004/cfform"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmln232="http://www.w3.org/1999/l" xmlns:xf="http://www.w3.org/2002/XForms">
<xf:model id="xActorForm">
<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 id="NameFirst"
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 id="NameFirstReal"
. . .
</xf:bind>
<xf:bind id="Age" 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 id="isTotalBabe"
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 id="isTotalBabe"
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 id="isEgomaniac"
. . .
</xf:bind>
</xf:model>
<xf:output><![CDATA[<H1>Edit Actor</H1>]]><xf:extension/>
</xf:output>
<xf:input bind="NameFirst" id="NameFirst">
<xf:label>First Name</xf:label>
<xf:extension>
<cf:attribute name="type">text</cf:attribute>
</xf:extension>
</xf:input>
. . .
<xf:input bind="Age" id="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" id="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 id="doEdit" 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="/CFIDE/scripts/"
html:action="/ows/20/actorFormXML.cfm"l:method="post"
html:name="xActorForm"
xmlns:cf="http://www.macromedia.com/2004/cfform"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmln232="http://www.w3.org/1999/l" 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 <> 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 id="xActorForm">...</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 a227 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 id="NameFirst"
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 th218 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" id="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 a232), 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 a227 Web browser.

Chapter 30 of the ColdFusion Developer's 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 Form

As mentioned previously, ColdFusion both generates the XML, and then turns that into standar217 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 an217 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.