Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

Commit

Permalink
fix(ngView): Add template to DOM before linking other directives
Browse files Browse the repository at this point in the history
The template needs to be added to the DOM before
other directives at the same element as `ngView` are linked.

Related to #5247.
  • Loading branch information
tbosch committed Dec 13, 2013
1 parent 43072e3 commit f8944ef
Show file tree
Hide file tree
Showing 2 changed files with 87 additions and 27 deletions.
74 changes: 47 additions & 27 deletions src/ngRoute/directive/ngView.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
'use strict';

ngRouteModule.directive('ngView', ngViewFactory);
ngRouteModule.directive('ngView', ngViewFillContentFactory);


/**
* @ngdoc directive
Expand Down Expand Up @@ -166,8 +168,8 @@ ngRouteModule.directive('ngView', ngViewFactory);
* @description
* Emitted every time the ngView content is reloaded.
*/
ngViewFactory.$inject = ['$route', '$anchorScroll', '$compile', '$controller', '$animate'];
function ngViewFactory( $route, $anchorScroll, $compile, $controller, $animate) {
ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
function ngViewFactory( $route, $anchorScroll, $animate) {
return {
restrict: 'ECA',
terminal: true,
Expand Down Expand Up @@ -199,41 +201,26 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,

if (template) {
var newScope = scope.$new();
var current = $route.current;

// 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();
}
var clone = $transclude(newScope, function(clone) {
$animate.enter(clone, null, currentElement || $element, function onNgViewEnter () {
if (angular.isDefined(autoScrollExp)
&& (!autoScrollExp || scope.$eval(autoScrollExp))) {
$anchorScroll();
}
});
cleanupLastView();
});

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 = current.scope = newScope;
currentScope.$emit('$viewContentLoaded');
currentScope.$eval(onloadExp);
} else {
Expand All @@ -243,3 +230,36 @@ function ngViewFactory( $route, $anchorScroll, $compile, $controller,
}
};
}

// This directive is called during the $transclude call of the first `ngView` directive.
// It will replace and compile the content of the element with the loaded template.
// We need this directive so that the element content is already filled when
// the link function of another directive on the same element as ngView
// is called.
ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
function ngViewFillContentFactory($compile, $controller, $route) {
return {
restrict: 'ECA',
priority: -400,
link: function(scope, $element) {
var current = $route.current,
locals = current.locals;

$element.html(locals.$template);

var link = $compile($element.contents());

if (current.controller) {
locals.$scope = scope;
var controller = $controller(current.controller, locals);
if (current.controllerAs) {
scope[current.controllerAs] = controller;
}
$element.data('$ngControllerController', controller);
$element.children().data('$ngControllerController', controller);
}

link(scope);
}
};
}
40 changes: 40 additions & 0 deletions test/ngRoute/directive/ngViewSpec.js
Original file line number Diff line number Diff line change
Expand Up @@ -582,6 +582,46 @@ describe('ngView and transcludes', function() {
});

});

it('should link directives on the same element after the content has been loaded', function() {
var contentOnLink;
module(function($compileProvider, $routeProvider) {
$routeProvider.when('/view', {template: 'someContent'});
$compileProvider.directive('test', function() {
return {
link: function(scope, element) {
contentOnLink = element.text();
}
};
});
});
inject(function($compile, $rootScope, $location) {
element = $compile('<div><div ng-view test></div>')($rootScope);
$location.url('/view');
$rootScope.$apply();
expect(contentOnLink).toBe('someContent');
});
});

it('should add the content to the element before compiling it', function() {
var root;
module(function($compileProvider, $routeProvider) {
$routeProvider.when('/view', {template: '<span test></span>'});
$compileProvider.directive('test', function() {
return {
link: function(scope, element) {
root = element.parent().parent();
}
};
});
});
inject(function($compile, $rootScope, $location) {
element = $compile('<div><div ng-view></div>')($rootScope);
$location.url('/view');
$rootScope.$apply();
expect(root[0]).toBe(element[0]);
});
});
});

describe('ngView animations', function() {
Expand Down

1 comment on commit f8944ef

@tbosch
Copy link
Contributor Author

@tbosch tbosch commented on f8944ef Apr 23, 2014

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi,
please create an issue with jsfiddle/plunker associated (plunker would be better here as it can load partials).

Thanks!

Please sign in to comment.