Skip to content

Commit

Permalink
docs(ngOptions): explain the caveats of using select as and `track …
Browse files Browse the repository at this point in the history
…by` together

Changes:

* Modify warning message to indicate that `track by` can be used with `select as`,
  but subject to certain limitations.
* Provide both a working and an non-working example.
* Explain why the latter does not work.

Closes angular#13007
  • Loading branch information
ryanhart2 authored and gkalpak committed Oct 27, 2015
1 parent 794d1c1 commit a995ee1
Showing 1 changed file with 26 additions and 17 deletions.
43 changes: 26 additions & 17 deletions src/ng/directive/ngOptions.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,13 @@ var ngOptionsMinErr = minErr('ngOptions');
* ### `select` **`as`** and **`track by`**
*
* <div class="alert alert-warning">
* Do not use `select` **`as`** and **`track by`** in the same expression. They are not designed to work together.
* Be careful when using `select` **`as`** and **`track by`** in the same expression.
* </div>
*
* Consider the following example:
*
* ```html
* <select ng-options="item.subItem as item.label for item in values track by item.id" ng-model="selected"></select>
* ```
* Given this array of items on the $scope:
*
* ```js
* $scope.values = [{
* $scope.items = [{
* id: 1,
* label: 'aLabel',
* subItem: { name: 'aSubItem' }
Expand All @@ -85,20 +81,33 @@ var ngOptionsMinErr = minErr('ngOptions');
* label: 'bLabel',
* subItem: { name: 'bSubItem' }
* }];
* ```
*
* $scope.selected = { name: 'aSubItem' };
* This will work:
*
* ```html
* <select ng-options="item as item.label for item in items track by item.id" ng-model="selected"></select>
* ```
* ```js
* $scope.selected = $scope.items[0];
* ```
*
* but this will not work:
*
* With the purpose of preserving the selection, the **`track by`** expression is always applied to the element
* of the data source (to `item` in this example). To calculate whether an element is selected, we do the
* following:
* ```html
* <select ng-options="item.subItem as item.label for item in items track by item.id" ng-model="selected"></select>
* ```
* ```js
* $scope.selected = $scope.items[0].subItem;
* ```
*
* 1. Apply **`track by`** to the elements in the array. In the example: `[1, 2]`
* 2. Apply **`track by`** to the already selected value in `ngModel`.
* In the example: this is not possible as **`track by`** refers to `item.id`, but the selected
* value from `ngModel` is `{name: 'aSubItem'}`, so the **`track by`** expression is applied to
* a wrong object, the selected element can't be found, `<select>` is always reset to the "not
* selected" option.
* In both examples, the **`track by`** expression is applied successfully to each `item` in the
* `items` array. Because the selected option has been set programmatically in the controller, the
* **`track by`** expression is also applied to the `ngModel` value. In the first example, the
* `ngModel` value is `items[0]` and the **`track by`** expression evaluates to `items[0].id` with
* no issue. In the second example, the `ngModel` value is `items[0].subItem` and the **`track by`**
* 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.
*
*
* @param {string} ngModel Assignable angular expression to data-bind to.
Expand Down

0 comments on commit a995ee1

Please sign in to comment.