sid_com
sid_com

Reputation: 25137

Javascript: variable scope question

Why do I get here in the alert call an undef?

#!/usr/bin/env perl
use warnings;
use 5.014;
use utf8;
use Mojolicious::Lite;
use DBI;

my $db = 'my_test_db.db';
my $table = 'my_test_table';
my $dbh = DBI->connect( "dbi:SQLite:dbname=$db", '', '', 
            { RaiseError => 1, PrintError => 0, AutoCommit => 1, sqlite_unicode => 1, } 
        ) or die $DBI::errstr;
$dbh->do( "CREATE TEMP TABLE $table ( str TEXT, num INTEGER )" );
my $sth = $dbh->prepare( "INSERT INTO  $table ( str, num ) VALUES ( ?, ?)" );
$sth->execute( 'aaa', '111' );
$sth->execute( 'bbb', '222' );
$sth->execute( 'ccc', '333' );

get '/eingabe' => sub {
    my $self = shift;
    $self->render( 'eingabe' );
};

get '/search_db/:col' => sub {
    my $self = shift;
    my $col = $self->param( 'col' );
    my $term = $self->param( 'term' );
    my $sth = $dbh->prepare( "SELECT DISTINCT $col FROM $table WHERE $col LIKE ?" );
    $sth->execute( $term . '%');
    my $ref;
    while ( my $row = $sth->fetchrow_arrayref() ) {
            push @$ref, @$row;
    }
    $self->render( json => $ref );
};

app->start;

__DATA__
@@ eingabe.html.ep
<!DOCTYPE HTML>
<html>
<head>
<meta charset="UTF-8" />
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.16/jquery-ui.js"></script>
<script type="text/javascript">
    $( document ).ready( function() {
        var ids = [ 'str', 'num' ];
        for ( var i = 0; i < ids.length; i++ ){
            $( "#" + ids[i] ).autocomplete({
                source: function( request, response ){ 

                    alert( ids[i] );  // <---

                    $.getJSON( '/search_db/'  + ids[i], request, function( data_from_server ){
                        var suggestions = [];
                        var len = data_from_server.length;
                        for( var i = 0; i < len; i++ ){
                            suggestions.push( data_from_server[i] );
                        }
                        response( suggestions );
                    });
                }
            });
        }
    });
</script>
</head>
<body>
<form>
    <table>
        <tr><td>String:</td><td><input type="text" id="str" name="str"" /></td></tr>
        <tr><td>Number:</td><td><input type="number" id="num" name="num" /></td></tr>
    </table><br />
    <input type="submit" value="OK"/>
</form>
</body>
</html>

Upvotes: 0

Views: 130

Answers (3)

Ian Oxley
Ian Oxley

Reputation: 11056

It sounds like you're having the problem that's explained here: Creating closures in loops: A common mistake

The reason why you're getting undefined in your call to alert is that, when the autocomplete function is called, your for loop has finished executing meaning your loop variable i has the value of 2 i.e. ids.length + 1. Therefore ids[i] is the same as ids[2] which doesn't exist as you only have 2 elements in your ids array. I've tried to come up with a simple demo of this behaviour to help illustrate what's going on: http://jsfiddle.net/ianoxley/KwXVs/1/ (you'll need to have your browser's console open to see the results).

If you create an extra closure that should help preserve your scope and get rid of the undefined (see http://jsfiddle.net/ianoxley/BVa5Q/).

If you try changing your code to something like this hopefully it will get rid of the problem:

$(document).ready(function () {
    function initAutocomplete(element_id) {
        $("#" + element_id).autocomplete({
            source: function (request, response) {

                alert(element_id);  // <---

                $.getJSON('/search_db/' + element_id, request, function (data_from_server) {
                    var suggestions = [];
                    var len = data_from_server.length;
                    for (var i = 0; i < len; i++) {
                        suggestions.push(data_from_server[i]);
                    }
                    response(suggestions);
                });
            }
        });        
    }

    var ids = ['str', 'num'];
    for (var i = 0; i < ids.length; i++) {
        var current_id = ids[i];
        initAutocomplete(current_id);
    }
});

Hope this helps.

Upvotes: 3

arychj
arychj

Reputation: 711

You source callback function is out of scope. When you attack a function to an event link that it gets called when the event is triggered which is after your for loop has completed.

A good rule of thumb whenever your are using callbacks is to always pass in everything that that function requires or attach everything that object requires to the window object and back it global.

Upvotes: 0

Dennis
Dennis

Reputation: 14495

You are defining the i iteration variable twice: it is used inside of your callback to $.getJSON call again.

you should rename that one to a different name, j (just an example).

so it would say:

$( document ).ready( function() {
    var ids = [ 'str', 'num' ];
    for ( var i = 0; i < ids.length; i++ ){
        $( "#" + ids[i] ).autocomplete({
            source: function( request, response ){ 

                alert( ids[i] );  // <---

                $.getJSON( '/search_db/'  + ids[i], request, function( data_from_server ){
                    var suggestions = [];
                    var len = data_from_server.length;
                    for( var j = 0; j < len; j++ ){
                        suggestions.push( data_from_server[j] );
                    }
                    response( suggestions );
                });
            }
        });
    }
});

i hope this helps!

Upvotes: 0

Related Questions