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

feat(ngRepeat): add index as property #7802

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 16 additions & 6 deletions src/ng/directive/ngRepeat.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
* | `$even` | {@type boolean} | true if the iterator position `$index` is even (otherwise false). |
* | `$odd` | {@type boolean} | true if the iterator position `$index` is odd (otherwise false). |
*
* Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}.
* Creating an alternative name for the property `$index` is possible with the `index as` syntax.
* This may be useful when, for instance, nesting ngRepeats.
*
* # Special repeat start and end points
Expand Down Expand Up @@ -90,6 +90,15 @@
*
* For example: `(name, age) in {'adam':10, 'amalie':12}`.
*
* * `variable in expression index as property_name` - You can also provide an optional property name to be used
* as the index. If no property name is specified the default property name `$index` is used.
*
* For example: `item in items` is equivalent to `item in items index as $index`. This implies that the
* property `$index` is used to specify the index.
*
* For example: `item in items index as myIndex` can be used when there is a need to keep the index property
* into a user defined property name. This is useful when dealing with nested ng-repeats.
*
* * `variable in expression track by tracking_expression` – You can also provide an optional tracking function
* which can be used to associate the objects in the collection with the DOM elements. If no tracking function
* is specified the ng-repeat associates elements by identity in the collection. It is an error to have
Expand Down Expand Up @@ -208,19 +217,20 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
$$tlb: true,
link: function($scope, $element, $attr, ctrl, $transclude){
var expression = $attr.ngRepeat;
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),
trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
var match = expression.match(/^\s*([\s\S]+?)\s+in\s+([\s\S]+?)(?:\s+index\s+as\s+([\s\S]+?))?(?:\s+track\s+by\s+([\s\S]+?))?\s*$/),
indexBy, trackByExp, trackByExpGetter, trackByIdExpFn, trackByIdArrayFn, trackByIdObjFn,
lhs, rhs, valueIdentifier, keyIdentifier,
hashFnLocals = {$id: hashKey};

if (!match) {
throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ track by _id_]' but got '{0}'.",
throw ngRepeatMinErr('iexp', "Expected expression in form of '_item_ in _collection_[ index as _property_][ track by _id_]' but got '{0}'.",
expression);
}

lhs = match[1];
rhs = match[2];
trackByExp = match[3];
indexBy = match[3] || '$index';
trackByExp = match[4];

if (trackByExp) {
trackByExpGetter = $parse(trackByExp);
Expand Down Expand Up @@ -275,7 +285,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) {
var updateScope = function(scope, index) {
scope[valueIdentifier] = value;
if (keyIdentifier) scope[keyIdentifier] = key;
scope.$index = index;
scope[indexBy] = index;
scope.$first = (index === 0);
scope.$last = (index === (arrayLength - 1));
scope.$middle = !(scope.$first || scope.$last);
Expand Down
39 changes: 38 additions & 1 deletion test/ng/directive/ngRepeatSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ describe('ngRepeat', function() {
element = jqLite('<ul><li ng-repeat="i dont parse"></li></ul>');
$compile(element)(scope);
expect($exceptionHandler.errors.shift()[0].message).
toMatch(/^\[ngRepeat:iexp\] Expected expression in form of '_item_ in _collection_\[ track by _id_\]' but got 'i dont parse'\./);
toMatch(/^\[ngRepeat:iexp\] Expected expression in form of '_item_ in _collection_\[ index as _property_\]\[ track by _id_\]' but got 'i dont parse'\./);
});


Expand Down Expand Up @@ -461,6 +461,29 @@ describe('ngRepeat', function() {
});


it('should be possible to define the `$index` property when iterating over arrays',
function() {
element = $compile(
'<ul>' +
'<li ng-repeat="item in items index as myIndex">{{item}}:{{myIndex}}|</li>' +
'</ul>')(scope);
scope.items = ['misko', 'shyam', 'frodo'];
scope.$digest();
expect(element.text()).toEqual('misko:0|shyam:1|frodo:2|');
});

it('should be possible to define the `$index` property when iterating over arrays',
function() {
element = $compile(
'<ul>' +
'<li ng-repeat="(key, val) in items index as myIndex">{{key}}:{{val}}:{{myIndex}}|</li>' +
'</ul>')(scope);
scope.items = {'misko':'m', 'shyam':'s', 'frodo':'f'};
scope.$digest();
expect(element.text()).toEqual('frodo:f:0|misko:m:1|shyam:s:2|');
});


it('should expose iterator position as $first, $middle and $last when iterating over arrays',
function() {
element = $compile(
Expand Down Expand Up @@ -618,6 +641,20 @@ describe('ngRepeat', function() {
});


it('should be possible to use `index as` in nested arrays', function() {
element = $compile(
'<ul>' +
'<li ng-repeat="item in items index as outerIndex">' +
'<div ng-repeat="item in items index as innerIndex">{{outerIndex}}:{{innerIndex}}|</div>X' +
'</li>' +
'</ul>')(scope);
scope.items = [0, 1, 2];
scope.$digest();

expect(element.text()).toEqual('0:0|0:1|0:2|X1:0|1:1|1:2|X2:0|2:1|2:2|X');
});


it('should ignore non-array element properties when iterating over an array', function() {
element = $compile('<ul><li ng-repeat="item in array">{{item}}|</li></ul>')(scope);
scope.array = ['a', 'b', 'c'];
Expand Down