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

Commit

Permalink
fix(typeahead): play nicelly with existing formatters
Browse files Browse the repository at this point in the history
  • Loading branch information
pkozlowski-opensource committed Jun 30, 2013
1 parent 5ddd01f commit d2df0b3
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 28 deletions.
30 changes: 29 additions & 1 deletion src/typeahead/test/typeahead.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,18 @@ describe('typeahead tests', function () {

beforeEach(module('ui.bootstrap.typeahead'));
beforeEach(module('template/typeahead/typeahead.html'));
beforeEach(module(function($compileProvider) {
$compileProvider.directive('formatter', function () {
return {
require: 'ngModel',
link: function (scope, elm, attrs, ngModelCtrl) {
ngModelCtrl.$formatters.unshift(function (viewVal) {
return 'formatted' + viewVal;
});
}
};
});
}));
beforeEach(inject(function (_$rootScope_, _$compile_, _$document_, $sniffer) {
$scope = _$rootScope_;
$scope.source = ['foo', 'bar', 'baz'];
Expand Down Expand Up @@ -237,7 +249,7 @@ describe('typeahead tests', function () {
expect($scope.$label).toEqual('Alaska');
});

it('should correctly update inputs value on mapping where label is not derived from the model', function () {
xit('should correctly update inputs value on mapping where label is not derived from the model', function () {

$scope.states = [
{code: 'AL', name: 'Alaska'},
Expand Down Expand Up @@ -270,4 +282,20 @@ describe('typeahead tests', function () {
});
});

describe('integration with existing formatters', function () {

it('should co-operate with existing formatters', function () {

$scope.states = [
{code: 'AL', name: 'Alaska'},
{code: 'CL', name: 'California'}
];
$scope.result = $scope.states[0];

var element = prepareInputEl("<div><input ng-model='result.name' formatter typeahead='state.name for state in states | filter:$viewValue'></div>"),
inputEl = findInput(element);

expect(inputEl.val()).toEqual('formatted' + $scope.result.name);
});
});
});
53 changes: 26 additions & 27 deletions src/typeahead/typeahead.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
require:'ngModel',
link:function (originalScope, element, attrs, modelCtrl) {

var selected;
var $setModelValue = $parse(attrs.ngModel).assign;

//minimal no of characters that needs to be entered before typeahead kicks-in
var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1;
Expand Down Expand Up @@ -130,48 +130,47 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
var timeoutId;

resetMatches();
if (selected) {
return inputValue;
} else {
if (inputValue && inputValue.length >= minSearch) {
if (waitTime > 0) {
if (timeoutId) {
$timeout.cancel(timeoutId);//cancel previous timeout
}
timeoutId = $timeout(function () {
getMatchesAsync(inputValue);
}, waitTime);
} else {
getMatchesAsync(inputValue);
if (inputValue && inputValue.length >= minSearch) {
if (waitTime > 0) {
if (timeoutId) {
$timeout.cancel(timeoutId);//cancel previous timeout
}
timeoutId = $timeout(function () {
getMatchesAsync(inputValue);
}, waitTime);
} else {
getMatchesAsync(inputValue);
}
}

return isEditable ? inputValue : undefined;
});

modelCtrl.$render = function () {
var locals = {};
locals[parserResult.itemName] = selected || modelCtrl.$viewValue;
element.val(parserResult.viewMapper(scope, locals) || modelCtrl.$viewValue);
selected = undefined;
};
modelCtrl.$formatters.push(function (modelValue) {
var locals = {}, viewValue;
locals[parserResult.itemName] = modelValue;

viewValue = parserResult.viewMapper(originalScope, locals);

return viewValue ? viewValue : modelValue;
});

scope.select = function (activeIdx) {
//called from within the $digest() cycle
var locals = {};
var model, item;
locals[parserResult.itemName] = item = selected = scope.matches[activeIdx].model;

model = parserResult.modelMapper(scope, locals);
modelCtrl.$setViewValue(model);
modelCtrl.$render();
onSelectCallback(scope, {
locals[parserResult.itemName] = item = scope.matches[activeIdx].model;
model = parserResult.modelMapper(originalScope, locals);
$setModelValue(originalScope, model);

onSelectCallback(originalScope, {
$item: item,
$model: model,
$label: parserResult.viewMapper(scope, locals)
$label: parserResult.viewMapper(originalScope, locals)
});

//return focus to the input element if a mach was selected via a mouse click event
element[0].focus();
};

Expand Down Expand Up @@ -259,4 +258,4 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position'])
return function(matchItem, query) {
return query ? matchItem.replace(new RegExp(escapeRegexp(query), 'gi'), '<strong>$&</strong>') : query;
};
});
});

3 comments on commit d2df0b3

@chrisronline
Copy link

Choose a reason for hiding this comment

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

Did this commit break the "select as label for value in array" syntax?

http://plnkr.co/edit/jVLnb6IrCoLX9gfldMPQ?p=preview

@pkozlowski-opensource
Copy link
Member Author

Choose a reason for hiding this comment

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

@chrisronline sort of... In reality it fixes inconsistency in input text rendering where it is impossible to derive input text (state.title) from the model (state.id). In such a case the input text was rendered correctly upon selection but wan't good upon initial rendering.

But you can still get the proper functionality by using the typeahead-input-formatter attribute:

http://plnkr.co/edit/jdATVORkKlEf23Ef8Bp2?p=preview

@chrisronline
Copy link

Choose a reason for hiding this comment

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

Works for us! Cheers

Please sign in to comment.