From 15e1a29cd08993b599f390e83a249ec17f753972 Mon Sep 17 00:00:00 2001
From: Igor Minar
Date: Wed, 13 Mar 2013 16:29:26 -0700
Subject: fix(compiler): corrects component transclusion on compilation root.
Closes# 2155
---
src/ng/compile.js | 20 ++++++---
test/ng/compileSpec.js | 10 ++---
test/ng/directive/ngRepeatSpec.js | 95 ++++++++++++++++++++++++++++++++++++++-
3 files changed, 112 insertions(+), 13 deletions(-)
diff --git a/src/ng/compile.js b/src/ng/compile.js
index caa4e8ce..36043a15 100644
--- a/src/ng/compile.js
+++ b/src/ng/compile.js
@@ -749,7 +749,7 @@ function $CompileProvider($provide) {
newTemplateAttrs
)
);
- mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
+ mergeTemplateAttributes(templateAttrs, newTemplateAttrs, directive.name);
ii = directives.length;
} else {
@@ -1007,15 +1007,16 @@ function $CompileProvider($provide) {
*
* @param {object} dst destination attributes (original DOM)
* @param {object} src source attributes (from the directive template)
+ * @param {string} ignoreName attribute which should be ignored
*/
- function mergeTemplateAttributes(dst, src) {
+ function mergeTemplateAttributes(dst, src, ignoreName) {
var srcAttr = src.$attr,
dstAttr = dst.$attr,
$element = dst.$$element;
// reapply the old attributes to the new element
forEach(dst, function(value, key) {
- if (key.charAt(0) != '$') {
+ if (key.charAt(0) != '$' && key != ignoreName) {
if (src[key]) {
value += (key === 'style' ? ';' : ' ') + src[key];
}
@@ -1030,7 +1031,7 @@ function $CompileProvider($provide) {
dst['class'] = (dst['class'] ? dst['class'] + ' ' : '') + value;
} else if (key == 'style') {
$element.attr('style', $element.attr('style') + ';' + value);
- } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key)) {
+ } else if (key.charAt(0) != '$' && !dst.hasOwnProperty(key) && key != ignoreName) {
dst[key] = value;
dstAttr[key] = srcAttr[key];
}
@@ -1073,14 +1074,19 @@ function $CompileProvider($provide) {
tempTemplateAttrs = {$attr: {}};
replaceWith($rootElement, $compileNode, compileNode);
collectDirectives(compileNode, directives, tempTemplateAttrs);
- mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
+ mergeTemplateAttributes(tAttrs, tempTemplateAttrs, origAsyncDirective.name);
} else {
compileNode = beforeTemplateCompileNode;
$compileNode.html(content);
}
directives.unshift(derivedSyncDirective);
- afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn);
+ afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs, childTranscludeFn, $compileNode);
+ forEach($rootElement, function(node, i) {
+ if (node == compileNode) {
+ $rootElement[i] = $compileNode[0];
+ }
+ });
afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
@@ -1089,7 +1095,7 @@ function $CompileProvider($provide) {
beforeTemplateLinkNode = linkQueue.shift(),
linkRootElement = linkQueue.shift(),
controller = linkQueue.shift(),
- linkNode = compileNode;
+ linkNode = $compileNode[0];
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
// it was cloned therefore we have to clone as well.
diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js
index 672a704d..0ef7e8bf 100755
--- a/test/ng/compileSpec.js
+++ b/test/ng/compileSpec.js
@@ -534,7 +534,7 @@ describe('$compile', function() {
expect(div.hasClass('log')).toBe(true);
expect(div.css('width')).toBe('10px');
expect(div.css('height')).toBe('20px');
- expect(div.attr('replace')).toEqual('');
+ expect(div.attr('replace')).toEqual(undefined);
expect(div.attr('high-log')).toEqual('');
}));
@@ -856,7 +856,7 @@ describe('$compile', function() {
$rootScope.$digest();
expect(sortedHtml(element)).
- toEqual('
Hello, Elvis!
');
+ toEqual('Hello, Elvis!
');
}));
@@ -868,7 +868,7 @@ describe('$compile', function() {
$rootScope.$digest();
expect(sortedHtml(element)).
- toEqual('Hello, Elvis!');
+ toEqual('Hello, Elvis!');
}));
@@ -1077,7 +1077,7 @@ describe('$compile', function() {
var div = element.find('div');
expect(div.attr('i-first')).toEqual('');
- expect(div.attr('i-second')).toEqual('');
+ expect(div.attr('i-second')).toEqual(undefined);
expect(div.attr('i-third')).toEqual('');
expect(div.attr('i-last')).toEqual('');
@@ -1127,7 +1127,7 @@ describe('$compile', function() {
var div = element.find('div');
expect(div.attr('i-first')).toEqual('');
- expect(div.attr('i-second')).toEqual('');
+ expect(div.attr('i-second')).toEqual(undefined);
expect(div.attr('i-third')).toEqual('');
expect(div.attr('i-last')).toEqual('');
diff --git a/test/ng/directive/ngRepeatSpec.js b/test/ng/directive/ngRepeatSpec.js
index 9fc445ba..925a93ce 100644
--- a/test/ng/directive/ngRepeatSpec.js
+++ b/test/ng/directive/ngRepeatSpec.js
@@ -1,7 +1,11 @@
'use strict';
describe('ngRepeat', function() {
- var element, $compile, scope, $exceptionHandler;
+ var element, $compile, scope, $exceptionHandler, $compileProvider;
+
+ beforeEach(module(function(_$compileProvider_) {
+ $compileProvider = _$compileProvider_;
+ }));
beforeEach(module(function($exceptionHandlerProvider) {
@@ -448,6 +452,95 @@ describe('ngRepeat', function() {
});
+ describe('nesting in replaced directive templates', function() {
+
+ it('should work when placed on a root element of attr directive with SYNC replaced template',
+ inject(function($templateCache, $compile, $rootScope) {
+ $compileProvider.directive('replaceMeWithRepeater', function() {
+ return {
+ replace: true,
+ template: '{{log(i)}}'
+ }
+ });
+ element = jqLite('');
+ $compile(element)($rootScope);
+ expect(element.text()).toBe('');
+ var logs = [];
+ $rootScope.log = function(t) { logs.push(t); };
+
+ // This creates one item, but it has no parent so we can't get to it
+ $rootScope.items = [1, 2];
+ $rootScope.$apply();
+
+ // This cleans up to prevent memory leak
+ $rootScope.items = [];
+ $rootScope.$apply();
+ expect(angular.mock.dump(element)).toBe('');
+ expect(logs).toEqual([1, 2, 1, 2]);
+ }));
+
+
+ iit('should work when placed on a root element of attr directive with ASYNC replaced template',
+ inject(function($templateCache, $compile, $rootScope) {
+ $compileProvider.directive('replaceMeWithRepeater', function() {
+ return {
+ replace: true,
+ templateUrl: 'replace-me-with-repeater.html'
+ }
+ });
+ $templateCache.put('replace-me-with-repeater.html', '{{log(i)}}
');
+ element = jqLite('--');
+ $compile(element)($rootScope);
+ expect(element.text()).toBe('--');
+ var logs = [];
+ $rootScope.log = function(t) { logs.push(t); };
+
+ // This creates one item, but it has no parent so we can't get to it
+ $rootScope.items = [1, 2];
+ $rootScope.$apply();
+
+ // This cleans up to prevent memory leak
+ $rootScope.items = [];
+ $rootScope.$apply();
+ expect(sortedHtml(element)).toBe('--');
+ expect(logs).toEqual([1, 2, 1, 2]);
+ }));
+
+
+ it('should work when placed on a root element of element directive with SYNC replaced template',
+ inject(function($templateCache, $compile, $rootScope) {
+ $compileProvider.directive('replaceMeWithRepeater', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ template: '{{i}}
'
+ }
+ });
+ element = $compile('
')($rootScope);
+ expect(element.text()).toBe('');
+ $rootScope.$apply();
+ expect(element.text()).toBe('123');
+ }));
+
+
+ it('should work when placed on a root element of element directive with ASYNC replaced template',
+ inject(function($templateCache, $compile, $rootScope) {
+ $compileProvider.directive('replaceMeWithRepeater', function() {
+ return {
+ restrict: 'E',
+ replace: true,
+ templateUrl: 'replace-me-with-repeater.html'
+ }
+ });
+ $templateCache.put('replace-me-with-repeater.html', '{{i}}
');
+ element = $compile('
')($rootScope);
+ expect(element.text()).toBe('');
+ $rootScope.$apply();
+ expect(element.text()).toBe('123');
+ }));
+ });
+
+
describe('stability', function() {
var a, b, c, d, lis;
--
cgit v1.2.3