Search code examples
javascriptapache-flexe4x

Editing XML in Flex using e4x


In Flex, I have an xml document such as the following:

var xml:XML = <root><node>value1</node><node>value2</node><node>value3</node></root>

At runtime, I want to create a TextInput control for each node under root, and have the values bound to the values in the XML. As far as I can tell I can't use BindingUtils to bind to e4x nodes at runtime (please tell me if I'm wrong here!), so I'm trying to do this by hand:

for each (var node:XML in xml.node)
{
    var textInput:TextInput = new TextInput();
    var handler:Function = function(event:Event):void 
    {
        node.setChildren(event.target.text);
    };
    textInput.text = node.text();
    textInput.addEventListener(Event.CHANGE, handler);
    this.addChild(pileHeightEditor);
}

My problem is that when the user edits one of the TextInputs, the node getting assigned to is always the last one encountered in the for loop. I am used to this pattern from C#, where each time an anonymous function is created, a "snapshot" of the values of the used values is taken, so "node" would be different in each handler function.

How do I "take a snapshot" of the current value of node to use in the handler? Or should I be using a different pattern in Flex?


Solution

  • The closure only captures a reference to the variable, not its current value. Since local variables are Function-scoped (not block-scoped) each iteration through the loop creates a closure that captures a reference to the same variable.

    You could extract the TextInput creation code into a separate function, which would give you a separate variable instance to capture for the closure. Something like this:

    for each (var node:XML in xml.node)
    {
        var textInput:TextInput = createTextInput(node);
        this.addChild(pileHeightEditor);
    }
    ... 
    
    private function createTextInput(node:XML) : TextInput {
        var textInput:TextInput = new TextInput();
        var handler:Function = function(event:Event):void 
        {
            node.setChildren(event.target.text);
        };
        textInput.text = node.text();
        textInput.addEventListener(Event.CHANGE, handler);
        return textInput;
    }