Random
Random

Reputation: 97

Passing event listener in to event listener?

I have a tableView that is populated from a remote source. I add several buttons on each row dynamically, buttons and content of each row are in a for loop. I have an alert dialog box listener within the button listener. Once this appears if confirm (YES) is selected I want to be able to change the button title.

I receive the following error:

        Uncaught TypeError: Cannot set property 'title' of undefined.

This is the source of the error:

        btn[i].title = "Finish" 

Sorry I don't have access to the code at the moment and haven't gave much to work with but any help/ideas would be greatly appreciated...

EDIT:

Dummy code:

        var btn = new Array();

        btn[i] = Ti.UI.createButton({
            backgroundImage: '/images/button.png',
            title:'Start',
            top: 0,
            left: 0,
            height: '20%',
            width: '20%'
        });

        btn[i].addEventListener('click', function(e){
            var alert = Titanium.UI.createAlertDialog({
                title : 'Dialog',
                message : 'Change Title',
                buttonNames : ['Yes', 'No']
            });
            alert.addEventListener('click', function(e) {
                if(e.index == 0) {
                    btn[i].title = "Finish";
                }
            });
            alert.show();
        });                                         
        tableViewRow.add(btn[i]);

Upvotes: 1

Views: 121

Answers (2)

Michael Geary
Michael Geary

Reputation: 28850

Somewhere you have a for loop like this:

for( i = 0;  i < btn.length;  i++ ) {
    // do stuff with btn[i]
}

Change it to:

for( i = 0;  i < btn.length;  i++ ) {
    addButton( btn[i] );
}

function addButton( button ) {
    // do stuff with button
}

The body of the addButton() function will be all of the code that was in your for loop with one difference: everywhere you have btn[i] change it to button.

By moving this code into a function, you create a "closure" which preserves the value of the button variable as long as needed, unlike the original code where btn[i] becomes invalid after the loop finishes running.

Remember that event listeners are called long after the original code that sets them up finishes. If you use a loop index in the event listener, that index value is not the one you expect. The closure fixes this in a very clean and simple way.

You mentioned in a comment that you need the loop index as well. In that case you could do this:

for( i = 0;  i < btn.length;  i++ ) {
    addButton( i );
}

function addButton( i ) {
    // do stuff with btn[i]
}

In fact, now you don't have to change any of the code in the loop body/function body. You can still use btn[i] as before.

Or, you can do it this way:

for( i = 0;  i < btn.length;  i++ ) {
    addButton( button, i );
}

function addButton( button, i ) {
    // do stuff with button (instead of btn[i]) and i where needed
}

Now you can replace all the btn[i] references inside the addButton() function with button as in the first example, and you still have i available when you need it.

Upvotes: 1

GameAlchemist
GameAlchemist

Reputation: 19294

you are creating a closure on i within a for loop. When the loop exit, all created functions will have a closure on the very same var i, which value will be "length" for all functions, hence the undefined.

Upvotes: 0

Related Questions