AngularJs UI typeahead match on leading characters
Asked Answered
M

3

22

The typeahead functionality in AngularJs UI seems simple and powerful however I have been trying to figure out how to get the matching to be done on the leading characters. For example if I type 'A' in the input box I would like to see all the states that start with 'A" and not all the states that contain an 'A' in the name. I have been looking for a couple of days on this and it seems that Angular has the concept of a custom filter that has a 'comparator'. The docs on this have a simple example that does not show the exact syntax for implementing a comparator.

The html looks like this:

<div>Selected: <span>{{selected}}</span></div>
    <div><input type="text" ng-model="selected" typeahead="name for name in states | filter:selected"></div>

The basic javascript looks like this

angular.module('firstChar', ['ui.bootstrap']);

    function TypeaheadCtrl($scope) {
        $scope.selected = undefined;
        $scope.states = ['Alabama', 'Alaska', 'Arizona', 'Arkansas', 'California', 'Colorado', 'Connecticut', 'Delaware', 'Florida', 'Georgia', 'Hawaii', 'Idaho', 'Illinois', 'Indiana', 'Iowa', 'Kansas', 'Kentucky', 'Louisiana', 'Maine', 'Maryland', 'Massachusetts', 'Michigan', 'Minnesota', 'Mississippi', 'Missouri', 'Montana', 'Nebraska', 'Nevada', 'New Hampshire', 'New Jersey', 'New Mexico', 'New York', 'North Dakota', 'North Carolina', 'Ohio', 'Oklahoma', 'Oregon', 'Pennsylvania', 'Rhode Island', 'South Carolina', 'South Dakota', 'Tennessee', 'Texas', 'Utah', 'Vermont', 'Virginia', 'Washington', 'West Virginia', 'Wisconsin', 'Wyoming'];
    }

I have a plunker here http://plnkr.co/edit/LT6pAnS8asnpFEd5e6Ri

So the challenge in a nutshell is to get the AngularUI typeahead to match only on the leading characters.

Any help or ideas on this would be hugely appreciated.

Mnemonics answered 25/8, 2013 at 14:22 Comment(0)
E
54

At the end of the day your question is not really specific to the typeahead directive but it has more to do with how AngularJS filters work.

Before presenting a working solution please note that the typeahead directive makes heavy use of AngularJS infrastructure ($http, promises) and expression language. So it is important to realize that the states | filter:selected is nothing more that an AngularJS expression.

Having a look at the above expression we need to find a way of filtering an array to return a list of matching items. The only special thing about the typeahead directive is that there is a $viewValue variable representing a value entered by a user in the input box. So we basically just need to filter the states array to return items starting with the $viewValue.

There are many ways of doing this but since you've mentioned the comparator for filters (please note that those were only introduced in 1.1.x version of AngularJS) you would have to define a comparator function that should decide if a given item should be returned in the list of results or not. Such a function could look like follows:

$scope.startsWith = function(state, viewValue) {
  return state.substr(0, viewValue.length).toLowerCase() == viewValue.toLowerCase();
} 

Having it defined the usage is very simple:

typeahead="name for name in states | filter:$viewValue:startsWith"

Here is the working plunk: http://plnkr.co/edit/WWWEwU4oPxvbN84fmAl0?p=preview

Evanthe answered 25/8, 2013 at 17:20 Comment(11)
WOW! Thanks! Seems to work perfectly. Gives a nice example of the concepts and chaining syntax of Angular.Mnemonics
One question though... How do we define 'state' in the startsWith function? I realize it is iterating through states but how does it come up with the name/object 'state'. I am trying to use this code in a more real world example and it is not working.Mnemonics
The answer to my question is that 'state' in the example is the current value from the array that is used to populate the typeahead list. My problem is that I am using an object to populate the typeahead so I can get the 'id' for the value selected. Angular iterates through the object and passes each one of the members of the object to the compare function to do the string compare.Mnemonics
Finally if you want to get an 'id' and have another value displayed this is the syntax '<input type="text" ng-model="selected" typeahead="item.id as item.name for item in phones | filter:{name: $viewValue}:phoneStartsWith">'Mnemonics
This way all states that do not start with the viewValue are filtered out. Ok. But what if I would like to append those states still matching the viewValue starting on an index bigger zero? How would I do that? Guess I'll have to post another question...Barncard
Here it goes: #26894211Barncard
This solution is not working for me, unfortunately. startsWith method itself is not getting called. I am using AngularJS v1.0.7Brucie
Solution not working for me on angular 1.2.8 with ui.bootstrap 0.11Symbolize
@pat-capozzi Thanks so much for your answer - it's exactly what I needed and it works with Angular 1.4.0-rc.1 and 1.4.7Timothytimour
@patcapozzi: I am having issue while using http req for searching it's not updating the suggestion list as per response for first search and one more issue with searching like if I type "a" it will search list that have text like "amit, anshu, annu" after clear first search I type "n" then instead showing new response it suggest the old name which having "n" char like "anshu, annu" and if I clear the search and type "n" again it will show the correct answer, So can you help why its showing previous list on second search and how I can resolve it –Clearcole
Note that you will probably also want to modify the highlighting filter as well so it only highlights the start of the string. The only change you need to make is to remove the g flag on the regex in the existing filter (line 687 here: github.com/angular-ui/bootstrap/blob/master/src/typeahead/…). You can do so using a decorator, or, if you're using a custom results template you can specify your new highlighting filter in the template itself. see my answer here: #36247079Suspensive
H
1

Custom filter for get the matching to be done on the leading characters in typehead auto completion box.

(function() {

    // Create global filters using angular.filter() only. Never use local filters inside
    // controllers/services. This enhances testing and reusability.
    function xpTypeaheadFilter() {
          return function(items, props) {

            var out = [];
            
            if (angular.isArray(items)) {

              items.forEach(function(item) {
                
                


                var text = props.toLowerCase();
                var itemLoverCase =item.toLowerCase();
                var substr = itemLoverCase.substr(0, text.length);

                 
                if (substr === text ) {
                    
                     out.push(item);
                     
                }
                

              });
            } else {
              // Let the output be the input untouched
              out = items;
            }
            console.log("out lem", out.length);

            return out;
          };
    }

    // Pass functions into module methods rather than assigning a callback.
    // This helps aid with readability and helps reduced the amount of code "wrapped"
    // inside Angular.
    angular.module('common')
    .filter('xpTypeaheadFilter', xpTypeaheadFilter);
})();
<input type="text" ng-model="vesselName" placeholder="Vessel Name" typeahead="vesselName for vesselName in vesselNames | xpTypeaheadFilter:$viewValue | limitTo:8"  class="form-control form-textbox" >
Hepplewhite answered 11/6, 2015 at 8:49 Comment(0)
D
0

I just edited post marked as answered and it works when list is having Id and title both:

<input type="text" ng-model="ledgerstatementModel.Supplier" typeahead="supplier as supplier.Name for supplier in supplierList | filter:{Name:$viewValue}:startsWith" class="form-control">

and in js :

 $scope.startsWith = function (supplier, viewValue) {
        return supplier.substr(0, viewValue.length).toLowerCase() == viewValue.toLowerCase();
    }
Defenestration answered 24/5, 2017 at 7:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.