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

Commit 15e1a29

Browse files
IgorMinarmhevery
authored andcommitted
fix(compiler): corrects component transclusion on compilation root.
Closes# 2155
1 parent 344e195 commit 15e1a29

File tree

3 files changed

+112
-13
lines changed

3 files changed

+112
-13
lines changed

src/ng/compile.js

+13-7
Original file line numberDiff line numberDiff line change
@@ -749,7 +749,7 @@ function $CompileProvider($provide) {
749749
newTemplateAttrs
750750
)
751751
);
752-
mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
752+
mergeTemplateAttributes(templateAttrs, newTemplateAttrs, directive.name);
753753

754754
ii = directives.length;
755755
} else {
@@ -1007,15 +1007,16 @@ function $CompileProvider($provide) {
10071007
*
10081008
* @param {object} dst destination attributes (original DOM)
10091009
* @param {object} src source attributes (from the directive template)
1010+
* @param {string} ignoreName attribute which should be ignored
10101011
*/
1011-
function mergeTemplateAttributes(dst, src) {
1012+
function mergeTemplateAttributes(dst, src, ignoreName) {
10121013
var srcAttr = src.$attr,
10131014
dstAttr = dst.$attr,
10141015
$element = dst.$$element;
10151016

10161017
// reapply the old attributes to the new element
10171018
forEach(dst, function(value, key) {
1018-
if (key.charAt(0) != '$') {
1019+
if (key.charAt(0) != '$' && key != ignoreName) {
10191020
if (src[key]) {
10201021
value += (key === 'style' ? ';' : ' ') + src[key];
10211022
}
@@ -1030,7 +1031,7 @@ function $CompileProvider($provide) {
10301031
dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
10311032
} else if (key == 'style') {
10321033
$element.attr('style', $element.attr('style') + ';' + value);
1033-
} else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
1034+
} else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key) && key != ignoreName) {
10341035
dst[key] = value;
10351036
dstAttr[key] = srcAttr[key];
10361037
}
@@ -1073,14 +1074,19 @@ function $CompileProvider($provide) {
10731074
tempTemplateAttrs = {$attr: {}};
10741075
replaceWith($rootElement, $compileNode, compileNode);
10751076
collectDirectives(compileNode, directives, tempTemplateAttrs);
1076-
mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
1077+
mergeTemplateAttributes(tAttrs, tempTemplateAttrs, origAsyncDirective.name);
10771078
} else {
10781079
compileNode = beforeTemplateCompileNode;
10791080
$compileNode.html(content);
10801081
}
10811082

10821083
directives.unshift(derivedSyncDirective);
1083-
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
1084+
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn, $compileNode);
1085+
forEach($rootElement, function(node, i) {
1086+
if (node == compileNode) {
1087+
$rootElement[i] = $compileNode[0];
1088+
}
1089+
});
10841090
afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
10851091

10861092

@@ -1089,7 +1095,7 @@ function $CompileProvider($provide) {
10891095
beforeTemplateLinkNode = linkQueue.shift(),
10901096
linkRootElement = linkQueue.shift(),
10911097
controller = linkQueue.shift(),
1092-
linkNode = compileNode;
1098+
linkNode = $compileNode[0];
10931099

10941100
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
10951101
// it was cloned therefore we have to clone as well.

test/ng/compileSpec.js

+5-5
Original file line numberDiff line numberDiff line change
@@ -534,7 +534,7 @@ describe('$compile', function() {
534534
expect(div.hasClass('log')).toBe(true);
535535
expect(div.css('width')).toBe('10px');
536536
expect(div.css('height')).toBe('20px');
537-
expect(div.attr('replace')).toEqual('');
537+
expect(div.attr('replace')).toEqual(undefined);
538538
expect(div.attr('high-log')).toEqual('');
539539
}));
540540

@@ -856,7 +856,7 @@ describe('$compile', function() {
856856
$rootScope.$digest();
857857

858858
expect(sortedHtml(element)).
859-
toEqual('<div><b class="hello"><span replace="">Hello, Elvis!</span></b></div>');
859+
toEqual('<div><b class="hello"><span>Hello, Elvis!</span></b></div>');
860860
}));
861861

862862

@@ -868,7 +868,7 @@ describe('$compile', function() {
868868
$rootScope.$digest();
869869

870870
expect(sortedHtml(element)).
871-
toEqual('<span replace="">Hello, Elvis!</span>');
871+
toEqual('<span>Hello, Elvis!</span>');
872872
}));
873873

874874

@@ -1077,7 +1077,7 @@ describe('$compile', function() {
10771077

10781078
var div = element.find('div');
10791079
expect(div.attr('i-first')).toEqual('');
1080-
expect(div.attr('i-second')).toEqual('');
1080+
expect(div.attr('i-second')).toEqual(undefined);
10811081
expect(div.attr('i-third')).toEqual('');
10821082
expect(div.attr('i-last')).toEqual('');
10831083

@@ -1127,7 +1127,7 @@ describe('$compile', function() {
11271127

11281128
var div = element.find('div');
11291129
expect(div.attr('i-first')).toEqual('');
1130-
expect(div.attr('i-second')).toEqual('');
1130+
expect(div.attr('i-second')).toEqual(undefined);
11311131
expect(div.attr('i-third')).toEqual('');
11321132
expect(div.attr('i-last')).toEqual('');
11331133

test/ng/directive/ngRepeatSpec.js

+94-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
'use strict';
22

33
describe('ngRepeat', function() {
4-
var element, $compile, scope, $exceptionHandler;
4+
var element, $compile, scope, $exceptionHandler, $compileProvider;
5+
6+
beforeEach(module(function(_$compileProvider_) {
7+
$compileProvider = _$compileProvider_;
8+
}));
59

610

711
beforeEach(module(function($exceptionHandlerProvider) {
@@ -448,6 +452,95 @@ describe('ngRepeat', function() {
448452
});
449453

450454

455+
describe('nesting in replaced directive templates', function() {
456+
457+
it('should work when placed on a root element of attr directive with SYNC replaced template',
458+
inject(function($templateCache, $compile, $rootScope) {
459+
$compileProvider.directive('replaceMeWithRepeater', function() {
460+
return {
461+
replace: true,
462+
template: '<span ng-repeat="i in items">{{log(i)}}</span>'
463+
}
464+
});
465+
element = jqLite('<span replace-me-with-repeater></span>');
466+
$compile(element)($rootScope);
467+
expect(element.text()).toBe('');
468+
var logs = [];
469+
$rootScope.log = function(t) { logs.push(t); };
470+
471+
// This creates one item, but it has no parent so we can't get to it
472+
$rootScope.items = [1, 2];
473+
$rootScope.$apply();
474+
475+
// This cleans up to prevent memory leak
476+
$rootScope.items = [];
477+
$rootScope.$apply();
478+
expect(angular.mock.dump(element)).toBe('<!-- ngRepeat: i in items -->');
479+
expect(logs).toEqual([1, 2, 1, 2]);
480+
}));
481+
482+
483+
iit('should work when placed on a root element of attr directive with ASYNC replaced template',
484+
inject(function($templateCache, $compile, $rootScope) {
485+
$compileProvider.directive('replaceMeWithRepeater', function() {
486+
return {
487+
replace: true,
488+
templateUrl: 'replace-me-with-repeater.html'
489+
}
490+
});
491+
$templateCache.put('replace-me-with-repeater.html', '<div ng-repeat="i in items">{{log(i)}}</div>');
492+
element = jqLite('<span>-</span><span replace-me-with-repeater></span><span>-</span>');
493+
$compile(element)($rootScope);
494+
expect(element.text()).toBe('--');
495+
var logs = [];
496+
$rootScope.log = function(t) { logs.push(t); };
497+
498+
// This creates one item, but it has no parent so we can't get to it
499+
$rootScope.items = [1, 2];
500+
$rootScope.$apply();
501+
502+
// This cleans up to prevent memory leak
503+
$rootScope.items = [];
504+
$rootScope.$apply();
505+
expect(sortedHtml(element)).toBe('<span>-</span><!-- ngRepeat: i in items --><span>-</span>');
506+
expect(logs).toEqual([1, 2, 1, 2]);
507+
}));
508+
509+
510+
it('should work when placed on a root element of element directive with SYNC replaced template',
511+
inject(function($templateCache, $compile, $rootScope) {
512+
$compileProvider.directive('replaceMeWithRepeater', function() {
513+
return {
514+
restrict: 'E',
515+
replace: true,
516+
template: '<div ng-repeat="i in [1,2,3]">{{i}}</div>'
517+
}
518+
});
519+
element = $compile('<div><replace-me-with-repeater></replace-me-with-repeater></div>')($rootScope);
520+
expect(element.text()).toBe('');
521+
$rootScope.$apply();
522+
expect(element.text()).toBe('123');
523+
}));
524+
525+
526+
it('should work when placed on a root element of element directive with ASYNC replaced template',
527+
inject(function($templateCache, $compile, $rootScope) {
528+
$compileProvider.directive('replaceMeWithRepeater', function() {
529+
return {
530+
restrict: 'E',
531+
replace: true,
532+
templateUrl: 'replace-me-with-repeater.html'
533+
}
534+
});
535+
$templateCache.put('replace-me-with-repeater.html', '<div ng-repeat="i in [1,2,3]">{{i}}</div>');
536+
element = $compile('<div><replace-me-with-repeater></replace-me-with-repeater></div>')($rootScope);
537+
expect(element.text()).toBe('');
538+
$rootScope.$apply();
539+
expect(element.text()).toBe('123');
540+
}));
541+
});
542+
543+
451544
describe('stability', function() {
452545
var a, b, c, d, lis;
453546

0 commit comments

Comments
 (0)