From d314eee14df2b2ca5501593c9b4911ae52c41b04 Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Thu, 21 Nov 2013 21:54:59 -0800 Subject: [PATCH] fix(ngView): Don't throw when the ngView element contains content with directives. Fixes #5069 --- src/ngRoute/directive/ngView.js | 64 +++++++++++++++------------- test/ngRoute/directive/ngViewSpec.js | 42 +++++++++++++++--- 2 files changed, 72 insertions(+), 34 deletions(-) diff --git a/src/ngRoute/directive/ngView.js b/src/ngRoute/directive/ngView.js index 3271ac0c2108..280bf42324c1 100644 --- a/src/ngRoute/directive/ngView.js +++ b/src/ngRoute/directive/ngView.js @@ -199,37 +199,43 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller, if (template) { var newScope = scope.$new(); - $transclude(newScope, function(clone) { - clone.html(template); - $animate.enter(clone, null, currentElement || $element, function onNgViewEnter () { - if (angular.isDefined(autoScrollExp) - && (!autoScrollExp || scope.$eval(autoScrollExp))) { - $anchorScroll(); - } - }); - - cleanupLastView(); - - var link = $compile(clone.contents()), - current = $route.current; - - currentScope = current.scope = newScope; - currentElement = clone; - - if (current.controller) { - locals.$scope = currentScope; - var controller = $controller(current.controller, locals); - if (current.controllerAs) { - currentScope[current.controllerAs] = controller; - } - clone.data('$ngControllerController', controller); - clone.children().data('$ngControllerController', controller); - } - link(currentScope); - currentScope.$emit('$viewContentLoaded'); - currentScope.$eval(onloadExp); + // Note: This will also link all children of ng-view that were contained in the original + // html. If that content contains controllers, ... they could pollute/change the scope. + // However, using ng-view on an element with additional content does not make sense... + // Note: We can't remove them in the cloneAttchFn of $transclude as that + // function is called before linking the content, which would apply child + // directives to non existing elements. + var clone = $transclude(newScope, angular.noop); + clone.html(template); + $animate.enter(clone, null, currentElement || $element, function onNgViewEnter () { + if (angular.isDefined(autoScrollExp) + && (!autoScrollExp || scope.$eval(autoScrollExp))) { + $anchorScroll(); + } }); + + cleanupLastView(); + + var link = $compile(clone.contents()), + current = $route.current; + + currentScope = current.scope = newScope; + currentElement = clone; + + if (current.controller) { + locals.$scope = currentScope; + var controller = $controller(current.controller, locals); + if (current.controllerAs) { + currentScope[current.controllerAs] = controller; + } + clone.data('$ngControllerController', controller); + clone.children().data('$ngControllerController', controller); + } + + link(currentScope); + currentScope.$emit('$viewContentLoaded'); + currentScope.$eval(onloadExp); } else { cleanupLastView(); } diff --git a/test/ngRoute/directive/ngViewSpec.js b/test/ngRoute/directive/ngViewSpec.js index 533f0b53cde6..f4b28058aa0a 100644 --- a/test/ngRoute/directive/ngViewSpec.js +++ b/test/ngRoute/directive/ngViewSpec.js @@ -515,12 +515,23 @@ describe('ngView', function() { }); describe('ngView and transcludes', function() { + var element, directive; + + beforeEach(module('ngRoute', function($compileProvider) { + element = null; + directive = $compileProvider.directive; + })); + + afterEach(function() { + if (element) { + dealoc(element); + } + }); + it('should allow access to directive controller from children when used in a replace template', function() { var controller; - module('ngRoute'); - module(function($compileProvider, $routeProvider) { + module(function($routeProvider) { $routeProvider.when('/view', {templateUrl: 'view.html'}); - var directive = $compileProvider.directive; directive('template', function() { return { template: '
', @@ -542,14 +553,35 @@ describe('ngView and transcludes', function() { }); inject(function($compile, $rootScope, $httpBackend, $location) { $httpBackend.expectGET('view.html').respond('
'); - var element = $compile('
')($rootScope); + element = $compile('
')($rootScope); $location.url('/view'); $rootScope.$apply(); $httpBackend.flush(); expect(controller.flag).toBe(true); - dealoc(element); }); }); + + it("should compile it's content correctly (although we remove it later)", function() { + var testElement; + module(function($compileProvider, $routeProvider) { + $routeProvider.when('/view', {template: ' '}); + var directive = $compileProvider.directive; + directive('test', function() { + return { + link: function(scope, element) { + testElement = element; + } + }; + }); + }); + inject(function($compile, $rootScope, $location) { + element = $compile('
')($rootScope); + $location.url('/view'); + $rootScope.$apply(); + expect(testElement[0].nodeName).toBe('DIV'); + }); + + }); }); describe('ngView animations', function() {