Skip to content

Commit 73624b2

Browse files
committed
fix(ngView): Add template to DOM before linking other directives
The template needs to be added to the DOM before other directives at the same element as `ngView` are linked. Related to angular#5247.
1 parent 9396d55 commit 73624b2

File tree

2 files changed

+87
-27
lines changed

2 files changed

+87
-27
lines changed

src/ngRoute/directive/ngView.js

+47-27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
'use strict';
22

33
ngRouteModule.directive('ngView', ngViewFactory);
4+
ngRouteModule.directive('ngView', ngViewFillContentFactory);
5+
46

57
/**
68
* @ngdoc directive
@@ -166,8 +168,8 @@ ngRouteModule.directive('ngView', ngViewFactory);
166168
* @description
167169
* Emitted every time the ngView content is reloaded.
168170
*/
169-
ngViewFactory.$inject = ['$route', '$anchorScroll', '$compile', '$controller', '$animate'];
170-
function ngViewFactory( $route, $anchorScroll, $compile, $controller, $animate) {
171+
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
172+
function ngViewFactory( $route, $anchorScroll, $animate) {
171173
return {
172174
restrict: 'ECA',
173175
terminal: true,
@@ -199,41 +201,26 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
199201

200202
if (template) {
201203
var newScope = scope.$new();
204+
var current = $route.current;
202205

203206
// Note: This will also link all children of ng-view that were contained in the original
204207
// html. If that content contains controllers, ... they could pollute/change the scope.
205208
// However, using ng-view on an element with additional content does not make sense...
206209
// Note: We can't remove them in the cloneAttchFn of $transclude as that
207210
// function is called before linking the content, which would apply child
208211
// 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-
}
212+
var clone = $transclude(newScope, function(clone) {
213+
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
214+
if (angular.isDefined(autoScrollExp)
215+
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
216+
$anchorScroll();
217+
}
218+
});
219+
cleanupLastView();
216220
});
217221

218-
cleanupLastView();
219-
220-
var link = $compile(clone.contents()),
221-
current = $route.current;
222-
223-
currentScope = current.scope = newScope;
224222
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);
223+
currentScope = current.scope = newScope;
237224
currentScope.$emit('$viewContentLoaded');
238225
currentScope.$eval(onloadExp);
239226
} else {
@@ -243,3 +230,36 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
243230
}
244231
};
245232
}
233+
234+
// This directive is called during the $transclude call of the first `ngView` directive.
235+
// It will replace and compile the content of the element with the loaded template.
236+
// We need this directive so that the element content is already filled when
237+
// the link function of another directive on the same element as ngView
238+
// is called.
239+
ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
240+
function ngViewFillContentFactory($compile, $controller, $route) {
241+
return {
242+
restrict: 'ECA',
243+
priority: -400,
244+
link: function(scope, $element) {
245+
var current = $route.current,
246+
locals = current.locals;
247+
248+
$element.html(locals.$template);
249+
250+
var link = $compile($element.contents());
251+
252+
if (current.controller) {
253+
locals.$scope = scope;
254+
var controller = $controller(current.controller, locals);
255+
if (current.controllerAs) {
256+
scope[current.controllerAs] = controller;
257+
}
258+
$element.data('$ngControllerController', controller);
259+
$element.children().data('$ngControllerController', controller);
260+
}
261+
262+
link(scope);
263+
}
264+
};
265+
}

test/ngRoute/directive/ngViewSpec.js

+40
Original file line numberDiff line numberDiff line change
@@ -582,6 +582,46 @@ describe('ngView and transcludes', function() {
582582
});
583583

584584
});
585+
586+
it('should link directives on the same element after the content has been loaded', function() {
587+
var contentOnLink;
588+
module(function($compileProvider, $routeProvider) {
589+
$routeProvider.when('/view', {template: 'someContent'});
590+
$compileProvider.directive('test', function() {
591+
return {
592+
link: function(scope, element) {
593+
contentOnLink = element.text();
594+
}
595+
};
596+
});
597+
});
598+
inject(function($compile, $rootScope, $location) {
599+
element = $compile('<div><div ng-view test></div>')($rootScope);
600+
$location.url('/view');
601+
$rootScope.$apply();
602+
expect(contentOnLink).toBe('someContent');
603+
});
604+
});
605+
606+
it('should add the content to the element before compiling it', function() {
607+
var root;
608+
module(function($compileProvider, $routeProvider) {
609+
$routeProvider.when('/view', {template: '<span test></span>'});
610+
$compileProvider.directive('test', function() {
611+
return {
612+
link: function(scope, element) {
613+
root = element.parent().parent();
614+
}
615+
};
616+
});
617+
});
618+
inject(function($compile, $rootScope, $location) {
619+
element = $compile('<div><div ng-view></div>')($rootScope);
620+
$location.url('/view');
621+
$rootScope.$apply();
622+
expect(root[0]).toBe(element[0]);
623+
});
624+
});
585625
});
586626

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

0 commit comments

Comments
 (0)