views:

554

answers:

2

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

+9  A: 

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 :).

viatropos
Can u provide me the complete... code i am getting some error
@viatropos: Thanks for the answer, I just leaned something new today :)
Leo Jweda
I just learned something new today [2] :)
MysticEarth
I should say GOD.... what an riposte... and a solution...
+1  A: 

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.

Leo Jweda
Thanks... it gave errors... let me work out it..
@theband: Feel free to post the errors.
Leo Jweda
Can we create components inside a Accordian... depending upon XML