Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

feat(typeahead): add event object to on select callback #5165

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/typeahead/docs/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,9 @@ The typeahead directives provide several attributes:
_(Defaults: angular.noop)_ :
Binding to a variable that indicates if no matching results were found

* `typeahead-on-select($item, $model, $label)`
* `typeahead-on-select($item, $model, $label, $event)`
_(Defaults: null)_ :
A callback executed when a match is selected
A callback executed when a match is selected. $event can be undefined if selection not triggered from a user event.

* `typeahead-select-on-exact`
_(Defaults: false)_ :
Expand Down
6 changes: 4 additions & 2 deletions src/typeahead/test/typeahead.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -478,12 +478,13 @@ describe('typeahead tests', function() {
});

it('should invoke select callback on select', function() {
$scope.onSelect = function($item, $model, $label) {
$scope.onSelect = function($item, $model, $label, $event) {
$scope.$item = $item;
$scope.$model = $model;
$scope.$label = $label;
$scope.$event = $event;
};
var element = prepareInputEl('<div><input ng-model="result" typeahead-on-select="onSelect($item, $model, $label)" uib-typeahead="state.code as state.name for state in states | filter:$viewValue"></div>');
var element = prepareInputEl('<div><input ng-model="result" typeahead-on-select="onSelect($item, $model, $label, $event)" uib-typeahead="state.code as state.name for state in states | filter:$viewValue"></div>');

changeInputValueTo(element, 'Alas');
triggerKeyDown(element, 13);
Expand All @@ -492,6 +493,7 @@ describe('typeahead tests', function() {
expect($scope.$item).toEqual($scope.states[0]);
expect($scope.$model).toEqual('AL');
expect($scope.$label).toEqual('Alaska');
expect($scope.$event.type).toEqual("keydown");
});

it('should correctly update inputs value on mapping where label is not derived from the model', function() {
Expand Down
33 changes: 17 additions & 16 deletions src/typeahead/typeahead.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
id: popupId,
matches: 'matches',
active: 'activeIdx',
select: 'select(activeIdx)',
select: 'select(activeIdx, evt)',
'move-in-progress': 'moveInProgress',
query: 'query',
position: 'position',
Expand Down Expand Up @@ -202,7 +202,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
return false;
};

var getMatchesAsync = function(inputValue) {
var getMatchesAsync = function(inputValue, evt) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are some calls to this function which do not seem to have an event object, such as when the typeahead directive inits.

Is it okay for the event object to be undefined in these cases? Any other suggestions in order to always have an event object?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it is fine - only thing is to perhaps add a note in the documentation that this can be the case.

var locals = {$viewValue: inputValue};
isLoadingSetter(originalScope, true);
isNoResultsSetter(originalScope, false);
Expand Down Expand Up @@ -238,10 +238,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
if (selectOnExact && scope.matches.length === 1 && inputIsExactMatch(inputValue, 0)) {
if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
$$debounce(function() {
scope.select(0);
scope.select(0, evt);
}, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
} else {
scope.select(0);
scope.select(0, evt);
}
}

Expand Down Expand Up @@ -329,7 +329,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
isOpenSetter(originalScope, isOpen);
};

scope.select = function(activeIdx) {
scope.select = function(activeIdx, evt) {
//called from within the $digest() cycle
var locals = {};
var model, item;
Expand All @@ -344,7 +344,8 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
onSelectCallback(originalScope, {
$item: item,
$model: model,
$label: parserResult.viewMapper(originalScope, locals)
$label: parserResult.viewMapper(originalScope, locals),
$event: evt
});

resetMatches();
Expand Down Expand Up @@ -378,10 +379,10 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
scope.$apply(function () {
if (angular.isNumber(scope.debounceUpdate) || angular.isObject(scope.debounceUpdate)) {
$$debounce(function() {
scope.select(scope.activeIdx);
scope.select(scope.activeIdx, evt);
}, angular.isNumber(scope.debounceUpdate) ? scope.debounceUpdate : scope.debounceUpdate['default']);
} else {
scope.select(scope.activeIdx);
scope.select(scope.activeIdx, evt);
}
});
break;
Expand All @@ -404,25 +405,25 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
}
});

element.bind('focus', function () {
element.bind('focus', function (evt) {
hasFocus = true;
if (minLength === 0 && !modelCtrl.$viewValue) {
$timeout(function() {
getMatchesAsync(modelCtrl.$viewValue);
getMatchesAsync(modelCtrl.$viewValue, evt);
}, 0);
}
});

element.bind('blur', function() {
element.bind('blur', function(evt) {
if (isSelectOnBlur && scope.matches.length && scope.activeIdx !== -1 && !selected) {
selected = true;
scope.$apply(function() {
if (angular.isObject(scope.debounceUpdate) && angular.isNumber(scope.debounceUpdate.blur)) {
$$debounce(function() {
scope.select(scope.activeIdx);
scope.select(scope.activeIdx, evt);
}, scope.debounceUpdate.blur);
} else {
scope.select(scope.activeIdx);
scope.select(scope.activeIdx, evt);
}
});
}
Expand Down Expand Up @@ -585,14 +586,14 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.debounce', 'ui.bootstrap
scope.active = matchIdx;
};

scope.selectMatch = function(activeIdx) {
scope.selectMatch = function(activeIdx, evt) {
var debounce = scope.debounce();
if (angular.isNumber(debounce) || angular.isObject(debounce)) {
$$debounce(function() {
scope.select({activeIdx: activeIdx});
scope.select({activeIdx: activeIdx, evt: evt});
}, angular.isNumber(debounce) ? debounce : debounce['default']);
} else {
scope.select({activeIdx: activeIdx});
scope.select({activeIdx: activeIdx, evt: evt});
}
};
}
Expand Down
2 changes: 1 addition & 1 deletion template/typeahead/typeahead-popup.html
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<ul class="dropdown-menu" ng-show="isOpen() && !moveInProgress" ng-style="{top: position().top+'px', left: position().left+'px'}" style="display: block;" role="listbox" aria-hidden="{{!isOpen()}}">
<li ng-repeat="match in matches track by $index" ng-class="{active: isActive($index) }" ng-mouseenter="selectActive($index)" ng-click="selectMatch($index)" role="option" id="{{::match.id}}">
<li ng-repeat="match in matches track by $index" ng-class="{active: isActive($index) }" ng-mouseenter="selectActive($index)" ng-click="selectMatch($index, $event)" role="option" id="{{::match.id}}">
<div uib-typeahead-match index="$index" match="match" query="query" template-url="templateUrl"></div>
</li>
</ul>