views:

126

answers:

3

I'm trying to pass the current value of a variable when an a dynamically generated navigation 'node' is clicked. This needs to just be an integer, but it always results in the last node's value.. have tried some different methods to pass the value, a custom event listener, a setter, but I suspect it's a closure problem.. help would be appreciated ;-)

function callGrid():void {

    for (var i:Number = 0; i < my_total; i++) {

        var gridnode_url = my_grid[i].@gridnode;
        var news_category= my_grid[i].@category;
        var newstitle = my_grid[i].@newstitle;
        var news_content = my_grid[i]..news_content;
        var news_image = my_grid[i]..news_image;

        var gridnode_loader = new Loader();
        container_mc.addChild(gridnode_loader);
        container_mc.mouseChildren = false;
        gridnode_loader.load(new URLRequest(gridnode_url));
        gridnode_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, gridLoaded);
        gridnode_loader.name = i;

        text_container_mc = new MovieClip();
        text_container_mc.x = 0;
        text_container_mc.mouseEnabled = false;
        var textY = text_container_mc.y = (my_gridnode_height+18)*y_counter;
        addChild(text_container_mc);
        var tf:TextSplash=new TextSplash(newstitle,10,0,4 );
        container_mc.addChild(tf);
        tf.mouseEnabled = false;
        tf.height = my_gridnode_height;
        text_container_mc.addChild(tf); 
        var text_container_mc_tween = new Tween(text_container_mc, "alpha", Strong.easeIn, 0,1,0.1, true);

        gridnode_loader.x = (my_gridnode_width+5) * x_counter;
        gridnode_loader.y = (my_gridnode_height+15) * y_counter;

        if (x_counter+1 < columns) {
            x_counter++;
        } else {
            x_counter = 0;
            y_counter++;
        }
    }
}
function gridLoaded(e:Event):void {
    var i:uint;
    var my_gridnode:Loader = Loader(e.target.loader);
    container_mc.addChild(my_gridnode);
    _xmlnewstarget = my_gridnode.name;

//||||||||||||||||||||||||||||||||||||||||   
//when a particular grid node is clicked I need to send the current _xmlnewstarget value to the LoadNewsContent function...
//||||||||||||| ||||||||||||||||||||||||

    my_tweens[Number(my_gridnode.name)]=new Tween(my_gridnode, "alpha", Strong.easeIn, 0,1,0.1, true);
    my_gridnode.contentLoaderInfo.removeEventListener(Event.COMPLETE, gridLoaded);
    my_gridnode.addEventListener(MouseEvent.CLICK, loadNewsContent);

}

function loadNewsContent(e:MouseEvent):void {
    createNewsContainer();
    getXMLNewsTarget();
    news_category = my_grid[_xmlnewstarget].@category;
    var tfnews_category:TextSplash=new TextSplash(news_category,20,16,32,false,false,0xffffff );
    tfnews_category.mouseEnabled = false;

    newstitle = my_grid[_xmlnewstarget].@newstitle;
    var tftitle:TextSplash=new TextSplash(newstitle,20,70,24,false,false,0x333333 );
    news_container_mc.addChild(tftitle);
    tftitle.mouseEnabled = false;

    news_content = my_grid[_xmlnewstarget]..news_content;
    var tfnews_content:TextSplash=new TextSplash(news_content,20,110,20,true,true,0x333333,330);
    news_container_mc.addChild(tfnews_content);
    tfnews_content.mouseEnabled = false;
    news_image = my_grid[_xmlnewstarget].@news_image;
    loadNewsImage();
    addChild(tfnews_category);
    addChild(tftitle);
    addChild(tfnews_content);

    var news_container_mc_tween = new Tween(news_container_mc, "alpha", Strong.easeIn, 0,1,0.3, true);
    news_container_mc_tween.addEventListener(Event.INIT, newsContentLoaded);
}
+4  A: 

I'm not going to try to read your code (try to work on your formatting, even if it's just indenting), but I'll provide a simplified example:

for (var i = 0; i < my_total; i++) {
    var closure = function() {
        // use i here
    }
}

As you say, when closure is called it will contain the last value of i (which in this case would be my_total). Do this instead:

for (var i = 0; i < my_total; i++) {
    (function(i) {
        var closure = function() {
            // use i here
        }
    })(i);
}

This creates another function inside the loop which "captures" the current value of i so that your closure can refer to that value.

See also JavaScript: Why the anonymous function wrapper? for further similar examples.

Greg Hewgill
So in summary :) Scopes *and not* variables, are "closured" in Javascript and only a new function declaration can create a new scope.
pst
Thanks Greg for the closure function, that really helps. I've added the code, made sure the type conversion is correct and added a setter for the _xmlnewstarget variable. I've created the perfect roulette wheel! The values change for the 5 nodes 24130, 01432, 04312, 02143, 02143, 14302, 02143, 21403, 43021, 24310, 02143, 14302, 02143, 43021... the last value being the the one for _xmlnewstarget...
martin
I managed to sort this out eventually by a different route.. I added a news id in the xml for each news item, referenced this to the text container - text_container_mc.id = my_grid[i].@id; I then jumped straight to loadNewsContent with text_container_mc.addEventListener(MouseEvent.CLICK, loadNewsContent); and then in that function linked the textfields using the e.target.id - newstitle = my_grid[e.target.id].@newstitle;Now I just have to sort out reverse navigation.. but I think this problem is sorted ;-) thanks
martin
A: 

Umm, as mentioned above, the code is a bit dense, but I think you might have a bit of type conversion problem between string and integers, is the "last value" always 0? try making these changes and let me know how you get on.

// replace this gridnode_loader.name = i;
gridnode_loader.name = i.toString();

// explictly type this as an int
_xmlnewstarget = parseInt(my_gridnode.name);

// replace this: my_tweens[Number(my_gridnode.name)] = new Tween(......
my_tweens[parseInt(my_gridnode.name)] = new Tween();

Oh and I think it goes without saying that you should massively refactor this code block once you've got it working.

Edit: after further study I think you need this

//replace this: my_gridnode.addEventListener(MouseEvent.CLICK, loadNewsContent);

var anonHandler:Function = function(e:MouseEvent):void
{
 loadNewsContent(_xmlnewstarget);

};
my_gridnode.addEventListener(MouseEvent.CLICK, anonHandler);

Where your loadNewsContent has changed arguements from (e:MouseEvent) to (id:String)

sorry about the code layout, I'll try and incorporate your suggestions asap ;-)
martin
A: 

Firstly, you do not need to call addChild for the same loader twice (once in callGrid) and then in (gridLoaded). Then you can try putting inside loadNewsContent:
news_category = my_grid[int(e.target.name)].@category;
instead of
news_category = my_grid[_xmlnewstarget].@category;
As _xmlnewstarget seems to be bigger scope, which is why it is getting updated every time a load operation completes.

bhups
thanks for that, I'll do some testing as soon as possible
martin