 | Level: Intermediate Nicholas Chase (nicholas@nicholaschase.com), Developer/Writer, Studio B
22 Jul 2004 Because they can easily send and receive XML, XForms forms make great Web services clients, but using them in this way limits your control over the structure of your instance. This tip explains how to manage your data within one instance while submitting a second.
In a previous tip, I explained how to create an XForms form that sends a request to a Web service and then displays the results. The difficulty here is that using this technique requires you to use an instance that conforms to the message the Web service expects, such as a SOAP message. In this tip, I'll explain how you can create and interact with a second instance, but submit the SOAP-formatted instance when the time comes.
This tip assumes a basic familiarity with XForms and with Web services. It uses the FormsPlayer plug-in for Internet Explorer (see Resources to download it).
The Web service
To keep things simple, I'm once again going to use the ubiquitous weather example, sending a ZIP code and receiving the current temperature. The actual Web service is provided by XMethods.net (see Resources).
The initial request is just a SOAP message, such as:
Listing 1. The SOAP message
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:getTemp xmlns:ns1="urn:xmethods-Temperature"
SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<zipcode xsi:type="xsd:string">02134</zipcode>
</ns1:getTemp>
lt;/SOAP-ENV:Body>
</SOAP-ENV:Envelope>
|
In this case, you're not going to use the form to directly manipulate this message. You're going to create a second instance that's a bit friendlier, and then use the data to populate the above structure before submitting it.
The basic XForms form
Begin by creating the basic form. An XForms form consists of a model and controls, so start with the basic instance and the controls to manipulate it as shown in Listing 2.
Listing 2. The basic XForms form
<html
xmlns="http://www.w3.org/1999/xhtml"
xmlns:xforms="http://www.w3.org/2002/xforms"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:ns1="urn:xmethods-Temperature"
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
>
<head>
<title>XForms and Web Services</title>
</head>
<body>
<object id="FormsPlayer"
classid="CLSID:4D0ABA11-C5F0-4478-991A-375C4B648F58">
<b>FormsPlayer has not loaded. Please check your installation.</b>
</object>
<?import namespace="xforms" implementation="#FormsPlayer" ?>
<xforms:model id="WeatherService">
<xforms:instance id="guiInstance" xmlns="">
<inputData>
<zip>enter zip</zip>
</inputData>
</xforms:instance>
</xforms:model>
<xforms:input ref="instance('guiInstance')//zip">
<xforms:label>Zip code: </xforms:label>
<xforms:hint>Enter a zip code and submit the form for the current
temperature in that area.</xforms:hint>
</xforms:input>
</body>
</html>
|
The page begins by defining all of the relevant namespaces, and then moves on to importing the FormsPlayer object and its namespace. You can then get into the form itself, which is shown in Figure 1.
Start with the model, which will ultimately hold both instances and the submission information when you're ready for it. In the meantime, you've just defined a very simple instance to hold the data you're looking for. This could just as easily be a complex document drawn from your legacy systems. At the end of the page, you've defined a simple input box that's bound to the zip element.
Figure 1: The input control
Now you'll prepare for submitting the form.
Create the submission framework
Notice that I said "you'll prepare for submitting the form," rather than "you'll submit the form." As in the first tip, you have to deal with a situation in which you need to build controls (the result output) for which the associated data (the Web service result) doesn't yet exist. To take care of that, create a switch that you can control through a pair of cases and a toggle, as shown in Listing 3:
Listing 3. Add the switch
...
</xforms:instance>
</xforms:model>
<xforms:switch>
<xforms:case id="request">
<h2>Enter your zip code</h2>
<xforms:input ref="instance('guiInstance')//zip">
<xforms:label>Zip code: </xforms:label>
<xforms:hint>Enter a zip code and submit the form for the current
temperature in that area.</xforms:hint>
</xforms:input>
</xforms:case>
<xforms:case id="response">
<h2>And the result is...</h2>
The zip code you requested is:
<xforms:output ref="instance('guiInstance')//zip" />
</xforms:case>
</xforms:switch>
</body>
</html>
|
Only one case -- by default, the first -- can show at a time. To move to the second case, add a toggle as the action of a trigger, as shown in Listing 4:
Listing 4. Add the trigger
...
<xforms:case id="request">
<h2>Enter your zip code</h2>
<xforms:input ref="instance('guiInstance')//zip">
<xforms:label>Zip code: </xforms:label>
<xforms:hint>Enter a zip code and submit the form for the current
temperature in that area.</xforms:hint>
</xforms:input>
<xforms:trigger style="display:block">
<xforms:label>Get current temperature</xforms:label>
<xforms:action ev:event="DOMActivate">
<xforms:toggle case="response" />
</xforms:action>
</xforms:trigger>
</xforms:case>
|
The trigger is represented as a simple button, as you can see in Figure 2.
Figure 2: The trigger button
When the user clicks the button or otherwise activates the trigger, you get the second case, which I'll show you in a moment.
First, take a look at the second instance.
Add the second instance
Now you're ready to add the second instance to the page. Because the Web service is looking for XML, the instance is simply the message the Web service is looking for, as shown in Listing 5:
Listing 5. Add the second instance
...
<xforms:model id="WeatherService">
<xforms:instance id="messages">
<SOAP-ENV:Envelope
xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/1999/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/1999/XMLSchema">
<SOAP-ENV:Body>
<ns1:getTemp xmlns:ns1="urn:xmethods-Temperature"
SOAP-ENV:encodingStyle=
"http://schemas.xmlsoap.org/soap/encoding/">
<zipcode xsi:type="xsd:string">NO DATA SUBMITTED</zipcode>
</ns1:getTemp>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
</xforms:instance>
<xforms:instance id="guiInstance" xmlns="">
<inputData>
<zip>enter zip</zip>
</inputData>
</xforms:instance>
</xforms:model>
...
|
The important piece of this puzzle is the zipcode element, and you can see the state of it by adding a second output control to the response case, as in Listing 6:
Listing 6. Look at the data to submit
...
<xforms:case id="response">
<h2>And the result is...</h2>
The zip code you requested is:
<xforms:output ref="instance('guiInstance')//zip" />
<br />
The zip code you are going to send is:
<xforms:output ref="instance('messages')//zipcode" />
</xforms:case>
...
|
If you enter a zip code and click the trigger button, you can see the current state of things, as in Figure 3.
Figure 3. Monitor the data to submit
Clearly, this will not do. You need to get the requested data into the instance you want to submit.
Move the value into the submission instance
Simply put, when the user activates the trigger, you want to copy the data from the guiInstance zip element into the messages zipcode element. As it happens, that translates directly into the form:
Listing 7: Copy the value into the messages instance
...
<xforms:trigger style="display:block">
<xforms:label>Get current temperature</xforms:label>
<xforms:action ev:event="DOMActivate">
<xforms:setvalue ref="instance('messages')//zipcode"
value="instance('guiInstance')//zip" />
<xforms:toggle case="response" />
</xforms:action>
</xforms:trigger>
...
|
When the user activates the trigger, the form sets the value of the zipcode element in the messages instance to the value of the zip element of the guiInstance instance. To see this, reload the form and resubmit it (see Figure 4).
Figure 4. Set the value of the zipcode element
Now you just have to take care of the submission.
Submit the instance
To submit the message instance, create a submission element as part of the model and send it from the trigger, as shown in Listing 8:
Listing 8. Create a submission element
...
<xforms:instance id="guiInstance" xmlns="">
<inputData>
<zip>enter zip</zip>
</inputData>
</xforms:instance>
<xforms:submission id="getweather"
method="text-xml-post"
replace="instance"
ref="instance('messages')"
action="http://services.xmethods.net:80/soap/servlet/rpcrouter"
/>
</xforms:model>
<xforms:switch>
<xforms:case id="request">
...
<xforms:trigger style="display:block">
<xforms:label>Get current temperature</xforms:label>
<xforms:action ev:event="DOMActivate">
<xforms:setvalue value="instance('guiInstance')//zip"
ref="instance('messages')//zipcode"/>
<xforms:toggle case="response" />
<xforms:send submission="getweather"/>
</xforms:action>
</xforms:trigger>
</xforms:case>
<xforms:case id="response">
<h2>And the result is...</h2>
The temperature in zip code
<xforms:output ref="instance('guiInstance')//zip" />
is approximately
<xforms:output ref="instance('messages')//return" /> degrees.
</xforms:case>
</xforms:switch>
</body>
</html>
|
So you create the submission element, send it from the trigger, and extract the return value to display. As you can see in Figure 5, you have the benefit of access to the original data -- since it's still present in the original instance -- but you also have the ability to submit a separate instance to the service.
Figure 5. The end result
Summary
XForms are handy as Web services clients because of their ability to send and receive XML, but using the Web service request as your data instance can be cumbersome and inconvenient. This tip has explained how to create one instance for manipulating your data and a second for sending and receiving it. You learned about:
- Creating the basic form
- Creating both instances
- Moving data from once instance to the other before submission
- Submitting the request
- Receiving the response
Resources
About the author  | |  | Nicholas Chase, a Studio B author, has been involved in Web site development for companies such as Lucent Technologies, Sun Microsystems, Oracle, and the Tampa Bay Buccaneers. Nick has been a high school physics teacher, a low-level radioactive waste facility manager, an online science fiction magazine editor, a multimedia engineer, and an Oracle instructor. More recently, he was the Chief Technology Officer of an interactive communications firm in Clearwater, Florida, USA, and is the author of several books on Web development, including
XML Primer Plus
(Sams). He's currently trying to buy a farm so he and his wife can raise alpacas and chickens. He loves to hear from readers and can be reached at: nicholas@nicholaschase.com. |
Rate this page
|  |