Tuesday, May 19, 2009

Populating a Flex DataGrid Dynamically from an XMLList

Ok, so I've looked everywhere and couldn't find one succinct, easy to understand explanation for how to do this. So I'm going to take a stab at it.

The Problem
I have a web service that provides my Flex application with some XML. This XML represents a data table, and in my Flex application I want to populate a DataGrid with that XML, thus showing a visual representation of the data table. The problem is it is quite tricky to populate the DataGrid dynamically at run time. Most examples show you how to do it at compile time, that is, by specifying the columns and their fields in the code. My problem is that I don't know the column fields in advance, this is passed to me from the web service.

The Solution
The XML from the web service is in the following format:

<NewDataSet>
<Table1>
<column1>data1</column1>
<column2>data2</column2>
<column3>data3</column3>
</Table1>
<Table1>
...
</Table1>
</NewDataSet>

First, we create a global variable that will hold the contents of the XML sent from the web service. Here's mine:

[Bindable]
private var _xmlData:XMLList;

Now, we need to get the XML from the web service. First, I have a button that the user can click to connect to the web service. When the button is clicked, we activate the following function:

private function getTableData(event:MouseEvent):void
{
var service:WebService = new WebService();
service.addEventListener(ResultEvent.RESULT, serviceResultDataTableReceiver);
service.addEventListener(FaultEvent.FAULT, serviceFaultHandler);
service.loadWSDL("http://localhost:4753/PlayersService.asmx?wsdl");
service.getDataTable(tableComboBoxData.text());
}

Here 'getDataTable' is the web service method, which takes a parameter representating the ID of the table I want to retrieve from the database. That part isn't really important to you, it could be any web service method that sends your Flex application some XML.

To retreive the XML from the web service, I've attached an eventListener called 'serviceResultDataTableReceiver'. Here are the details for that function:

private function serviceResultDataTableReceiver(event:ResultEvent):void {
var tempXML:XML = XML(event.result);
_xmlData = tempXML.Table1 as XMLList;
dataGrid.dataProvider = _xmlData;
for each ( var node : XML in _xmlData[0].children() ) {
addDataGridColumn(node.name());
}
}

Here we get the result XML from the web service (tempXML) and convert it to an XMLList. That will be a DataProvider for our DataGrid. Once we have the XMLList, we need to add the columns dynamically, depending on how many child nodes we have (i.e.: column1, column2, and column3 in our XML example above.

Next, we need the function to add the columns to our DataGrid:

private function addDataGridColumn(dataField:String):void {
var dgc:DataGridColumn = new DataGridColumn(dataField);
var cols:Array = dataGrid.columns;
cols.push(dgc);
dataGrid.columns = cols;
dataGrid.validateNow();
}

Here we pass the name of the node to the function and create a new DataGridColumn with this name. We add this column to the array containing the DataGrid's columns ('dataGrid.columns'). validateNow() ensures the table is refreshed before the next column is added. If you don't do this, you may end up with a table that only has the most recently added column.

Finally, special thanks to the poster at The Blogger Guide for the tip on posting code snippets in Blogger posts!

No comments: