Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 3b921a3

Browse files
committed
feat(ngOptions): add $value variable for easier use of trackBy+selectAs
1 parent 840b5f0 commit 3b921a3

File tree

2 files changed

+23
-23
lines changed

2 files changed

+23
-23
lines changed

Diff for: src/ng/directive/ngOptions.js

+15-11
Original file line numberDiff line numberDiff line change
@@ -66,9 +66,7 @@ var ngOptionsMinErr = minErr('ngOptions');
6666
*
6767
* ### `select` **`as`** and **`track by`**
6868
*
69-
* <div class="alert alert-warning">
70-
* Be careful when using `select` **`as`** and **`track by`** in the same expression.
71-
* </div>
69+
* When using `select` **`as`** and **`track by`** in the same expression use the `$value` variable.
7270
*
7371
* Given this array of items on the $scope:
7472
*
@@ -110,6 +108,14 @@ var ngOptionsMinErr = minErr('ngOptions');
110108
* expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
111109
* is not matched against any `<option>` and the `<select>` appears as having no selected value.
112110
*
111+
* here is the fixed version of the broken example above.
112+
*
113+
* ```html
114+
* <select ng-options="item.subItem as item.label for item in items track by $value.id" ng-model="selected"></select>
115+
* ```
116+
* ```js
117+
* $scope.selected = $scope.items[0].subItem;
118+
* ```
113119
*
114120
* @param {string} ngModel Assignable AngularJS expression to data-bind to.
115121
* @param {comprehension_expression} ngOptions in one of the following forms:
@@ -285,7 +291,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
285291
function(value, locals) { return trackByFn(scope, locals); } :
286292
function getHashOfValue(value) { return hashKey(value); };
287293
var getTrackByValue = function(value, key) {
288-
return getTrackByValueFn(value, getLocals(value, key));
294+
return getTrackByValueFn(value, getLocals(value, key, true));
289295
};
290296

291297
var displayFn = $parse(match[2] || match[1]);
@@ -294,12 +300,10 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
294300
var valuesFn = $parse(match[8]);
295301

296302
var locals = {};
297-
var getLocals = keyName ? function(value, key) {
298-
locals[keyName] = key;
299-
locals[valueName] = value;
300-
return locals;
301-
} : function(value) {
303+
var getLocals = function(value, key, isViewValue) {
304+
if (keyName) locals[keyName] = key;
302305
locals[valueName] = value;
306+
locals['$value'] = isViewValue ? value : viewValueFn(value, locals);
303307
return locals;
304308
};
305309

@@ -345,7 +349,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
345349
var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
346350
var value = optionValues[key];
347351

348-
var locals = getLocals(value, key);
352+
var locals = getLocals(value, key, true);
349353
var selectValue = getTrackByValueFn(value, locals);
350354
watchedArray.push(selectValue);
351355

@@ -378,7 +382,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
378382
for (var index = 0; index < optionValuesLength; index++) {
379383
var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
380384
var value = optionValues[key];
381-
var locals = getLocals(value, key);
385+
var locals = getLocals(value, key, false);
382386
var viewValue = viewValueFn(scope, locals);
383387
var selectValue = getTrackByValueFn(viewValue, locals);
384388
var label = displayFn(scope, locals);

Diff for: test/ng/directive/ngOptionsSpec.js

+8-12
Original file line numberDiff line numberDiff line change
@@ -1509,22 +1509,18 @@ describe('ngOptions', function() {
15091509
});
15101510

15111511

1512-
/**
1513-
* This behavior is broken and should probably be cleaned up later as track by and select as
1514-
* aren't compatible.
1515-
*/
15161512
describe('selectAs+trackBy expression', function() {
15171513
beforeEach(function() {
15181514
scope.arr = [{subItem: {label: 'ten', id: 10}}, {subItem: {label: 'twenty', id: 20}}];
15191515
scope.obj = {'10': {subItem: {id: 10, label: 'ten'}}, '20': {subItem: {id: 20, label: 'twenty'}}};
15201516
});
15211517

15221518

1523-
it('It should use the "value" variable to represent items in the array as well as for the ' +
1519+
it('It should use the "$value" variable to represent items in the array as well as for the ' +
15241520
'selected values in track by expression (single&array)', function() {
15251521
createSelect({
15261522
'ng-model': 'selected',
1527-
'ng-options': 'item.subItem as item.subItem.label for item in arr track by (item.id || item.subItem.id)'
1523+
'ng-options': 'item.subItem as item.subItem.label for item in arr track by $value.id'
15281524
});
15291525

15301526
// First test model -> view
@@ -1558,12 +1554,12 @@ describe('ngOptions', function() {
15581554
});
15591555

15601556

1561-
it('It should use the "value" variable to represent items in the array as well as for the ' +
1557+
it('It should use the "$value" variable to represent items in the array as well as for the ' +
15621558
'selected values in track by expression (multiple&array)', function() {
15631559
createSelect({
15641560
'ng-model': 'selected',
15651561
'multiple': true,
1566-
'ng-options': 'item.subItem as item.subItem.label for item in arr track by (item.id || item.subItem.id)'
1562+
'ng-options': 'item.subItem as item.subItem.label for item in arr track by $value.id'
15671563
});
15681564

15691565
// First test model -> view
@@ -1599,12 +1595,12 @@ describe('ngOptions', function() {
15991595
});
16001596

16011597

1602-
it('It should use the "value" variable to represent items in the array as well as for the ' +
1598+
it('It should use the "$value" variable to represent items in the array as well as for the ' +
16031599
'selected values in track by expression (multiple&object)', function() {
16041600
createSelect({
16051601
'ng-model': 'selected',
16061602
'multiple': true,
1607-
'ng-options': 'val.subItem as val.subItem.label for (key, val) in obj track by (val.id || val.subItem.id)'
1603+
'ng-options': 'val.subItem as val.subItem.label for (key, val) in obj track by $value.id'
16081604
});
16091605

16101606
// First test model -> view
@@ -1644,11 +1640,11 @@ describe('ngOptions', function() {
16441640
});
16451641

16461642

1647-
it('It should use the "value" variable to represent items in the array as well as for the ' +
1643+
it('It should use the "$value" variable to represent items in the array as well as for the ' +
16481644
'selected values in track by expression (single&object)', function() {
16491645
createSelect({
16501646
'ng-model': 'selected',
1651-
'ng-options': 'val.subItem as val.subItem.label for (key, val) in obj track by (val.id || val.subItem.id)'
1647+
'ng-options': 'val.subItem as val.subItem.label for (key, val) in obj track by $value.id'
16521648
});
16531649

16541650
// First test model -> view

0 commit comments

Comments
 (0)