I have a XML and it has an attribute option or combo box, parsing that i need to create components in my flex dynamically.
Viatropos has given a wonderful code, but i am not able to execute it... can anyone produce it... thanks
I have a XML and it has an attribute option or combo box, parsing that i need to create components in my flex dynamically.
Viatropos has given a wonderful code, but i am not able to execute it... can anyone produce it... thanks
You can create components in Flex dynamically with something like this:
Sample Data
<?xml version="1.0" encoding="UTF-8"?>
<components type="array">
<component type="mx.controls.ComboBox">
<width>100</width>
<height>100</height>
<color isStyle="true">0xff0000</color>
<prompt>Im a Combo Box!</prompt>
</component>
<component type="mx.controls.Button">
<width>100</width>
<height>100</height>
<color isStyle="true">0xff0000</color>
<label>Im a Button!</label>
</component>
</components>
Sample App:
<?xml version="1.0" encoding="utf-8"?>
<mx:Application
xmlns:mx="http://www.adobe.com/2006/mxml"
creationComplete="creationCompleteHandler()">
<mx:Script>
<![CDATA[
import flash.display.DisplayObject;
import mx.core.UIComponent;
import mx.controls.ComboBox; ComboBox;
protected function creationCompleteHandler():void
{
var components:Array = getComponentsFromXML(xml.component);
var i:int = 0;
var n:int = components.length;
for (i; i < n; i++)
{
panel.addChild(components[i] as DisplayObject);
}
}
/**
* Parses an XML string, returns array of new components.
*/
public function getComponentsFromXML(components:XMLList):Array
{
var result:Array = [];
var child:Object;
var component:UIComponent;
var type:String;
var clazz:Class;
var i:int = 0;
var n:int = components.length();
for (i; i < n; i++)
{
child = components[i];
type = child.@type;
try {
clazz = flash.utils.getDefinitionByName(type) as Class;
} catch (error:ReferenceError) {
traceImportError(type);
}
component = new clazz(); // dynamic
var properties:XMLList = child.elements();
var property:XML;
var name:String;
var value:Object;
// for each child node
for each (property in properties)
{
name = property.localName();
value = property.toString();
// create a more generic method to convert
// strings to numbers and whatnot
// this is a regular expression matching any digit
// check out rubular.com
if (/\d+/.test(value.toString()))
value = Number(value);
if (property.attribute("isStyle") == "true")
component.setStyle(name, value);
else
component[name] = value;
}
result.push(component);
}
return result;
}
protected function traceImportError(type:String):void
{
trace("Please include the class '" + type + "' in the swf.");
var names:Array = type.split(".");
var last:String = names[names.length - 1];
trace("import " + type + "; " + last + ";");
}
]]>
</mx:Script>
<!-- sample data -->
<mx:XML id="xml" source="components.xml" />
<!-- sample container -->
<mx:Panel id="panel" width="100%" height="100%"/>
</mx:Application>
As long as you have a defined structure for your XML, you could create an XMLUtil for processing xml generically (getting all properties, for example, or converting strings to their correct type), and a ComponentManifest class that took an XML file and converted it into components.
You also need to make sure all of the classes defined in XML are imported into the swf, or else it will throw that error. You can do that like so:
import mx.controls.ComboBox; ComboBox;
A straight import, without that second ComboBox
won't do it.
This should get you started, fill it out as needed!
Check out Rubular to mess around with Regular Expressions if you want to have better/advanced xml value parsing :).
Here's the modified version of @viatropos's solution (I would've put it as a comment but it's too long for a comment and it won't look good).
Sample Data:
<?xml version="1.0" encoding="UTF-8"?>
<components type="array">
<component type="mx.controls::ComboBox">
<width>100</width>
<height>100</height>
<color isStyle="true">"0xff0000"</location>
<label>"Im a Combo Box!"</label>
</component>
<component type="mx.controls::Button">
<width>100</width>
<height>100</height>
<color isStyle="true">"0xff0000"</location>
<label>"Im a Button!"</label>
</component>
</components>
Here apparently there was a mistake where was used to end the tag.
I also added quotation marks around all the string values to make it easier to identify them.
Sample (pseudo) Method: createComponentsFromXML(xml.components)
public function createComponentsFromXML(components:XMLList):void
{
var child:Object;
var component:UIComponent;
var i:int = 0;
var n:int = components.length();
for (i; i < n; i++)
{
child = components[i];
var clazz:Class = flash.utils.getDefinitionByName(child.@type);
component = new clazz(); // dynamic
var property:Object;
var value:Object;
var useIntVal:Boolean;
var intVal:int;
// for each child node
for (property in child.children())
{
useIntVal = false;
value = property.toString();
if(!(value.substr(1, 2) == '"' AND value.substr(-1, value.length()) == '"')) {
useIntVal = true;
intVal = parseInt(value);
}
// button["width"] = 100;
if (property.attribute("isStyle") == "true")
if(useIntVal) {
component.setStyle(property.localName(), intVal);
} else {
component.setStyle(property.localName(), value);
}
else {
if(useIntVal) {
component[property.localName()] = intVal;
} else {
component[property.localName()] = value;
}
}
}
}
}
I implemented the conversion to int making sure I check whether the property is supposed to be a string or an int.
Hope this helps, if you need any more help just let me know.
God bless.
PS: I don't have Flex installed right now so you may find a few error's you need to correct.
[Edit:] In this case you may want to have your xml like this:
<?xml version="1.0" encoding="UTF-8"?>
<components type="array">
<component type="mx.controls::ComboBox">
<properties>
<width>100</width>
<height>100</height>
<color isStyle="true">"0xff0000"</location>
<label>"Im a Combo Box!"</label>
</properties>
</component>
<component type="mx.controls::Button">
<properties>
<width>100</width>
<height>100</height>
<color isStyle="true">"0xff0000"</location>
<label>"Im a Button!"</label>
</properties>
<children>
<!--other children here-->
</children>
</component>
</components>
'cause I doubt you can do much with one level of nesting in an accordion.
I'll leave implementing the function to you.