diff --git a/src/ng/directive/ngRepeat.js b/src/ng/directive/ngRepeat.js index 11f35c56e84c..f7699b37d7c9 100644 --- a/src/ng/directive/ngRepeat.js +++ b/src/ng/directive/ngRepeat.js @@ -219,7 +219,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { } lhs = match[1]; - rhs = match[2]; + rhs = $parse(match[2]); trackByExp = match[3]; if (trackByExp) { @@ -255,8 +255,7 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { // - index: position var lastBlockMap = {}; - //watch props - $scope.$watchCollection(rhs, function ngRepeatAction(collection){ + var ngRepeatAction = function(collection){ var index, length, previousNode = $element[0], // current position of the node nextNode, @@ -382,7 +381,11 @@ var ngRepeatDirective = ['$parse', '$animate', function($parse, $animate) { } } lastBlockMap = nextBlockMap; - }); + }; + + ngRepeatAction(rhs($scope)); + //watch props + $scope.$watchCollection(rhs, ngRepeatAction); } }; diff --git a/test/ng/directive/ngRepeatSpec.js b/test/ng/directive/ngRepeatSpec.js index e267e0e9b029..6976b55ee48d 100644 --- a/test/ng/directive/ngRepeatSpec.js +++ b/test/ng/directive/ngRepeatSpec.js @@ -142,7 +142,7 @@ describe('ngRepeat', function() { scope.items = {age:20}; $compile('
')(scope); scope.$digest(); - expect($exceptionHandler.errors.shift().message).toMatch(/ng:badname/); + expect($exceptionHandler.errors.shift()[0].message).toMatch(/ng:badname/); }); @@ -757,7 +757,7 @@ describe('ngRepeat', function() { }; }); element = $compile('
')($rootScope); - expect(element.text()).toBe(''); + expect(element.text()).toBe('{{i}}{{i}}{{i}}'); $rootScope.$apply(); expect(element.text()).toBe('123'); })); @@ -1273,3 +1273,279 @@ describe('ngRepeat animations', function() { ); }); + +describe('ngRepeat nesting with other directives', function() { + var element, expectedCalls, actualCalls, $compile, scope, $exceptionHandler; + + beforeEach(module(function(_$compileProvider_) { + actualCalls = []; + _$compileProvider_.directive('callLog', valueFn({ + controller: function($scope, $attrs) { + actualCalls.push($attrs.callLog + '-controller'); + }, + compile: function compile(element, attrs) { + actualCalls.push(attrs.callLog + '-compile'); + return { + pre: function preLink(scope, element, attrs) { + actualCalls.push(attrs.callLog + '-preLink'); + }, + post: function postLink(scope, element, attrs) { + actualCalls.push(attrs.callLog + '-postLink'); + } + }; + } + })); + })); + + beforeEach(module(function($exceptionHandlerProvider) { + $exceptionHandlerProvider.mode('log'); + })); + + beforeEach(inject(function(_$compile_, $rootScope, _$exceptionHandler_) { + $compile = _$compile_; + $exceptionHandler = _$exceptionHandler_; + scope = $rootScope.$new(); + })); + + afterEach(function() { + if ($exceptionHandler.errors.length) { + dump(jasmine.getEnv().currentSpec.getFullName()); + dump('$exceptionHandler has errors'); + dump($exceptionHandler.errors); + expect($exceptionHandler.errors).toBe([]); + } + dealoc(element); + }); + + it('should preserve directive function call order when collections are empty', function() { + expectedCalls = [ + 'outer-compile', + 'middle-compile', + 'inner-compile' + ]; + actualCalls = []; + scope.outerItems = []; + scope.middleItems = []; + scope.innerItems = []; + element = $compile('
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
')(scope); + scope.$digest(); + expect(actualCalls).toEqual(expectedCalls); + }); + + it('should preserve directive function call order when outer collection is empty', function() { + expectedCalls = [ + 'outer-compile', + 'middle-compile', + 'inner-compile' + ]; + actualCalls = []; + scope.outerItems = []; + scope.middleItems = [1]; + scope.innerItems = [1]; + element = $compile('
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
')(scope); + scope.$digest(); + expect(actualCalls).toEqual(expectedCalls); + }); + + it('should preserve directive function call order when middle collection is empty', function() { + expectedCalls = [ + 'outer-compile', + 'middle-compile', + 'inner-compile', + 'outer-controller', + 'outer-preLink', + 'outer-postLink' + ]; + actualCalls = []; + scope.outerItems = [1]; + scope.middleItems = []; + scope.innerItems = [1]; + element = $compile('
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
')(scope); + scope.$digest(); + expect(actualCalls).toEqual(expectedCalls); + }); + + it('should preserve directive function call order when inner collection is empty', function() { + expectedCalls = [ + 'outer-compile', + 'middle-compile', + 'inner-compile', + 'outer-controller', + 'outer-preLink', + 'middle-controller', + 'middle-preLink', + 'middle-postLink', + 'outer-postLink' + ]; + actualCalls = []; + scope.outerItems = [1]; + scope.middleItems = [1]; + scope.innerItems = []; + element = $compile('
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
')(scope); + scope.$digest(); + expect(actualCalls).toEqual(expectedCalls); + }); + + it('should preserve directive function call order when collections are not empty', function() { + expectedCalls = [ + 'outer-compile', + 'middle-compile', + 'inner-compile', + 'outer-controller', + 'outer-preLink', + 'middle-controller', + 'middle-preLink', + 'inner-controller', + 'inner-preLink', + 'inner-postLink', + 'middle-postLink', + 'outer-postLink' + ]; + actualCalls = []; + scope.outerItems = [1]; + scope.middleItems = [1]; + scope.innerItems = [1]; + element = $compile('
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
')(scope); + scope.$digest(); + expect(actualCalls).toEqual(expectedCalls); + }); + + it('should preserve directive function call order when outer collection is modified', function() { + scope.outerItems = [1]; + scope.middleItems = [1]; + scope.innerItems = [1]; + element = $compile('
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
')(scope); + scope.$digest(); + + // GROW + expectedCalls = [ + 'outer-controller', + 'outer-preLink', + 'middle-controller', + 'middle-preLink', + 'inner-controller', + 'inner-preLink', + 'inner-postLink', + 'middle-postLink', + 'outer-postLink' + ]; + actualCalls = []; + scope.outerItems.push(2); + scope.$digest(); + expect(actualCalls).toEqual(expectedCalls); + + // SHRINK + expectedCalls = []; + actualCalls = []; + scope.outerItems.pop(); + scope.outerItems.shift(); + scope.$digest(); + expect(actualCalls).toEqual(expectedCalls); + }); + + it('should preserve directive function call order when middle collection is modified', function() { + scope.outerItems = [1]; + scope.middleItems = [1]; + scope.innerItems = [1]; + element = $compile('
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
')(scope); + scope.$digest(); + + // GROW + expectedCalls = [ + 'middle-controller', + 'middle-preLink', + 'inner-controller', + 'inner-preLink', + 'inner-postLink', + 'middle-postLink' + ]; + actualCalls = []; + scope.middleItems.push(2); + scope.$digest(); + expect(actualCalls).toEqual(expectedCalls); + + // SHRINK + expectedCalls = []; + actualCalls = []; + scope.middleItems.pop(); + scope.middleItems.shift(); + scope.$digest(); + expect(actualCalls).toEqual(expectedCalls); + }); + + it('should preserve directive function call order when inner collection is modified', function() { + scope.outerItems = [1]; + scope.middleItems = [1]; + scope.innerItems = [1]; + element = $compile('
' + + '
' + + '
' + + '
' + + '
' + + '
' + + '
')(scope); + scope.$digest(); + + // GROW + expectedCalls = [ + 'inner-controller', + 'inner-preLink', + 'inner-postLink' + ]; + actualCalls = []; + scope.innerItems.push(2); + scope.$digest(); + expect(actualCalls).toEqual(expectedCalls); + + // SHRINK + expectedCalls = []; + actualCalls = []; + scope.innerItems.pop(); + scope.innerItems.shift(); + scope.$digest(); + expect(actualCalls).toEqual(expectedCalls); + }); + +});