Subscribe

You are currently browsing the archives for the Flex category.

Archives

  • Categories

  • License

    Creative Commons License


    All work on this site, excepting software and unless otherwise noted, is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported License.




    Archive for the ‘Flex’ Category

    Embedding HTML in XML Data Bound to Flex Components

    Friday, May 1st, 2009

    So with school officially out of the way for the year and a little time left before work begins, I dusted off my copy of Flex 3 the other day to re-learn the basics for a vague idea I had for an RI app. Flex seemed to have some pretty solid means by which to serve up and display data from a database fairly well, so I set out to try it out. The way it works is by POSTing a request to a simple server script (in this case written in PHP) which queries a specific backend database table, returning the list of records in the table as XML. Adding new records to the table and modifying existing ones are accomplished in exactly the same way, just with slightly modified server scripts for each operation. Using the data binding features that Flex provides, hooking up to this data and displaying it in a DataProvider is a peice of cake thanks to the HttpService class (there are plenty of examples of this process on the ‘net).

    The problem arose, however, when I wanted to save rich text in HTML format to a specific field in the database. The rich text would save properly, but it would not redisplay since the tags were interpreted as XML themselves; this thoroughly confuses Flex which normally attempts to bind XML straight into a collection of objects for simple programmatic access. Here’s a screenshot of the raw server responses that it was choking over:

    HTML within XML: A Messy Parsing Problem

    Instead of the HTML text between the <description></description> tags getting displayed properly, the cell showed a frustrating value of “[Object object],” with my HTML nowhere in sight (see screenshot below).

    Where's My HTML!?

    The solution for this is to bypass object binding — at least temporarily — and control the HTML before its containing node gets bound to an object. This involves traversing the XML structure as much as we dare; that is, up to the parent node and from there grabbing its internal text. Finally, using the ObjectProxy class, we can bind the HTML as the value to an XML node… converted to an object. In code, this looks like the following (notice that serv_result() is a callback for HttpServer’s ResultEvent):

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    
    <mx:Script>
          <![CDATA[
    		[Bindable]
    		private var _noteList:ArrayCollection;
     
    		private function serv_result(evt:ResultEvent):void {
    			var xmlStr:String = evt.result.toString();
    			var xmlDoc:XMLDocument = new XMLDocument(xmlStr);
    			var note:ObjectProxy;
     
    			_noteList = new ArrayCollection();
     
    			for each (var n:XMLNode in xmlDoc.childNodes[0].childNodes) {
    				var szDescription:String = n.childNodes[1].toString();
     
    				var indexFirstBracket:Number, indexLastBracket:Number;
    				indexFirstBracket = indexLastBracket = -1;
     
    				indexFirstBracket = szDescription.indexOf(">");
    				if (indexFirstBracket != -1) {
    					szDescription = szDescription.substr(indexFirstBracket+1, szDescription.length-indexFirstBracket-1);
    				}
    				indexLastBracket = szDescription.lastIndexOf("<");
    				if (indexLastBracket != -1) {
    					szDescription = szDescription.substr(0, indexLastBracket);
    				}
     
    				var next:Object = { title:szTitle,
    									description:szDescription,
    									question:szQuestion,
    									startdate:szStartdate,
    									enddate:szEnddate,
    									imageurl:szImageurl};
     
    				note = new ObjectProxy(next);
    				_noteList.addItem(note);
    			}
     
    			notes.dataProvider = _noteList;
    		}
     
    		private function initCellRenderer():void {
    			description.itemRenderer = new ClassFactory(HtmlRenderer);
    		}
    	]]>
    </mx:Script>
     
    <mx:DataGrid id="notes" x="10" y="46" height="544" width="780" initialize="initCellRenderer()" change="updateButtons()">
    	<mx:columns>
    		<mx:DataGridColumn headerText="Content" id="description" dataField="description" width="250" />
    	</mx:columns>
    </mx:DataGrid>

    You can see that we are managing a small step of the binding process ourselves to prevent Flex from parsing the HTML and trying to bind it to an object (which fails and just results in the “[Object object]” text). Note that in order for this to work you must set the resultFormat property of your instance of HttpService to “xml” (the default is “object”).

    As you may have guessed from the initCellRenderer() function, a custom cell rendering component is necessary in order to display an HTML-capable widget, but this is the most trivial part of the process:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    // HtmlRenderer.mxml
    <?xml version="1.0" encoding="utf-8"?>
    <mx:VBox xmlns:mx="http://www.adobe.com/2006/mxml" width="100%" height="140" 
    	paddingBottom="5" paddingLeft="5" paddingRight="5" paddingTop="5">
    <mx:Script>
    	<![CDATA[
    		import mx.controls.Alert;
    	]]>
    </mx:Script>
    	<mx:TextArea id="htmltext" htmlText="{data.description}" height="100%"
    		width="100%" backgroundAlpha="0" focusAlpha="0" wordWrap="true"
    		borderThickness="0"/>
    </mx:VBox>

    So Close

    The results were better, but not quite perfect. As you can see from the capture above, the HTML was still getting “beautified” by the XML parser. In order to convince it to leave the HTML data alone, I ended up wrapping the HTML with quotes just prior to sending it to the database, and then removing those quotes before setting an HTML widget to its contents. The final results:

    Final Results

    There you have it — embedded HTML in XML, bound to a Flex data view.