diff --git a/src/typeahead/docs/readme.md b/src/typeahead/docs/readme.md index 621415c565..d9072dd56a 100644 --- a/src/typeahead/docs/readme.md +++ b/src/typeahead/docs/readme.md @@ -27,6 +27,10 @@ The typeahead directives provide several attributes: * `typeahead-editable` _(Defaults: true)_ : Should it restrict model values to the ones selected from the popup only ? + +* `typeahead-focus-first` + _(Defaults: true)_ : + Should the first match automatically be focused as you type? * `typeahead-input-formatter` _(Defaults: undefined)_ : @@ -39,11 +43,15 @@ The typeahead directives provide several attributes: * `typeahead-min-length` _(Defaults: 1)_ : Minimal no of characters that needs to be entered before typeahead kicks-in - + * `typeahead-on-select($item, $model, $label)` _(Defaults: null)_ : A callback executed when a match is selected +* `typeahead-select-on-exact` + _(Defaults: false)_ : + Should it automatically select an item when there is one option that exactly matches the user input? + * `typeahead-template-url` : Set custom item template @@ -51,7 +59,3 @@ The typeahead directives provide several attributes: * `typeahead-wait-ms` _(Defaults: 0)_ : Minimal wait time after last character typed before typeahead kicks-in - -* `typeahead-focus-first` - _(Defaults: true)_ : - Should the first match automatically be focused as you type? diff --git a/src/typeahead/test/typeahead.spec.js b/src/typeahead/test/typeahead.spec.js index a86dcedd7e..9042b06cd8 100644 --- a/src/typeahead/test/typeahead.spec.js +++ b/src/typeahead/test/typeahead.spec.js @@ -430,6 +430,63 @@ describe('typeahead tests', function () { expect(inputEl.val()).toEqual('AL'); }); }); + + describe('select on exact match', function(){ + + it('should select on an exact match when set', function () { + + $scope.onSelect = jasmine.createSpy('onSelect'); + var element = prepareInputEl('
'); + var inputEl = findInput(element); + + changeInputValueTo(element, 'bar'); + + expect($scope.result).toEqual('bar'); + expect(inputEl.val()).toEqual('bar'); + expect(element).toBeClosed(); + expect($scope.onSelect).toHaveBeenCalled(); + }); + + it('should not select on an exact match by default', function () { + + $scope.onSelect = jasmine.createSpy('onSelect'); + var element = prepareInputEl(''); + var inputEl = findInput(element); + + changeInputValueTo(element, 'bar'); + + expect($scope.result).toBeUndefined(); + expect(inputEl.val()).toEqual('bar'); + expect($scope.onSelect.calls.any()).toBe(false); + }); + + it('should not be case sensitive when select on an exact match', function () { + + $scope.onSelect = jasmine.createSpy('onSelect'); + var element = prepareInputEl(''); + var inputEl = findInput(element); + + changeInputValueTo(element, 'BaR'); + + expect($scope.result).toEqual('bar'); + expect(inputEl.val()).toEqual('bar'); + expect(element).toBeClosed(); + expect($scope.onSelect).toHaveBeenCalled(); + }); + + it('should not auto select when not a match with one potential result left', function () { + + $scope.onSelect = jasmine.createSpy('onSelect'); + var element = prepareInputEl(''); + var inputEl = findInput(element); + + changeInputValueTo(element, 'fo'); + + expect($scope.result).toBeUndefined(); + expect(inputEl.val()).toEqual('fo'); + expect($scope.onSelect.calls.any()).toBe(false); + }); + }); describe('pop-up interaction', function () { var element; diff --git a/src/typeahead/typeahead.js b/src/typeahead/typeahead.js index 10b7778cf7..97b8127f65 100644 --- a/src/typeahead/typeahead.js +++ b/src/typeahead/typeahead.js @@ -63,6 +63,9 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap var appendToBody = attrs.typeaheadAppendToBody ? originalScope.$eval(attrs.typeaheadAppendToBody) : false; var focusFirst = originalScope.$eval(attrs.typeaheadFocusFirst) !== false; + + //If input matches an item of the list exactly, select it automatically + var selectOnExact = attrs.typeaheadSelectOnExact ? originalScope.$eval(attrs.typeaheadSelectOnExact) : false; //INTERNAL VARIABLES @@ -123,6 +126,15 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap element.attr('aria-activedescendant', getMatchId(index)); } }); + + var inputIsExactMatch = function(inputValue, index) { + + if (scope.matches.length > index && inputValue){ + return inputValue.toUpperCase() === scope.matches[index].label.toUpperCase(); + } + + return false; + }; var getMatchesAsync = function(inputValue) { @@ -157,6 +169,11 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position', 'ui.bootstrap scope.position.top = scope.position.top + element.prop('offsetHeight'); element.attr('aria-expanded', true); + + //Select the single remaining option if user input matches + if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)){ + scope.select(0); + } } else { resetMatches(); }