Building dynamic Flex forms
Why? is the first question that comes to mind. Why should we dynamicly build a form in a flex application? One answer can be: “The need to create questionaires that dynamicly change over time.”
With this in mind I started to roam the web, hoping to find some examples that would help me create this. No such luck. Hardly any information can be found. The only information I could find was in the adobe livedocs, and this was only about creating UIComponents with only the classname available.
For me thats enough to build my application. This article will describe a method of creating dynamic forms and form components depending on a xml file.
The project consists of 2 parts. 1 part is a piece of serverside code (java in my case) that serves the xml and recieves the output of the form. The xml is a static file, no need to explain how this works. My xml looks like this:
<?xml version=”1.0″ encoding=”UTF-8″?>
<questionaire>
<questions>
<question identifier=”q1″>
<type>InputText</type>
<questionText>What is the meaning of life?</questionText>
<answers/>
</question>
</questions>
</questionaire>
A simple canonial xml file defining the questions and its potential answers.
A simple canonial xml file defining the questions and its potential answers.
On the recieving end is a java servlet active that recieves the submit from the flex form and displays the result in the logging. The servlet looks like this.
package nl.wowww.enquete;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author marc de kwant, wowww.nl
*
*/
public class EnqueteReciever extends HttpServlet {
/**
* Generated serialversion id..
*/
private static final long serialVersionUID = -5816406766507179261L;
private static Log log = LogFactory.getLog(EnqueteReciever.class);
/* (non-Javadoc)
* @see javax.servlet.http.HttpServlet#doGet(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.doPost(request,response);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServlet#doPost(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
this.service(request, response);
}
/* (non-Javadoc)
* @see javax.servlet.http.HttpServlet#service(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
*/
@SuppressWarnings(“unchecked”)
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
log.debug(“Enquete reciever executed… showing parameters:”);
response.setContentType(“text/html”);
PrintWriter out = response.getWriter();
out.println(“<?xml version=\”1.0\” encoding=\”UTF-8\”?>”);
out.println(“<requestParams>”);
Map<String,String[]> parameters = request.getParameterMap();
for(String key: parameters.keySet()) {
String[] value = parameters.get(key);
String valuePair = key+” – “;
out.println(“<pair>”);
out.print(“<key>”+key+”</key>”);
out.println(“<values>”);
for (int i=0;i<value.length;i++) {
out.println(“<value>”+value[i]+”</value>”);
valuePair +=”["+value[i]+”] “;
}
out.println(“</values>”);
log.debug(valuePair);
out.println(“</pair>”);
}
out.println(“</requestParams>”);
}
}
Note that my servlet returns an valid xml file containing the parameters recieved .This must be a valid xml file, otherwise the flex application will generate a error on runtime.
These 2 files together with some supporting files are put into a war and deployed on a serlvet container. I use jboss, but any will do.
This is the complete servlet part of the application. Now we are set to build the UI side of the application. This is for this example a single mxml file containing all code. You can use and expand on this at your own leisure.
The mxml has a small piece of mx xml and 3 actionscript functions. First of all we define the basic mxml file with the bare mx components we need to facilitate the communication with the serverside components. This is the basic file:
<?xml version=”1.0″ encoding=”utf-8″?>
<mx:Application xmlns:mx=”http://www.adobe.com/2006/mxml” layout=”absolute” initialize=”initialize=”initApp();enqueteDefinition.send()”">
public function initApp():void {
this.addEventListener(FlexEvent.APPLICATION_COMPLETE,createControls);
}
public function createControls(event:Event):void {
createControl(enqueteDefinition.lastResult.questionaire.questions.question);
}
<mx:HTTPService id=”httpSrv” url=”http://localhost:8080/relatie-enquete-0.1/EnqueteReciever”/>
<mx:HTTPService id=”enqueteDefinition” url=”http://localhost:8080/relatie-enquete-0.1/enqueteDefinition.xml”/>
<mx:Form x=”121″ y=”85″ width=”347″ height=”143″ id=”idForm”>
<mx:Button label=”Button”/>
</mx:Form>
</mx:Application>
As you can see, it is a simple form with a button, 2 httpservice calls. 1 to get the xml definition file of the questionaire and 1 to send the resulting data towards.
To generate UIform elements, we need to parse the xml and use the information to create the nessesary components. For this we use a actionscript function createControl( ). A simple function which can create controls in a form. See the function below:
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;
import mx.core.UIComponent;
private function createControl(obj:Object ): void
{
if (obj != null) {
var className:String = obj.flexComponent;
var objClass:Class = getDefinitionByName( className ) as Class;
if( objClass != null ) {
var newObject:UIComponent = UIComponent( new objClass() );
if( newObject.hasOwnProperty( “label” ) ) {
newObject[ "label" ] = newObject.name + ” [" + className + "]“;
}
if( newObject.hasOwnProperty(“id”)) {
newObject["id"] = obj.identifier;
}
// Give the object a proper name…
newObject.name = obj.identifier;
idForm.addChild( newObject );
}
}
}
This function takes the results from the xml questionaire definition file and uses it to create an inputtext component. This example creates only 1 component, but i leave it up to your imagination to create more.
The component is displayed and you can enter text into it. When you click on the button, the information will be send back to the server and logged on the console. The function that sends the information back to the server looks like this:
public function callService():void {
// Cancel all previous pending calls.
httpSrv.cancel();
var params:Object = getParamsForForm();
httpSrv.send(params);
}
private function getParamsForForm ():Object {
var values:Object = new Object();
var formItems = idForm.getChildren();
// loop items and add values
for (var i:int = 0; i < formItems.length; i++) {
if (formItems[i] instanceof mx.controls.TextInput) {
values[formItems[i].name] = formItems[i].text;
} else if (formItems[i] instanceof mx.controls.CheckBox) {
if (formItems[i].selected == true) {
values[formItems[i].id] = formItems[i].label;
}
}
// etc.. voor alle type input controls van het formulier
}
return values;
}
The above callService() function is activated when you press the button. This will call the privat method getParamsForForm and process the form information dynamicly. The result is sended to the serverside java servlet for processing. Which in our example is just logging into the console.
This together with a event to handle the sequencing (we must wait with creating controls unitl the xml in fully read into memory clientside), you have a small example on how to dynamicly create and send questionaire data from and to the server.
Regards.
wowww.nl
Gepost in categorie: TechTalk
Tweet
I’m having a similar situation. Thanks for writing this!
However, regarding flex, I was expecting a nicer mechanism to handle situations like this. For instance, mx:HttpService/mx:request could take a mx:Form. Most of the time the bindings are one to one, so this way we could avoid extra bindings between mx:request and mx:Form.
5 december 2007 om 12:13This is a really useful post; one of the only ones I could find about something that really, I would have imagined more people would want to do!
I am in the middle of writting a Flex app which uses a .Net webservice layer to talk to SalesForce.com Apex API and retrieve metadata about our business objects and then translates the XML into a sign-up form (kind of bypassing their Web-To-Lead solution)
I am currently trying to prove that the form generation can work on the fly against an xml document and I’m running into a really odd problem. I am calling a WebService, which calls a ColdFusion CFC method as a SOAP call. That then returns an XML packet (the one listed in your post actually).
It all works fine calling the WebService and getting back the response, so if I do:
Alert.show(WS.GetExhibitorForm.lastResult.toString())
… it shows me the following:
InputText
What is the meaning of life?
… but as soon as I try and navigate further down the XML nodes, it can’t find anything (returns an empty string)… e.g:
Alert.show(WS.GetExhibitorForm.lastResult.questionaire.questions.question.toString());
…that just shows an empty alert box.
It lists the type of response as XMLList if I try and assign it to a variable (I found this my looking at the implicit coerce not allowed error)… so I don’t understand why I can’t use it like an XML document.
If you have any ideas I would be eternally grateful.
7 december 2007 om 11:38sorry, the soap response didn’t print out correctly there:
InputText
7 december 2007 om 11:39What is the meaning of life?
Hi,
I see that you tried to implement what I blogged about. Did you get passed your problem already?
kind regards,
7 maart 2008 om 03:45Marc
This is an interesting solution, and I have implemented something very similar.
However, has anyone tried using the FormItem component and validators? It seems like a massive waste of effort to not use those built-in components — especially the validators!
17 april 2008 om 21:02this helped.. thanks!
24 april 2008 om 10:53Hello,
This is a good post regarding dynamic generation of form.
I have a similar form with this approach. One issue I am facing with this approach is I have a edit option and I need to pass values to this form, so that user could edit the values he entered using the same form.
Any help is highly appreciable.
Thanks
17 september 2008 om 03:10I have built a reusable component I call SmartForm. You basically pass it an XML descriptor and a data object and it generates a form on the fly using elements in the descriptor. It handles the setup of bindings between the generated FormItems and the data object so that data entered by the user is correctly updated on your data object. It also supports a range of validations driven by the descriptor. What’s especially cool is that the form can even by changed dynamically at runtime by changing the XML!
If I can get my employer to agree, I hope to release it into the wild as open source soon.
11 november 2008 om 00:18Cool. Please let me know when it is abailable.
12 november 2008 om 11:43What do you think about error handling of the form? Will you do that on the client or at the server side?
26 december 2008 om 08:37Wow! This was exactly what i was looking for ….. Thanks a lot for this! saved my ass
It seems that instanceof in “formItems[i] instanceof mx.controls.CheckBox” is deprecated in AS3. You can use the “as” keyword instead. So it will be
“formItems[i] as mx.controls.CheckBox” instead.
4 februari 2009 om 21:05This example is awesome. It saved lots of my time. Can anyone help me with example to add hidden form fields to Flex form?
23 oktober 2009 om 09:46I do not see the definition for this referenced xml in your code:
http://localhost:8080/relatie-enquete-0.1/enqueteDefinition.xml
Is it possible that you can bundle up your solution into a zip and add it here for others to look at?
This is great if I could only get it to work.
4 maart 2010 om 10:35Hi Brett Adam,
Great!
Could you please post the SmartForm module which you have developed if it is made open source??
Am also in same channel of developing a dynamic user interface creation using Flex.
Thanks a lot.
5 mei 2010 om 06:33подростковый половой член
24 oktober 2010 om 05:18Three years later, with a newer version of flex out – is this still the way to go, using xml to drive the form?
15 april 2011 om 11:58In essence it is a technique to develop dynamic forms. Flex is just my front end solution. I could also have been Android, or JSP’s
16 april 2011 om 08:12