diff --git a/src/select.js b/src/select.js
index 3b81924b1..0f9c76253 100644
--- a/src/select.js
+++ b/src/select.js
@@ -109,13 +109,22 @@
expression);
}
- return {
+ var result = {
itemName: match[2], // (lhs) Left-hand side,
source: $parse(match[3]),
trackByExp: match[4],
modelMapper: $parse(match[1] || match[2])
};
+ if (result.trackByExp) {
+ var propMatch = result.trackByExp.match(/\S+\.(\S+)/);
+
+ if (propMatch) {
+ result.trackByProp = propMatch[1];
+ }
+ }
+
+ return result;
};
self.getGroupNgRepeatExpression = function() {
@@ -657,49 +666,93 @@
});
//From model --> view
- ngModel.$formatters.unshift(function (inputValue) {
- var data = $select.parserResult.source (scope, { $select : {search:''}}), //Overwrite $search
- locals = {},
- result;
- if (data){
- if ($select.multiple){
- var resultMultiple = [];
- var checkFnMultiple = function(list, value){
- if (!list || !list.length) return;
- for (var p = list.length - 1; p >= 0; p--) {
- locals[$select.parserResult.itemName] = list[p];
- result = $select.parserResult.modelMapper(scope, locals);
- if (result == value){
- resultMultiple.unshift(list[p]);
- return true;
- }
+ ngModel.$formatters.unshift(function() {
+ var matchesOptionMultiple = function(options, inputValue, resultMultiple) {
+ var locals, result;
+ var match = false;
+
+ if (options && options.length) {
+ for (var i = options.length - 1; i >= 0 && !match; i--) {
+ locals = {};
+ locals[$select.parserResult.itemName] = options[i];
+ result = $select.parserResult.modelMapper(scope, locals);
+
+ if ($select.parserResult.trackByProp && inputValue) {
+ match = angular.equals(result[$select.parserResult.trackByProp], inputValue[$select.parserResult.trackByProp]);
+ } else {
+ match = angular.equals(result, inputValue);
}
- return false;
- };
- if (!inputValue) return resultMultiple; //If ngModel was undefined
+
+ if (match) {
+ resultMultiple.unshift(options[i]);
+ }
+ }
+ }
+
+ return match;
+ };
+
+ var matchesOption = function(option, inputValue) {
+ var result, locals = {};
+ locals[$select.parserResult.itemName] = option;
+ result = $select.parserResult.modelMapper(scope, locals);
+
+ if ($select.parserResult.trackByProp && inputValue) {
+ return angular.equals(result[$select.parserResult.trackByProp], inputValue[$select.parserResult.trackByProp]);
+ }
+
+ return angular.equals(result, inputValue);
+ };
+
+ var handleMultiple = function(data, inputValue) {
+ var resultMultiple = [];
+
+ if (inputValue) {
for (var k = inputValue.length - 1; k >= 0; k--) {
- if (!checkFnMultiple($select.selected, inputValue[k])){
- checkFnMultiple(data, inputValue[k]);
+ if (!matchesOptionMultiple($select.selected, inputValue[k], resultMultiple)) {
+ matchesOptionMultiple(data, inputValue[k], resultMultiple);
}
}
- return resultMultiple;
- }else{
- var checkFnSingle = function(d){
- locals[$select.parserResult.itemName] = d;
- result = $select.parserResult.modelMapper(scope, locals);
- return result == inputValue;
- };
- //If possible pass same object stored in $select.selected
- if ($select.selected && checkFnSingle($select.selected)) {
- return $select.selected;
+ }
+
+ return resultMultiple;
+ };
+
+ var handleSingle = function(data, inputValue) {
+ var result = inputValue;
+
+ if (inputValue) {
+ // If possible pass same object stored in $select.selected
+ if ($select.selected && matchesOption($select.selected, inputValue)) {
+ result = $select.selected;
+ } else {
+ for (var i = data.length - 1; i >= 0; i--) {
+ if (matchesOption(data[i], inputValue)) {
+ result = data[i];
+ break;
+ }
+ }
}
- for (var i = data.length - 1; i >= 0; i--) {
- if (checkFnSingle(data[i])) return data[i];
+ }
+
+ return result;
+ };
+
+ return function(inputValue) {
+ var data = $select.parserResult.source(scope, {$select: {search: ''}}); //Overwrite $search
+ var result = inputValue;
+
+ if (data) {
+ if ($select.multiple) {
+ result = handleMultiple(data, inputValue);
+ } else {
+ result = handleSingle(data, inputValue);
}
}
- }
- return inputValue;
- });
+
+ return result;
+ };
+ }());
//Set reference to ngModel from uiSelectCtrl
$select.ngModel = ngModel;
diff --git a/test/select.spec.js b/test/select.spec.js
index 461ec3c13..bff5f971d 100644
--- a/test/select.spec.js
+++ b/test/select.spec.js
@@ -124,7 +124,7 @@ describe('ui-select tests', function() {
var $select = el.scope().$select;
$select.open = true;
scope.$digest();
- };
+ }
// Tests
@@ -157,6 +157,38 @@ describe('ui-select tests', function() {
expect(getMatchLabel(el)).toEqual('Adam');
});
+ it('should correctly render initial state with track by feature', function() {
+ scope.selection.selected = { name: 'Samantha', email: 'something different than array source', group: 'foobar', age: 31 };
+
+ var el = compileTemplate(
+ ' \
+ {{$select.selected.name}} \
+ \
+ \
+ \
+ \
+ '
+ );
+
+ expect(getMatchLabel(el)).toEqual('Samantha');
+ });
+
+ it('should not error when using track by feature and ng-model reference is set to null', function() {
+ scope.selection.selected = null;
+
+ var el = compileTemplate(
+ ' \
+ {{$select.selected.name}} \
+ \
+ \
+ \
+ \
+ '
+ );
+
+ expect(getMatchLabel(el)).toEqual('');
+ });
+
it('should display the choices when activated', function() {
var el = createUiSelect();
@@ -383,7 +415,7 @@ describe('ui-select tests', function() {
beforeEach(function() {
disablePerson({
disableAttr : 'active',
- disableBool : false,
+ disableBool : false
});
this.el = createUiSelect({
disabled: '!person.active'
@@ -727,7 +759,7 @@ describe('ui-select tests', function() {
});
- it('should invoke hover callback', function(){
+ it('should invoke hover callback', function() {
var highlighted;
scope.onHighlightFn = function ($item) {
@@ -955,7 +987,7 @@ describe('ui-select tests', function() {
scope.fetchFromServer = function(searching){
if (searching == 's')
- return scope.people
+ return scope.people;
if (searching == 'o'){
scope.people = []; //To simulate cases were previously selected item isnt in the list anymore
@@ -963,11 +995,11 @@ describe('ui-select tests', function() {
};
- setSearchText(el, 'r')
+ setSearchText(el, 'r');
clickItem(el, 'Samantha');
expect(getMatchLabel(el)).toBe('Samantha');
- setSearchText(el, 'o')
+ setSearchText(el, 'o');
expect(getMatchLabel(el)).toBe('Samantha');
});
@@ -1080,6 +1112,26 @@ describe('ui-select tests', function() {
expect(el.find('.ui-select-match-item').length).toBe(2);
});
+ it('should render initial selected items with track by feature', function() {
+ scope.selection.selectedMultiple = [
+ { name: 'Samantha', email: 'something different than array source', group: 'foobar', age: 31 },
+ { name: 'Wladimir', email: 'something different than array source', group: 'FooBar', age: 31 },
+ ];
+
+ var el = compileTemplate(
+ ' \
+ {{$item.name}} <{{$item.email}}> \
+ \
+ \
+ \
+ \
+ '
+ );
+
+ expect(el.scope().$select.selected.length).toBe(2);
+ expect(el.find('.ui-select-match-item').length).toBe(2);
+ });
+
it('should remove item by pressing X icon', function() {
scope.selection.selectedMultiple = [scope.people[4], scope.people[5]]; //Wladimir & Samantha
var el = createUiSelectMultiple();
@@ -1191,13 +1243,13 @@ describe('ui-select tests', function() {
var searchInput = el.find('.ui-select-search');
expect(isDropdownOpened(el)).toEqual(false);
- triggerKeydown(searchInput, Key.Left)
- triggerKeydown(searchInput, Key.Left)
+ triggerKeydown(searchInput, Key.Left);
+ triggerKeydown(searchInput, Key.Left);
expect(isDropdownOpened(el)).toEqual(false);
expect(el.scope().$select.activeMatchIndex).toBe(el.scope().$select.selected.length - 2);
- triggerKeydown(searchInput, Key.Left)
- triggerKeydown(searchInput, Key.Left)
- triggerKeydown(searchInput, Key.Left)
+ triggerKeydown(searchInput, Key.Left);
+ triggerKeydown(searchInput, Key.Left);
+ triggerKeydown(searchInput, Key.Left);
expect(el.scope().$select.activeMatchIndex).toBe(0);
});
@@ -1208,9 +1260,9 @@ describe('ui-select tests', function() {
var el = createUiSelectMultiple();
var searchInput = el.find('.ui-select-search');
- el.scope().$select.activeMatchIndex = 3
- triggerKeydown(searchInput, Key.Left)
- triggerKeydown(searchInput, Key.Left)
+ el.scope().$select.activeMatchIndex = 3;
+ triggerKeydown(searchInput, Key.Left);
+ triggerKeydown(searchInput, Key.Left);
expect(el.scope().$select.activeMatchIndex).toBe(1);
});
@@ -1221,9 +1273,9 @@ describe('ui-select tests', function() {
var el = createUiSelectMultiple();
var searchInput = el.find('.ui-select-search');
- el.scope().$select.activeMatchIndex = 0
- triggerKeydown(searchInput, Key.Right)
- triggerKeydown(searchInput, Key.Right)
+ el.scope().$select.activeMatchIndex = 0;
+ triggerKeydown(searchInput, Key.Right);
+ triggerKeydown(searchInput, Key.Right);
expect(el.scope().$select.activeMatchIndex).toBe(2);
});
@@ -1235,7 +1287,7 @@ describe('ui-select tests', function() {
var searchInput = el.find('.ui-select-search');
expect(isDropdownOpened(el)).toEqual(false);
- triggerKeydown(searchInput, Key.Down)
+ triggerKeydown(searchInput, Key.Down);
expect(isDropdownOpened(el)).toEqual(true);
});
@@ -1270,7 +1322,7 @@ describe('ui-select tests', function() {
var searchInput = el.find('.ui-select-search');
expect(isDropdownOpened(el)).toEqual(false);
- triggerKeydown(searchInput, Key.Down)
+ triggerKeydown(searchInput, Key.Down);
expect(isDropdownOpened(el)).toEqual(true);
clickItem(el, 'Wladimir');
@@ -1286,7 +1338,7 @@ describe('ui-select tests', function() {
var searchInput = el.find('.ui-select-search');
expect(isDropdownOpened(el)).toEqual(false);
- triggerKeydown(searchInput, Key.Down)
+ triggerKeydown(searchInput, Key.Down);
expect(isDropdownOpened(el)).toEqual(true);
clickItem(el, 'Wladimir');
@@ -1302,9 +1354,9 @@ describe('ui-select tests', function() {
var searchInput = el.find('.ui-select-search');
expect(isDropdownOpened(el)).toEqual(false);
- triggerKeydown(searchInput, Key.Down)
+ triggerKeydown(searchInput, Key.Down);
expect(isDropdownOpened(el)).toEqual(true);
- triggerKeydown(searchInput, Key.Escape)
+ triggerKeydown(searchInput, Key.Escape);
expect(isDropdownOpened(el)).toEqual(false);
});
@@ -1315,8 +1367,8 @@ describe('ui-select tests', function() {
var el = createUiSelectMultiple();
var searchInput = el.find('.ui-select-search');
- triggerKeydown(searchInput, Key.Down)
- triggerKeydown(searchInput, Key.Enter)
+ triggerKeydown(searchInput, Key.Down);
+ triggerKeydown(searchInput, Key.Enter);
expect(scope.selection.selectedMultiple.length).toEqual(2);
});
@@ -1328,9 +1380,9 @@ describe('ui-select tests', function() {
triggerKeydown(searchInput, Key.Down); //Open dropdown
- el.scope().$select.activeIndex = 0
- triggerKeydown(searchInput, Key.Down)
- triggerKeydown(searchInput, Key.Down)
+ el.scope().$select.activeIndex = 0;
+ triggerKeydown(searchInput, Key.Down);
+ triggerKeydown(searchInput, Key.Down);
expect(el.scope().$select.activeIndex).toBe(2);
});
@@ -1342,20 +1394,13 @@ describe('ui-select tests', function() {
triggerKeydown(searchInput, Key.Down); //Open dropdown
- el.scope().$select.activeIndex = 5
- triggerKeydown(searchInput, Key.Up)
- triggerKeydown(searchInput, Key.Up)
+ el.scope().$select.activeIndex = 5;
+ triggerKeydown(searchInput, Key.Up);
+ triggerKeydown(searchInput, Key.Up);
expect(el.scope().$select.activeIndex).toBe(3);
});
- it('should render initial selected items', function() {
- scope.selection.selectedMultiple = [scope.people[4], scope.people[5]]; //Wladimir & Samantha
- var el = createUiSelectMultiple();
- expect(el.scope().$select.selected.length).toBe(2);
- expect(el.find('.ui-select-match-item').length).toBe(2);
- });
-
it('should parse the items correctly using single property binding', function() {
scope.selection.selectedMultiple = ['wladimir@email.com', 'samantha@email.com'];
@@ -1397,7 +1442,7 @@ describe('ui-select tests', function() {
});
- it('should format view value correctly when using single property binding and refresh funcion', function () {
+ it('should format view value correctly when using single property binding and refresh function', function () {
scope.selection.selectedMultiple = ['wladimir@email.com', 'samantha@email.com'];
@@ -1417,7 +1462,7 @@ describe('ui-select tests', function() {
scope.fetchFromServer = function(searching){
if (searching == 'n')
- return scope.people
+ return scope.people;
if (searching == 'o'){
scope.people = []; //To simulate cases were previously selected item isnt in the list anymore
@@ -1425,13 +1470,13 @@ describe('ui-select tests', function() {
};
- setSearchText(el, 'n')
+ setSearchText(el, 'n');
clickItem(el, 'Nicole');
expect(el.find('.ui-select-match-item [uis-transclude-append]:not(.ng-hide)').text())
.toBe("Wladimir Samantha Nicole ");
- setSearchText(el, 'o')
+ setSearchText(el, 'o');
expect(el.find('.ui-select-match-item [uis-transclude-append]:not(.ng-hide)').text())
.toBe("Wladimir Samantha Nicole ");