From ea1e858a4923ef234db9622d0f9ba70d6d198ec3 Mon Sep 17 00:00:00 2001 From: Pawel Kozlowski Date: Sun, 30 Jun 2013 17:25:10 +0200 Subject: [PATCH] fix(typeahead): separate text field rendering and drop down rendering Closes #240 Closes #274 --- src/typeahead/test/typeahead.spec.js | 16 +++++++++++- src/typeahead/typeahead.js | 39 +++++++++++++++++++++------- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/typeahead/test/typeahead.spec.js b/src/typeahead/test/typeahead.spec.js index 331fe153cc..d488ecc661 100644 --- a/src/typeahead/test/typeahead.spec.js +++ b/src/typeahead/test/typeahead.spec.js @@ -279,7 +279,7 @@ describe('typeahead tests', function () { }); }); - describe('integration with existing formatters', function () { + describe('input formatting', function () { it('should co-operate with existing formatters', function () { @@ -290,6 +290,20 @@ describe('typeahead tests', function () { expect(inputEl.val()).toEqual('formatted' + $scope.result.name); }); + + it('should support a custom input formatting function', function () { + + $scope.result = $scope.states[0]; + $scope.formatInput = function($model) { + return $model.code; + }; + + var element = prepareInputEl("
"), + inputEl = findInput(element); + + expect(inputEl.val()).toEqual('AL'); + expect($scope.result).toEqual($scope.states[0]); + }); }); }); \ No newline at end of file diff --git a/src/typeahead/typeahead.js b/src/typeahead/typeahead.js index 21ee014031..0bf5d004fc 100644 --- a/src/typeahead/typeahead.js +++ b/src/typeahead/typeahead.js @@ -37,7 +37,7 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position']) require:'ngModel', link:function (originalScope, element, attrs, modelCtrl) { - var $setModelValue = $parse(attrs.ngModel).assign; + //SUPPORTED ATTRIBUTES (OPTIONS) //minimal no of characters that needs to be entered before typeahead kicks-in var minSearch = originalScope.$eval(attrs.typeaheadMinLength) || 1; @@ -45,16 +45,26 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position']) //minimal wait time after last character typed before typehead kicks-in var waitTime = originalScope.$eval(attrs.typeaheadWaitMs) || 0; - //expressions used by typeahead - var parserResult = typeaheadParser.parse(attrs.typeahead); - //should it restrict model values to the ones selected from the popup only? var isEditable = originalScope.$eval(attrs.typeaheadEditable) !== false; + //binding to a variable that indicates if matches are being retrieved asynchronously var isLoadingSetter = $parse(attrs.typeaheadLoading).assign || angular.noop; + //a callback executed when a match is selected var onSelectCallback = $parse(attrs.typeaheadOnSelect); + var inputFormatter = attrs.typeaheadInputFormatter ? $parse(attrs.typeaheadInputFormatter) : undefined; + + //INTERNAL VARIABLES + + //model setter executed upon match selection + var $setModelValue = $parse(attrs.ngModel).assign; + + //expressions used by typeahead + var parserResult = typeaheadParser.parse(attrs.typeahead); + + //pop-up element used to display matches var popUpEl = angular.element(''); popUpEl.attr({ @@ -147,16 +157,25 @@ angular.module('ui.bootstrap.typeahead', ['ui.bootstrap.position']) }); modelCtrl.$formatters.push(function (modelValue) { + var candidateViewValue, emptyViewValue; var locals = {}; - locals[parserResult.itemName] = modelValue; - //it might happen that we don't have enough info to properly render input value - //we need to check for this - candidateViewValue = parserResult.viewMapper(originalScope, locals); - emptyViewValue = parserResult.viewMapper(originalScope, {}); + if (inputFormatter) { - return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue; + locals['$model'] = modelValue; + return inputFormatter(originalScope, locals); + + } else { + locals[parserResult.itemName] = modelValue; + + //it might happen that we don't have enough info to properly render input value + //we need to check for this situation and simply return model value if we can't apply custom formatting + candidateViewValue = parserResult.viewMapper(originalScope, locals); + emptyViewValue = parserResult.viewMapper(originalScope, {}); + + return candidateViewValue!== emptyViewValue ? candidateViewValue : modelValue; + } }); scope.select = function (activeIdx) {