Skip to content

Commit d314eee

Browse files
tboschjamesdaily
authored andcommitted
fix(ngView): Don't throw when the ngView element contains content with directives.
Fixes angular#5069
1 parent 462d709 commit d314eee

File tree

2 files changed

+72
-34
lines changed

2 files changed

+72
-34
lines changed

src/ngRoute/directive/ngView.js

+35-29
Original file line numberDiff line numberDiff line change
@@ -199,37 +199,43 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
199199

200200
if (template) {
201201
var newScope = scope.$new();
202-
$transclude(newScope, function(clone) {
203-
clone.html(template);
204-
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
205-
if (angular.isDefined(autoScrollExp)
206-
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
207-
$anchorScroll();
208-
}
209-
});
210-
211-
cleanupLastView();
212-
213-
var link = $compile(clone.contents()),
214-
current = $route.current;
215-
216-
currentScope = current.scope = newScope;
217-
currentElement = clone;
218-
219-
if (current.controller) {
220-
locals.$scope = currentScope;
221-
var controller = $controller(current.controller, locals);
222-
if (current.controllerAs) {
223-
currentScope[current.controllerAs] = controller;
224-
}
225-
clone.data('$ngControllerController', controller);
226-
clone.children().data('$ngControllerController', controller);
227-
}
228202

229-
link(currentScope);
230-
currentScope.$emit('$viewContentLoaded');
231-
currentScope.$eval(onloadExp);
203+
// Note: This will also link all children of ng-view that were contained in the original
204+
// html. If that content contains controllers, ... they could pollute/change the scope.
205+
// However, using ng-view on an element with additional content does not make sense...
206+
// Note: We can't remove them in the cloneAttchFn of $transclude as that
207+
// function is called before linking the content, which would apply child
208+
// directives to non existing elements.
209+
var clone = $transclude(newScope, angular.noop);
210+
clone.html(template);
211+
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
212+
if (angular.isDefined(autoScrollExp)
213+
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
214+
$anchorScroll();
215+
}
232216
});
217+
218+
cleanupLastView();
219+
220+
var link = $compile(clone.contents()),
221+
current = $route.current;
222+
223+
currentScope = current.scope = newScope;
224+
currentElement = clone;
225+
226+
if (current.controller) {
227+
locals.$scope = currentScope;
228+
var controller = $controller(current.controller, locals);
229+
if (current.controllerAs) {
230+
currentScope[current.controllerAs] = controller;
231+
}
232+
clone.data('$ngControllerController', controller);
233+
clone.children().data('$ngControllerController', controller);
234+
}
235+
236+
link(currentScope);
237+
currentScope.$emit('$viewContentLoaded');
238+
currentScope.$eval(onloadExp);
233239
} else {
234240
cleanupLastView();
235241
}

test/ngRoute/directive/ngViewSpec.js

+37-5
Original file line numberDiff line numberDiff line change
@@ -515,12 +515,23 @@ describe('ngView', function() {
515515
});
516516

517517
describe('ngView and transcludes', function() {
518+
var element, directive;
519+
520+
beforeEach(module('ngRoute', function($compileProvider) {
521+
element = null;
522+
directive = $compileProvider.directive;
523+
}));
524+
525+
afterEach(function() {
526+
if (element) {
527+
dealoc(element);
528+
}
529+
});
530+
518531
it('should allow access to directive controller from children when used in a replace template', function() {
519532
var controller;
520-
module('ngRoute');
521-
module(function($compileProvider, $routeProvider) {
533+
module(function($routeProvider) {
522534
$routeProvider.when('/view', {templateUrl: 'view.html'});
523-
var directive = $compileProvider.directive;
524535
directive('template', function() {
525536
return {
526537
template: '<div ng-view></div>',
@@ -542,14 +553,35 @@ describe('ngView and transcludes', function() {
542553
});
543554
inject(function($compile, $rootScope, $httpBackend, $location) {
544555
$httpBackend.expectGET('view.html').respond('<div><div test></div></div>');
545-
var element = $compile('<div><div template></div></div>')($rootScope);
556+
element = $compile('<div><div template></div></div>')($rootScope);
546557
$location.url('/view');
547558
$rootScope.$apply();
548559
$httpBackend.flush();
549560
expect(controller.flag).toBe(true);
550-
dealoc(element);
551561
});
552562
});
563+
564+
it("should compile it's content correctly (although we remove it later)", function() {
565+
var testElement;
566+
module(function($compileProvider, $routeProvider) {
567+
$routeProvider.when('/view', {template: ' '});
568+
var directive = $compileProvider.directive;
569+
directive('test', function() {
570+
return {
571+
link: function(scope, element) {
572+
testElement = element;
573+
}
574+
};
575+
});
576+
});
577+
inject(function($compile, $rootScope, $location) {
578+
element = $compile('<div><div ng-view><div test someAttr></div></div></div>')($rootScope);
579+
$location.url('/view');
580+
$rootScope.$apply();
581+
expect(testElement[0].nodeName).toBe('DIV');
582+
});
583+
584+
});
553585
});
554586

555587
describe('ngView animations', function() {

0 commit comments

Comments
 (0)