Material: md-autocomplete: Cannot read property 'then' of undefined

Created on 22 Apr 2016  路  17Comments  路  Source: angular/material

In version 1.0.7 I get this error when typing in md-autocomplete:

angular.js:13550 TypeError: Cannot read property 'then' of undefined

The error seems NOT to happen in a previous version. More detailed explanation is on StackOverflow.

fixed works as expected

Most helpful comment

That's a f ridiculous issue: isPromise = items && items.then;
It makes me reconsider the experience of people I'm trusting on.

All 17 comments

Upgrade to 1.1.0-rc4.

I am also getting this, upgrade to 1.1.0-rc4 did not fix it.

I had the same error caused by following query function:

function querySearch(searchText) { if (!searchText) return; ... }

The fix was to return an empty array in case of an empty searchText.

@berkyl You are right! Thank you!

I'm getting a similar thing in 1.1.0-rc5 even when there is a searchText
error

it seems like it can't eval md-items="item in userSearch(searchHelpers.query)"

scope.userSearch = function (query) {
    if (scope.contactMap) {
        var results = query ? scope.contactMap.filter(createFilterFor(query)) : [];
        return results;
    }
};

That's a f ridiculous issue: isPromise = items && items.then;
It makes me reconsider the experience of people I'm trusting on.

This issue is first in google ( "angular autocomplete Cannot read property 'then' of undefined" ). I think it may help some people:

This error occures also when you use function that does not exist, like:

md-items="item in notExistingFunction(query)"

and there is no other information in error stack (angular digest.. and then 'cannot read prop...').

@marcioaso

https://material.angularjs.org/latest/api/directive/mdAutocomplete

  • md-items expression
    An expression in the format of item in items to iterate over matches for your search.

!!items.then - items must be defined, as every variable over which you can iterate
Checking if items exists would be an issue - waste of CPU (I'm not sure about it) and code not according to documentation.

I came across this problem as well, and managed to fix it. For future references you can check out my snippet (excuse the Dutch comments): http://pastebin.com/HtXgz5DC

version : 1.1.1

Hi got the same issue, and trying what u guys told didn't work for me..

Here is my component :

angular.module('searchbar').component('searchbar', {
    templateUrl: 'dist/search/template/search.html',
    controller: [
        '$scope',
        '$http',
        '$q',
        '$log',
        function autoCompleteController($scope, $http, $q, $log) {
            var self = this;
            self.queryResults = getResults().then(function(response) {
                return response.data;
            });
            console.log(self.queryResults);
            self.querySearch = querySearch;
            self.selectedItemChange = selectedItemChange;
            self.searchTextChange = searchTextChange;

            function querySearch(query) {
                var results = query ? self.queryResults : [];
                return $q.when(results);
            }

            function searchTextChange(text) {
                $log.info('Text changed to ' + text);
            }

            function selectedItemChange(item) {
                $log.info('Item changed to ' + JSON.stringify(item));
            }

            function getResults() {
                return $http.get('data/searchResults.json');
            }
        }
    ]
}).controller('autoCompleteController', function($scope) {

});

Here is my template/search.html:

<div id="searchBar" ng-controller="autoCompleteController as ctrl" layout="column" ng-cloak>
    <md-content>
        <form ng-submit="$event.preventDefault()">
            <md-autocomplete 
                md-no-cache="ctrl.noCache" 
                md-selected-item="ctrl.selectedItem" 
                md-search-text-change="ctrl.searchTextChange(ctrl.searchText)" 
                md-search-text="ctrl.searchText" 
                md-selected-item-change="ctrl.selectedItemChange(item)" 
                md-items="item in ctrl.querySearch(ctrl.searchText)"
                md-item-text="item.display" 
                md-min-length="0" 
                placeholder="Search..."
             >
                <md-item-template>
                    <span class="item-title">
                  <!-- <md-icon aria-label="account-circle" class="material-icons">
                      view_modules
                  </md-icon> -->
                  <span> {{$ctrl.item.name}} </span>
                    </span>
                </md-item-template>
                <md-not-found>
                    Aucun r茅sultat correspondant 脿 "{{ctrl.searchText}}" n'a 茅t茅 trouv茅.
                </md-not-found>
                </md-autocomplete>
        </form>
    </md-content>
</div>

Can someone tell me how to solve this issue ?

hello,
i have same problem than @KGALLET
Version: 1.1.1

@fx69005: It's probably because your self.queryResults is not well instantiated. I mean, the promise I made is not well done.
BTW, I managed to solve my problem. I can close so.

(function() {
angular
    .module('searchbar', ['ngMaterial'])
    .controller('SearchBar', SearchBar);

    function SearchBar($http) {
      var self = this;

      self.queryResults = [];
      self.transformedQueryResults = transformResults();
      self.querySearch   = querySearch;
      self.selectedItemChange = selectedItemChange;

      function getResults() { return $http.get('data/searchResults.json'); }
      function querySearch (query) {
        var results = query ?  self.transformedQueryResults.filter(createFilterFor(query)) : self.transformedQueryResults;
        console.log(self.queryResults);
        return results;
      }

      function selectedItemChange(item) {
        console.log(item);
      }

      function transformResults() {
        getResults().then(function(response){
          var transformedResults = [];
          var results = response.data;
          results.forEach(function(item) {
            var transformedItem = parseItem(item);
            transformedResults.push(transformedItem);
          });
          // queryResults get the items with all the informations, it is usefull
          // for the implementation of the selectedItemChange method (send mail, call number.. etc)
          self.queryResults = results;
          // here, only the objects i want to save
          self.transformedQueryResults = transformedResults;
        });
      }

      // the value is important to filter the results
      function parseItem(item) {
        switch(item.type) {
          // case 'software':
          //   return {
          //     "display" : item.infos.name,
          //     "value": angular.lowercase(item.infos.name),
          //     "icon" : item.infos.icon,
          //   }
          // break;
          // case 'customer':
          //   return {
          //     "display" : item.infos.name,
          //     "value": angular.lowercase(item.infos.name),
          //     "icon" : item.infos.icon,
          //   }
          // break;
          case 'contributor':
            return {
              "display" : item.infos.company + ', ' + item.infos.name,
              "value": angular.lowercase(item.infos.company + ', ' + item.infos.name),
              "icon" : item.infos.icon,
            }
          break;
          default:
            return {
                "display" : item.infos.name,
                "value": angular.lowercase(item.infos.name),
                "icon" : item.infos.icon,
              }
          break;
        }
      }

      function createFilterFor(query) {
       var lowercaseQuery = angular.lowercase(query);

       return function filterFn(state) {
         console.log(state.display);
         return (state.value.indexOf(lowercaseQuery) === 0);
       };
     }
    }
})();

Hi,
I had the same problem. In my case it was caused by querySearch.
In the view I had :
md-items="item in ctrl.querySearch(ctrl.searchText)"
I changed it to :
md-items="item in querySearch(ctrl.searchText)"
That's fixed the issue

it's really ridicule to have this kind of bug from google, 馃憥
how can fix that

It's not a bug. The search function should return a promise before it can resolve the async result from your function - assuming you do a async request to the server (read about promises here: https://docs.angularjs.org/api/ng/service/$q

One example how you can solve it (not a working example but here is the gist of it):

app.controller('TestCtrl', function($q, MySearchService) {
// handle undefined input (e.g. return empty array)
 if (typeof searchText != 'undefined') { 
  $scope.search = function(searchText) {
   deferred = $q.defer();
   MySearchService.searchText(searchText).then(function(resp) {
    // parse results to list
    deferred.resolve(resp.mylist);

   }).catch(function(err) {
    console.log(err);
   });

   return deferred.promise;
  }
 } else {
  return [];
 }
});

I solved this issue in my code by returning the http object in the search function:

$scope.getMapsMatches = function(searchText) {
return $http.get('http://maps.googleapis.com/maps/api/geocode/json', {
params: {
address: searchText
}
})
.then(function(response) { ....

I added self.states = [] before calling the query and the error disappeared from console

Two options, which worked for me:

1) md-min-length="0" -> make it more than 0

2) if you want to keep 0, make sure to return empty array, when nothing is found in search method, so instead of:
var results = query ? scope.contactMap.filter(createFilterFor(query)) : [];
return results;

do more like:

var results = query ? scope.contactMap.filter(createFilterFor(query)) : [];
return results ? results : [];

Was this page helpful?
0 / 5 - 0 ratings