From 49f286bcf254b2d1307638b1e20f02820336c427 Mon Sep 17 00:00:00 2001 From: nswbmw Date: Wed, 27 May 2015 14:47:44 +0800 Subject: [PATCH] feat(ngRepeat): add $prev and $next --- src/ng/directive/ngRepeat.js | 13 ++++++-- test/ng/directive/ngRepeatSpec.js | 51 ++++++++++++++++++++++++++++++- 2 files changed, 60 insertions(+), 4 deletions(-) diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js index 06180f7022ce..0ac4dcace943 100644 --- a/src/ng/directive/ngRepeat.js +++ b/src/ng/directive/ngRepeat.js @@ -19,6 +19,8 @@ * | `$last` | {@type boolean} | true if the repeated element is last in the iterator. | * | `$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). | + * | `$prev` | {@type number} {@type string} {@type null} | previous index/key of the iterator position `$index` (null if `$index` is `$first`). | + * | `$next` | {@type number} {@type string} {@type null} | next index/key of the iterator position `$index` (null if `$index` is `$last`). | * * Creating aliases for these properties is possible with {@link ng.directive:ngInit `ngInit`}. * This may be useful when, for instance, nesting ngRepeats. @@ -291,7 +293,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { var NG_REMOVED = '$$NG_REMOVED'; var ngRepeatMinErr = minErr('ngRepeat'); - var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength) { + var updateScope = function(scope, index, valueIdentifier, value, keyIdentifier, key, arrayLength, prev, next) { // TODO(perf): generate setters to shave off ~40ms or 1-1.5% scope[valueIdentifier] = value; if (keyIdentifier) scope[keyIdentifier] = key; @@ -302,6 +304,8 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { // jshint bitwise: false scope.$odd = !(scope.$even = (index&1) === 0); // jshint bitwise: true + scope.$prev = prev; + scope.$next = next; }; var getBlockStart = function(block) { @@ -398,6 +402,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { nextBlockMap = createMap(), collectionLength, key, value, // key/value of iteration + prev, next, trackById, trackByIdFn, collectionKeys, @@ -471,6 +476,8 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { for (index = 0; index < collectionLength; index++) { key = (collection === collectionKeys) ? index : collectionKeys[index]; value = collection[key]; + prev = (index > 0 ? ((collection === collectionKeys) ? (index - 1) : collectionKeys[index - 1]) : null); + next = (index < collectionLength - 1 ? ((collection === collectionKeys) ? (index + 1) : collectionKeys[index + 1]) : null); block = nextBlockOrder[index]; if (block.scope) { @@ -489,7 +496,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { $animate.move(getBlockNodes(block.clone), null, jqLite(previousNode)); } previousNode = getBlockEnd(block); - updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength); + updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength, prev, next); } else { // new item which we don't know about $transclude(function ngRepeatTransclude(clone, scope) { @@ -506,7 +513,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { // by a directive with templateUrl when its template arrives. block.clone = clone; nextBlockMap[block.id] = block; - updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength); + updateScope(block.scope, index, valueIdentifier, value, keyIdentifier, key, collectionLength, prev, next); }); } } diff --git a/test/ng/directive/ngRepeatSpec.js b/test/ng/directive/ngRepeatSpec.js index 0c7070fb5e6d..4ec4b394f980 100644 --- a/test/ng/directive/ngRepeatSpec.js +++ b/test/ng/directive/ngRepeatSpec.js @@ -482,6 +482,8 @@ describe('ngRepeat', function() { '$last', '$even', '$odd', + '$prev', + '$next', 'obj[key]', 'obj["key"]', 'obj[\'key\']', @@ -673,7 +675,6 @@ describe('ngRepeat', function() { expect(element.text()).toEqual('misko:m:true-false-true|'); }); - it('should expose iterator position as $even and $odd when iterating over objects', function() { element = $compile( @@ -694,6 +695,54 @@ describe('ngRepeat', function() { expect(element.text()).toBe('misko:m:true-false|doug:d:false-true|'); }); + it('should expose iterator position as $first, $prev, $next and $last when iterating over arrays', + function() { + element = $compile( + '')(scope); + scope.items = ['misko', 'shyam', 'doug']; + scope.$digest(); + expect(element.text()). + toEqual('misko:true--1-false|shyam:false-0-2-false|doug:false-1--true|'); + + scope.items.push('frodo'); + scope.$digest(); + expect(element.text()). + toEqual('misko:true--1-false|' + + 'shyam:false-0-2-false|' + + 'doug:false-1-3-false|' + + 'frodo:false-2--true|'); + + scope.items.pop(); + scope.items.pop(); + scope.$digest(); + expect(element.text()).toEqual('misko:true--1-false|shyam:false-0--true|'); + + scope.items.pop(); + scope.$digest(); + expect(element.text()).toEqual('misko:true---true|'); + }); + + it('should expose iterator position as $first, $prev, $next and $last when iterating over objects', + function() { + element = $compile( + '')(scope); + scope.items = {'misko':'m', 'shyam':'s', 'doug':'d'}; + scope.$digest(); + expect(element.text()). + toEqual('misko:m:true--shyam-false|' + + 'shyam:s:false-misko-doug-false|' + + 'doug:d:false-shyam--true|'); + delete scope.items.doug; + scope.$digest(); + expect(element.text()).toEqual('misko:m:true--shyam-false|shyam:s:false-misko--true|'); + delete scope.items.shyam; + scope.$digest(); + expect(element.text()).toEqual('misko:m:true---true|'); + }); it('should calculate $first, $middle and $last when we filter out properties from an obj', function() { element = $compile(