18

I am getting the following error when I attempt to get a typeahead values from AngularUI-Bootstrap, using a promise.

TypeError: Cannot read property 'length' of undefined
  at http://localhost:8000/static/js/ui-bootstrap-tpls-0.6.0.min.js:1:37982
  at i (http://localhost:8000/static/js/angular.min.js:79:437)
  at i (http://localhost:8000/static/js/angular.min.js:79:437)
  at http://localhost:8000/static/js/angular.min.js:80:485
  at Object.e.$eval (http://localhost:8000/static/js/angular.min.js:92:272)
  at Object.e.$digest (http://localhost:8000/static/js/angular.min.js:90:142)
  at Object.e.$apply (http://localhost:8000/static/js/angular.min.js:92:431)
  at HTMLInputElement.Va.i (http://localhost:8000/static/js/angular.min.js:120:156)
  at HTMLInputElement.x.event.dispatch (http://localhost:8000/static/js/jquery-1.10.2.min.js:5:14129)
  at HTMLInputElement.v.handle (http://localhost:8000/static/js/jquery-1.10.2.min.js:5:10866) 

My HTML tag is:

<input type="text" class="form-control" id="guestName" ng-model="name" typeahead="name for name in getTypeaheadValues($viewValue)">

With my getTypeaheadValues function doing the following:

$scope.getTypeaheadValues = function($viewValue)
{
    // return ['1','2','3','4'];

    $http({
        method: 'GET',
        url: 'api/v1/person?name__icontains=' + $viewValue
    }).error(function ($data) {
        console.log("failed to fetch typeahead data");
    }).success(function ($data) {
        var output = [];
        $data.objects.forEach(function (person)
        {
            output.push(person.name);
        });
        console.log(output);
        return output;
    });
}

I do not understand what AngularUI-Bootstrap is complaining about as being undefined. If I remove the comment on the top-most return the values show up fine. The console.log output in the success also return all the values I'm expecting in an array.

What am I missing that would cause AngularUI-Bootstrap not see the returned array?

Nicholas Pappas
  • 10,439
  • 12
  • 54
  • 87

3 Answers3

22

This problem was two fold.

The first is that I was not returning the promise event (the $http call). The lack of a return statement (as @tobo points out) is what was causing the error directly. I needed to be returning the promise though, not the array.

The second is that I need to be using .then rather than .success for AngularUI-Bootstrap to pick up the results.

I ran across the following question: How to tie angular-ui's typeahead with a server via $http for server side optimization?

Which updated my function call to the below:

$scope.getTypeaheadValues = function($viewValue)
{
    return $http({
        method: 'GET',
        url: 'api/v1/person?name__icontains=' + $viewValue
    }).then(function ($response) {
        var output = [];

        console.log($data);

        $response.data.objects.forEach(function (person)
        {
            output.push(person.name);
        });

        console.log(output);
        return output;
    });
}
Community
  • 1
  • 1
Nicholas Pappas
  • 10,439
  • 12
  • 54
  • 87
2

$scope.getTypeaheadValues is not returning any array. It returns null, because your return statement is in the callback function "success", which is called asynchrony.

Maybe this will work:

$scope.getTypeaheadValues = function($viewValue)
{    
    var output = [];
    $http({
        method: 'GET',
        url: 'api/v1/person?name__icontains=' + $viewValue
    }).error(function ($data) {
        console.log("failed to fetch typeahead data");
    }).success(function ($data) {            
        $data.objects.forEach(function (person)
        {
            output.push(person.name);
        });
        console.log(output);        
    });
    return output;
}
T.Riemer
  • 99
  • 6
  • But the asynchronous `$http` call will not have completed, populating the `output` array, by the time the `return` statement is called. While this solution removes the error, it is always an empty array because it is never populated in time. – Nicholas Pappas Oct 22 '13 at 18:38
  • I got it figured out. See my answer to the question. Your answer got me thinking along the right lines -- thank you. – Nicholas Pappas Oct 22 '13 at 18:54
  • 2
    Did some googleing. It seems you can just pass the promise to typeahead. – T.Riemer Oct 22 '13 at 19:00
  • How to do the same with a $resource instead of a $http ? – Stephane Sep 04 '14 at 09:50
  • Here is how to do it with a $resource Code: return TechnologyService.search($scope.searchTerm, function(data) { return data.content; }); – Stephane Sep 04 '14 at 10:06
-2
$scope.getTypeaheadValues = function($viewValue)
{
    var someOutput="";//return something

    $http({
        method: 'GET',
        url: 'api/v1/person?name__icontains=' + $viewValue
    }).error(function (data) {
        console.error(data);
    }).success(function (data) {
        console.log(data);//Do whatever you want 
    });
return someOutput;

}

//The return statement is missing