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

Commit 30a8b7d

Browse files
committed
fix(ngInclude): 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 `ngInclude` are linked. Fixes #5247.
1 parent f8944ef commit 30a8b7d

File tree

3 files changed

+71
-7
lines changed

3 files changed

+71
-7
lines changed

src/AngularPublic.js

+4
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
ngHideDirective,
2929
ngIfDirective,
3030
ngIncludeDirective,
31+
ngIncludeFillContentDirective,
3132
ngInitDirective,
3233
ngNonBindableDirective,
3334
ngPluralizeDirective,
@@ -181,6 +182,9 @@ function publishExternalAPI(angular){
181182
ngRequired: requiredDirective,
182183
ngValue: ngValueDirective
183184
}).
185+
directive({
186+
ngInclude: ngIncludeFillContentDirective
187+
}).
184188
directive(ngAttributeAliasDirectives).
185189
directive(ngEventDirectives);
186190
$provide.provider({

src/ng/directive/ngInclude.js

+27-7
Original file line numberDiff line numberDiff line change
@@ -147,13 +147,14 @@
147147
* @description
148148
* Emitted every time the ngInclude content is reloaded.
149149
*/
150-
var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile', '$animate', '$sce',
151-
function($http, $templateCache, $anchorScroll, $compile, $animate, $sce) {
150+
var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$animate', '$sce',
151+
function($http, $templateCache, $anchorScroll, $animate, $sce) {
152152
return {
153153
restrict: 'ECA',
154154
priority: 400,
155155
terminal: true,
156156
transclude: 'element',
157+
controller: angular.noop,
157158
compile: function(element, attr) {
158159
var srcExp = attr.ngInclude || attr.src,
159160
onloadExp = attr.onload || '',
@@ -187,22 +188,22 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
187188
$http.get(src, {cache: $templateCache}).success(function(response) {
188189
if (thisChangeId !== changeCounter) return;
189190
var newScope = scope.$new();
191+
ctrl.template = response;
190192

191193
// Note: This will also link all children of ng-include that were contained in the original
192194
// html. If that content contains controllers, ... they could pollute/change the scope.
193195
// However, using ng-include on an element with additional content does not make sense...
194196
// Note: We can't remove them in the cloneAttchFn of $transclude as that
195197
// function is called before linking the content, which would apply child
196198
// directives to non existing elements.
197-
var clone = $transclude(newScope, noop);
198-
cleanupLastIncludeContent();
199+
var clone = $transclude(newScope, function(clone) {
200+
cleanupLastIncludeContent();
201+
$animate.enter(clone, null, $element, afterAnimation);
202+
});
199203

200204
currentScope = newScope;
201205
currentElement = clone;
202206

203-
currentElement.html(response);
204-
$animate.enter(currentElement, null, $element, afterAnimation);
205-
$compile(currentElement.contents())(currentScope);
206207
currentScope.$emit('$includeContentLoaded');
207208
scope.$eval(onloadExp);
208209
}).error(function() {
@@ -211,9 +212,28 @@ var ngIncludeDirective = ['$http', '$templateCache', '$anchorScroll', '$compile'
211212
scope.$emit('$includeContentRequested');
212213
} else {
213214
cleanupLastIncludeContent();
215+
ctrl.template = null;
214216
}
215217
});
216218
};
217219
}
218220
};
219221
}];
222+
223+
// This directive is called during the $transclude call of the first `ngInclude` directive.
224+
// It will replace and compile the content of the element with the loaded template.
225+
// We need this directive so that the element content is already filled when
226+
// the link function of another directive on the same element as ngInclude
227+
// is called.
228+
var ngIncludeFillContentDirective = ['$compile',
229+
function($compile) {
230+
return {
231+
restrict: 'ECA',
232+
priority: -400,
233+
require: 'ngInclude',
234+
link: function(scope, $element, $attr, ctrl) {
235+
$element.html(ctrl.template);
236+
$compile($element.contents())(scope);
237+
}
238+
};
239+
}];

test/ng/directive/ngIncludeSpec.js

+40
Original file line numberDiff line numberDiff line change
@@ -524,6 +524,46 @@ describe('ngInclude and transcludes', function() {
524524
});
525525

526526
});
527+
528+
it('should link directives on the same element after the content has been loaded', function() {
529+
var contentOnLink;
530+
module(function() {
531+
directive('test', function() {
532+
return {
533+
link: function(scope, element) {
534+
contentOnLink = element.text();
535+
}
536+
};
537+
});
538+
});
539+
inject(function($compile, $rootScope, $httpBackend) {
540+
$httpBackend.expectGET('include.html').respond('someContent');
541+
element = $compile('<div><div ng-include="\'include.html\'" test></div>')($rootScope);
542+
$rootScope.$apply();
543+
$httpBackend.flush();
544+
expect(contentOnLink).toBe('someContent');
545+
});
546+
});
547+
548+
it('should add the content to the element before compiling it', function() {
549+
var root;
550+
module(function() {
551+
directive('test', function() {
552+
return {
553+
link: function(scope, element) {
554+
root = element.parent().parent();
555+
}
556+
};
557+
});
558+
});
559+
inject(function($compile, $rootScope, $httpBackend) {
560+
$httpBackend.expectGET('include.html').respond('<span test></span>');
561+
element = $compile('<div><div ng-include="\'include.html\'"></div>')($rootScope);
562+
$rootScope.$apply();
563+
$httpBackend.flush();
564+
expect(root[0]).toBe(element[0]);
565+
});
566+
});
527567
});
528568

529569
describe('ngInclude animations', function() {

0 commit comments

Comments
 (0)