Skyline Groningen
Toegevoegd op

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. Marc de Kwant wowww.nl