Search code examples
angularjsangular-promiseangular-ui-typeaheadangular-components

typhead not populating angular ui


  • Angular 1.5.8
  • Bootstrap 3.3.7 (CSS)
  • Angular-ui 2.0.2

Using angular Typeahead (ui.bootstrap.typeahead) requires a list of objects which it will display in the ui component with HTML

Problem

  1. Returning promise from service to component (1.5 style controller, view, bindings)

  2. Controller function uses the returned promise from the service and performs the then logic and returns the array of objects

  3. Typeahead does not process the array... performing a console log, you can see the array.

  4. If I statically pass the same array of objects without using the service method then the functionality works

HTML

            <input type="text" ng-model="$ctrl.search.term" ng-disabled="!$ctrl.search.type"
                   typeahead-wait-ms="600"
                   placeholder="Search..."
                   uib-typeahead="res as res.name for res in $ctrl.submit($viewValue)"
                   typeahead-no-results="noResults" class="form-control" required>
            <i ng-show="loadingLocations" class="icon ion-refresh"></i>

            <div ng-show="noResults">
                <i class="icon ion-close"></i> No Results Found
            </div> 

            <select class="form-control custom-select-md"
                   ng-model="$ctrl.search.type"
                   placeholder="Type"
                   required>
                <option value="" disabled selected>Select Type?</option>
                <option value="car">car</option>
                <option value="van">van</option>
            </select>

Component (controller, view )

//submit search for issuers or issuedCard
submit() {
    this.isSubmitting = true;

    this._SearchService.performSearch(this.search)
    .then(
        (resp) => {
            //e.g. [{id:1, name:'test'}]
            console.log('Search Result', resp);
            return resp;                                
        },

        (err) => {
            console.log('Error Search', err);
            this.reset(false);
            this.errors = err;
            return [];
        }
    );

    //Comment out method above to see this static data returning and working as should be :'(
    //return [{id:865,issuer: {},name:"British Testing"},
    //    {id:866,issuer: {},name:"American Testing"}];
}

Service

performSearch(searchData) {
    console.log('Search Qry', searchData);

    let deferred = this._$q.defer();

    if(!this.isValidSearch(searchData)) {
        deferred.reject({status:400,  error: 'Bad Request', message:'invalid data'});
        return deferred.promise;
    }

    let searchURI = (searchData.type === 'car' ? 'van' : 'issuer');

    this._$http({
        url: `${this._AppConstants.api}/${this._AppConstants[searchURI]['search']}`,
        method: 'GET',
        params: {
            name: searchData.term
        }
    }).then(
        (resp) => {
            console.log('Search Data', resp);
            this.result.term = searchData.term;
            this.result.type = searchURI;
            deferred.resolve(resp.data[this._AppConstants[searchURI]['searchResp']]);

        },

        (err) => {
            console.log('Error performing search', err);
            deferred.reject(err.data);
        }
    );

    return deferred.promise;
}

Solution

  • You're using

    res as res.name for res in $ctrl.submit($viewValue)
    

    What comes after the in is supposed to be an array, or a promise of array.

    But it's not. It's what returned by $ctrl.submit(). And this method doesn't return anything:

    submit() {
        this.isSubmitting = true;
    
        // no return here
    
        this._SearchService.performSearch(this.search)
        .then(
            (resp) => {
                //e.g. [{id:1, name:'test'}]
                console.log('Search Result', resp);
                return resp;                                
            },
    
            (err) => {
                console.log('Error Search', err);
                this.reset(false);
                this.errors = err;
                return [];
            }
        );
    
        // no return here
    }
    

    The only return statements return from the function passed to then(), and are executed asynchronously, after the submit() method has returned nothing (i.e. undefined).

    So, to be short, you need to return the promise:

    return this._SearchService.performSearch(this.search) ...
    

    Note that your service method could be reduced and cleaner if you used promise chaining, instead of the resolve/reject anti-pattern:

    performSearch(searchData) {
        console.log('Search Qry', searchData);
    
        if(!this.isValidSearch(searchData)) {
            return $q.reject({status:400,  error: 'Bad Request', message:'invalid data'});
        }
    
        let searchURI = (searchData.type === 'car' ? 'van' : 'issuer');
        return this._$http.get(`${this._AppConstants.api}/${this._AppConstants[searchURI]['search']}`,   { params: {name: searchData.term) } }).then(
            resp => {
                console.log('Search Data', resp);
                this.result.term = searchData.term;
                this.result.type = searchURI;
                return resp.data[this._AppConstants[searchURI]['searchResp']]);
            }).catch(resp => {
                console.log('Error Search', err);
                this.reset(false);
                this.errors = err;
                return $q.reject([]);
            });
    }