MTT
MTT

Reputation: 318

Material Design Autocomplete not updating dropdown on remote call

I'm trying to get md-autocomplete to play nice with Algolia's Angular service. I'm stuck on getting the dropdown to display the results being returned from Algolia. The content is in console yet the dropdown will not populate with the updated results. What am I missing?

tl;dr: Plnkr

"Backend"

.controller('DemoCtrl', DemoCtrl);
function DemoCtrl ($timeout, $q, $log, algolia, $http, $scope) {

   var self = this;
   self.simulateQuery = false;
   self.isDisabled    = false;
   self.repos         = loadAll();
   self.querySearch   = querySearch;
   self.selectedItemChange = selectedItemChange;
   self.searchTextChange   = searchTextChange;

   // Algolia demo keys below
   var client = algolia.Client('latency', '6be0576ff61c053d5f9a3225e2a90f76');
   var index = client.initIndex('contacts');
   $scope.query = '';
   $scope.hits = [];

   // Disable cache
   $scope.noCacheResults = false;


   // ******************************
   // Internal methods
   // ******************************
   /**
    * Search for repos... use $timeout to simulate
    * remote dataservice call.
    */
   function querySearch (query) {

      // This returns objects in console
      var results = self.algoliaSearch(query);

      // This returns: TypeError: Cannot read property 'then' of undefined
      // var results = query ? self.algoliaSearch( createFilterFor(query) ) : self.repos, deferred;

       return results;
   }
   function searchTextChange(text) {
     $log.info('Text changed to ' + text);
   }
   function selectedItemChange(item) {
     $log.info('Item changed to ' + JSON.stringify(item));
   }

   /**
    * Build `components` list of key/value pairs
    */
    function algoliaSearch(text) {
        try {
            index
                .search(text)
                .then(function(content) {
                    // if (content.query !== $scope.query) {
                    //     // do not take out-dated answers into account
                    //     return;
                    // }
                    $scope.hits = content.hits;

                    return content.hits.map( function (hit) {
                      hit.value = hit.name.toLowerCase();
                      $log.debug(hit);
                      return hit;
                    });

                }, function(content) {
                    console.log('Error: ' + content.message);
                });
        } catch(e) {
           $log.debug(e);
        }
    }

   function loadAll() {
     var repos = [
       {
         'name'      : 'Angular 1',
         'url'       : 'https://github.com/angular/angular.js',
         'watchers'  : '3,623',
         'forks'     : '16,175',
       }
     ];
     return repos.map( function (repo) {
       repo.value = repo.name.toLowerCase();
       return repo;
     });

   }

   /**
    * Create filter function for a query string
    */
   function createFilterFor(query) {
     var lowercaseQuery = angular.lowercase(query);
     return function filterFn(item) {
       return (item.value.indexOf(lowercaseQuery) === 0);
     };
   }
 }

"Frontend"

<div ng-controller="DemoCtrl as ctrl" layout="column" ng-cloak>
  <md-content layout-padding layout="column">
    <form ng-submit="$event.preventDefault()">
      <p>Use <code>&lt;md-autocomplete&gt;</code> with custom templates to show styled autocomplete results.</p>
      <md-autocomplete
          ng-disabled="ctrl.isDisabled"

          md-no-cache="ctrl.noCacheResults"

          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.name"
          md-min-length="0"
          placeholder="Pick an Angular repository"
          md-menu-class="autocomplete-custom-template">
        <md-item-template>
            {{item}}

          <span class="item-title">
            <md-icon md-svg-icon="img/icons/octicon-repo.svg"></md-icon>
            <span> {{item.name}} </span>
          </span>
          <span class="item-metadata">
            <span class="item-metastat">
              <strong>{{item.watchers}}</strong> watchers
            </span>
            <span class="item-metastat">
              <strong>{{item.forks}}</strong> forks
            </span>
          </span>
        </md-item-template>
      </md-autocomplete>
    </form>
  </md-content>
</div>

"Algolia default"

<section class="panel">
  <header class="panel-heading">
    <div class="search_box">
      <form action="#" method="get">
        <input autocomplete="off" class="autocomplete" placeholder="Start typing" type="text" spellcheck="false" id="q" ng-model="query" />
        <div class="searchbutton">
          <i class="icon-search icon-large"></i>
        </div>
      </form>
    </div>
  </header>
</section>

<h1>Results</h1>
<div class="hit" ng-repeat="hit in hits">
  <div class="attribute" ng-repeat="(attribute,v) in hit._highlightResult">
    <span>{{ attribute }}: </span>
    <span ng-bind-html="v.value"></span>
  </div>
</div>

<script type="text/javascript">
  angular
    .module('myapp', ['algoliasearch', 'ngSanitize'])
    .controller('SearchCtrl', ['$scope', 'algolia', function($scope, algolia) {
      $scope.query = '';
      $scope.hits = [];
      // Replace the following values by your ApplicationID and ApiKey.
      var client = algolia.Client('latency', '6be0576ff61c053d5f9a3225e2a90f76');
      // Replace the following value by the name of the index you want to query.
      var index = client.initIndex('contacts');
      $scope.$watch('query', function() {
        index.search($scope.query, { hitsPerPage: 5 }).then(function(content) {
          if (content.query !== $scope.query) {
            // do not take out-dated answers into account
            return;
          }
          $scope.hits = content.hits;
        }, function(content) {
          console.log('Error: ' + content.message);
        });
      });
    }]);
</script>

Upvotes: 1

Views: 1586

Answers (1)

Gianluca Bargelli
Gianluca Bargelli

Reputation: 1840

You should return a Promise since the call to AlgoliaSearch is asynchronous:

function querySearch(query) {
  return algoliaSearch(query);
  // var results = query ? self.algoliaSearch( createFilterFor(query) ) : self.repos, deferred;
}

You can find the updated code here: http://plnkr.co/edit/C8fmsNRzNsXEC7InNM6a

Upvotes: 2

Related Questions