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

Commit

Permalink
feat(ngOptions): add $value variable for easier use of trackBy+selectAs
Browse files Browse the repository at this point in the history
  • Loading branch information
icholy committed Jan 5, 2017
1 parent c00903b commit 4962888
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 23 deletions.
26 changes: 15 additions & 11 deletions src/ng/directive/ngOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,7 @@ var ngOptionsMinErr = minErr('ngOptions');
*
* ### `select` **`as`** and **`track by`**
*
* <div class="alert alert-warning">
* Be careful when using `select` **`as`** and **`track by`** in the same expression.
* </div>
* When using `select` **`as`** and **`track by`** in the same expression use the `$value` variable.
*
* Given this array of items on the $scope:
*
Expand Down Expand Up @@ -110,6 +108,14 @@ var ngOptionsMinErr = minErr('ngOptions');
* expression evaluates to `items[0].subItem.id` (which is undefined). As a result, the model value
* is not matched against any `<option>` and the `<select>` appears as having no selected value.
*
* here is the fixed version of the broken example above.
*
* ```html
* <select ng-options="item.subItem as item.label for item in items track by $value.id" ng-model="selected"></select>
* ```
* ```js
* $scope.selected = $scope.items[0].subItem;
* ```
*
* @param {string} ngModel Assignable angular expression to data-bind to.
* @param {string=} name Property name of the form under which the control is published.
Expand Down Expand Up @@ -283,7 +289,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
function(value, locals) { return trackByFn(scope, locals); } :
function getHashOfValue(value) { return hashKey(value); };
var getTrackByValue = function(value, key) {
return getTrackByValueFn(value, getLocals(value, key));
return getTrackByValueFn(value, getLocals(value, key, true));
};

var displayFn = $parse(match[2] || match[1]);
Expand All @@ -292,12 +298,10 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
var valuesFn = $parse(match[8]);

var locals = {};
var getLocals = keyName ? function(value, key) {
locals[keyName] = key;
locals[valueName] = value;
return locals;
} : function(value) {
var getLocals = function(value, key, isViewValue) {
if (keyName) locals[keyName] = key;
locals[valueName] = value;
locals['$value'] = isViewValue ? value : viewValueFn(value, locals);
return locals;
};

Expand Down Expand Up @@ -343,7 +347,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
var value = optionValues[key];

var locals = getLocals(value, key);
var locals = getLocals(value, key, true);
var selectValue = getTrackByValueFn(value, locals);
watchedArray.push(selectValue);

Expand Down Expand Up @@ -376,7 +380,7 @@ var ngOptionsDirective = ['$compile', '$document', '$parse', function($compile,
for (var index = 0; index < optionValuesLength; index++) {
var key = (optionValues === optionValuesKeys) ? index : optionValuesKeys[index];
var value = optionValues[key];
var locals = getLocals(value, key);
var locals = getLocals(value, key, false);
var viewValue = viewValueFn(scope, locals);
var selectValue = getTrackByValueFn(viewValue, locals);
var label = displayFn(scope, locals);
Expand Down
20 changes: 8 additions & 12 deletions test/ng/directive/ngOptionsSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -1453,22 +1453,18 @@ describe('ngOptions', function() {
});


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


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

// First test model -> view
Expand Down Expand Up @@ -1502,12 +1498,12 @@ describe('ngOptions', function() {
});


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

// First test model -> view
Expand Down Expand Up @@ -1543,12 +1539,12 @@ describe('ngOptions', function() {
});


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

// First test model -> view
Expand Down Expand Up @@ -1588,11 +1584,11 @@ describe('ngOptions', function() {
});


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

// First test model -> view
Expand Down

0 comments on commit 4962888

Please sign in to comment.