Angular Material autocomplete with $http call

All we need is an easy explanation of the problem, so here it is.

What i’m trying to do is an Angular Material autocomplete (md-autocomplete) with data dynamically retrieved from an AJAX call to my REST API. Unfortunately I get only indeterminate progress bar instead of autocomplete items as you can see below.

Result

enter image description here

Controller

$scope.customersSelect = {};
$scope.selectedItem = null;
$scope.searchText = null;

$scope.getCustomers = function (query) {
    selectsService.getCustomers(query).then(function (results) {
        $scope.customersSelect = results.data;
        console.log($scope.customersSelect);
    }, function(error) {
        alert(error.data.message);
    });
}

Service

var selectsServiceFactory = {};

_getCustomers = function (query) {
    return $http.get(serviceBase + 'api/selects/customers/' + query)
    .then(function(results) {
        return results;
    });
}

selectsServiceFactory.getCustomers = _getCustomers;

return selectsServiceFactory;

View

<md-autocomplete md-floating-label="Klient" 
            autocomplete="off" 
            flex="" 
            md-search-text-change="getCustomers(searchText)" 
            md-item-text="item" 
            md-items="item in customersSelect" 
            md-search-text="searchText" 
            md-selected-item="machine.customerId" 
            md-input-maxlength="100" 
            md-input-minlength="2" 
            md-input-name="machineOwner">
<md-item-template>
    <span md-highlight-text="searchText">{{item}}</span>
</md-item-template> 

I’m getting the data successfully from the API, because I can see it printed in the console.

How to solve :

I know you bored from this bug, So we are here to help you! Take a deep breath and look at the explanation of your problem. We have many solutions to this problem, But we recommend you to use the first method because it is tested & true method that will 100% work for you.

Method 1

(function() {
  'use strict';
  angular
    .module('MyApp')
    .controller('DemoCtrl', DemoCtrl);

  function DemoCtrl($http) {
    var self = this;
    
    self.data = null;
    self.selectedItem = null;
    self.searchText = null;
    
    self.querySearch = function (query) {
      $http.get('http://www.omdbapi.com/?s=' + escape(query))
        .then(function(result) {
          self.data = result.data.Search;
          return result.data.Search;
        });
    }
  }
})();
<!DOCTYPE html>
<html >
  <head>
    <meta charset="UTF-8">
    <title>$http md-Autocomplete</title>
    <link rel="stylesheet" href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700,400italic" rel="nofollow noreferrer noopener">
    
    
    <link rel='stylesheet prefetch' href='https://cdn.gitcdn.xyz/cdn/angular/bower-material/v1.0.0-rc4/angular-material.css'>
  </head>

  <body>

    <div class="autocompletedemoFloatingLabel" ng-controller="DemoCtrl as ctrl" ng-app="MyApp" layout="column" ng-cloak="">
      <md-content class="md-padding">
        <form name="searchForm" ng-submit="$event.preventDefault()">
            <div layout-gt-sm="row">
            <md-input-container flex="">
              <label>Name</label>
              <input type="text">
            </md-input-container>
            <md-autocomplete md-floating-label="Favorite movie" 
                            flex="" 
                            md-item-text="item.Title"
                            md-items="item in ctrl.data" 
                            md-search-text-change="ctrl.querySearch(ctrl.searchText)"
                            md-search-text="ctrl.searchText" 
                            md-selected-item="ctrl.selectedItem" 
                            md-no-cache="ctrl.noCache" 
                            md-input-maxlength="30" 
                            md-input-minlength="2" 
                            md-input-name="autocompleteField" 
                            required="">
              <md-item-template>
                <span md-highlight-text="ctrl.searchText">{{item.Title}}</span>
              </md-item-template>
              <div ng-messages="searchForm.autocompleteField.$error" ng-if="searchForm.autocompleteField.$touched">
                <div ng-message="required">You <b>must</b> have a favorite movie.</div>
                <div ng-message="minlength">Your entry is not long enough.</div>
                <div ng-message="maxlength">Your entry is too long.</div>
              </div>
            </md-autocomplete>
          </div>
        </form>
      </md-content>
    </div>
    <script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular.js'></script>
    <script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular-animate.min.js'></script>
    <script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular-route.min.js'></script>
    <script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular-aria.min.js'></script>
    <script src='https://ajax.googleapis.com/ajax/libs/angularjs/1.4.6/angular-messages.min.js'></script>
    <script src='https://cdn.gitcdn.xyz/cdn/angular/bower-material/v1.0.0-rc4/angular-material.js'></script>
    <script src='https://s3-us-west-2.amazonaws.com/s.cdpn.io/t-114/assets-cache.js'></script>    
  </body>
</html>

Finally I have done it. Here is the solution.

Method 2

I struggled with this as well. I end up doing something like this:

$scope.querySearch=function(searchTerm) {
  return $http({
       url:"http://mysearch.api?keyword="+searchTerm,
       method:"GET"
       })
       .then(function(res) {
          return res.data;
       }
}

Problem for me was that md-autocomplete need the result to be an array, where the response from $http is an object. I hope this help someone else!!

Method 3

You can use Angularjs promises to get data from $http call.

        <body>
                <form name="searchForm" ng-submit="searchForm.$valid && submit()" novalidate>
                    <md-autocomplete flex-gt-sm="35"
                                required md-input-name="autocompleteField" 
                                md-input-maxlength="80" 
                                md-selected-item="selectedItem"
                                md-search-text="formdata.searchText" 
                                md-items="item in querySearch(formdata.searchText)" 
                                md-item-text="item.display" 
                                md-min-length="0"
                                md-floating-label="Get Values">
                                <md-item-template>
                                  <span md-highlight-text="formdata.searchText" md-highlight-flags="^i">{{item.display}}</span>
                                </md-item-template>
                                <md-not-found>
                                  No data matching "{{formdata.searchText}}" were found.
                                </md-not-found>
                                <div ng-messages="searchForm.autocompleteField.$error" ng-if="searchForm.autocompleteField.$touched">
                                  <div ng-message="required">Please <b>select</b> Pricing model id.</div>
                                </div>
                            </md-autocomplete>
                </form>
             </body>
        <script>
        var getdataList= getData().then(function(greeting) {
                  $scope.getdataList = greeting;
            })
            $scope.querySearch = querySearch;
        function querySearch(query) {
                var results = query ? $scope.getdataList.filter(createFilterFor(query)): $scope.getdataList, deferred;
                    return results;
                }
        function getData() {
                deferred = $q.defer();
                $http.get(url).then(function(response) {
                        var responseList = response.map(function (task) {
                            return {value: task.dataname, display:task.dataname};  
                        });
                        deferred.resolve(responseList);
                    },function myError() {
                        console.log("Error")
                    });
                return deferred.promise;
            }
        function createFilterFor(query) {
                    var lowercaseQuery = query.toLowerCase();
                      return function filterFn(state) {
                        var lowercaseState = state.value.toLowerCase();
                      return (lowercaseState.search(lowercaseQuery) >= 0);
                    };
                }


        </script>

Method 4

Use $apply to kick a digest cycle yourself, it will update the view.

$scope.getCustomers = function (query) {
selectsService.getCustomers(query).then(function (results) {
    $scope.customersSelect = results.data;
    $scope.$apply();
    console.log($scope.customersSelect);
}, function(error) {
    alert(error.data.message);
});
}

Note: Use and implement method 1 because this method fully tested our system.
Thank you 🙂

All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply