From 433454c81db68fad081bba8016289482c2097dae Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Thu, 28 Aug 2014 11:47:41 -0700 Subject: [PATCH] fix($compile): use the correct namespace for transcluded svg elements This fixes the case when a directive that uses `templateUrl` is used inside of a transcluding directive like `ng-repeat`. Fixes #8808 --- src/ng/compile.js | 24 +++++++++++++----------- test/ng/compileSpec.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 11 deletions(-) diff --git a/src/ng/compile.js b/src/ng/compile.js index b6cfe5c372b2..8622b3b98f76 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -955,26 +955,29 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { var compositeLinkFn = compileNodes($compileNodes, transcludeFn, $compileNodes, maxPriority, ignoreDirective, previousCompileContext); - compile.$$addScopeClass($compileNodes); - + var namespace = null; + var namespaceAdaptedCompileNodes = $compileNodes; + var lastCompileNode; return function publicLinkFn(scope, cloneConnectFn, transcludeControllers, parentBoundTranscludeFn, futureParentElement){ - var namespace = null; assertArg(scope, 'scope'); if (!namespace) { namespace = detectNamespaceForChildElements(futureParentElement); - if (namespace !== 'html') { - $compileNodes = jqLite( - wrapTemplate(namespace, jqLite('
').append($compileNodes).html()) - ); - } } + if (namespace !== 'html' && $compileNodes[0] !== lastCompileNode) { + namespaceAdaptedCompileNodes = jqLite( + wrapTemplate(namespace, jqLite('
').append($compileNodes).html()) + ); + } + // When using a directive with replace:true and templateUrl the $compileNodes + // might change, so we need to recreate the namespace adapted compileNodes. + lastCompileNode = $compileNodes[0]; // important!!: we must call our jqLite.clone() since the jQuery one is trying to be smart // and sometimes changes the structure of the DOM. var $linkNode = cloneConnectFn - ? JQLitePrototype.clone.call($compileNodes) // IMPORTANT!!! - : $compileNodes; + ? JQLitePrototype.clone.call(namespaceAdaptedCompileNodes) // IMPORTANT!!! + : namespaceAdaptedCompileNodes; if (transcludeControllers) { for (var controllerName in transcludeControllers) { @@ -1940,7 +1943,6 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { // it was cloned therefore we have to clone as well. linkNode = jqLiteClone(compileNode); } - replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode); // Copy in CSS classes from original node diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 41808b2351a5..d628ba716dc7 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -289,6 +289,39 @@ describe('$compile', function() { })); + it('should support directives with SVG templates and a slow url '+ + 'that are stamped out later by a transcluding directive', function() { + module(function() { + directive('svgCircleUrl', valueFn({ + replace: true, + templateUrl: 'template.html', + templateNamespace: 'SVG', + })); + }); + inject(function($compile, $rootScope, $httpBackend) { + $httpBackend.expect('GET', 'template.html').respond(''); + element = $compile('')($rootScope); + + // initially the template is not yet loaded + $rootScope.$apply(function() { + $rootScope.list = [1]; + }); + expect(element.find('svg-circle-url').length).toBe(1); + expect(element.find('circle').length).toBe(0); + + // template is loaded and replaces the existing nodes + $httpBackend.flush(); + expect(element.find('svg-circle-url').length).toBe(0); + expect(element.find('circle').length).toBe(1); + + // new entry should immediately use the loaded template + $rootScope.$apply(function() { + $rootScope.list.push(2); + }); + expect(element.find('svg-circle-url').length).toBe(0); + expect(element.find('circle').length).toBe(2); + }); + }); });