From 9918b748be01266eb10db39d51b4d3098d54ab66 Mon Sep 17 00:00:00 2001 From: Misko Hevery Date: Thu, 15 Mar 2012 22:18:06 -0700 Subject: [PATCH] fix(compiler): allow transclusion of root elements Fixed an issue where a directive that uses transclusion (such as ngRepeat) failed to link if it was declared on the root element of the compilation tree. (For example ngView or ngInclude including template where ngRepeat was the top most element). --- src/Angular.js | 2 +- src/angular.suffix | 2 +- src/jqLite.js | 19 ++++++++++--------- src/scenario/angular.suffix | 2 +- src/service/compiler.js | 7 +++++-- test/jqLiteSpec.js | 2 +- test/service/compilerSpec.js | 22 ++++++++++++++++++++++ 7 files changed, 41 insertions(+), 15 deletions(-) diff --git a/src/Angular.js b/src/Angular.js index 0e9a98ef3424..3ce8e7dfd060 100644 --- a/src/Angular.js +++ b/src/Angular.js @@ -936,7 +936,7 @@ function bindJQuery() { JQLitePatchJQueryRemove('empty'); JQLitePatchJQueryRemove('html'); } else { - jqLite = jqLiteWrap; + jqLite = JQLite; } angular.element = jqLite; } diff --git a/src/angular.suffix b/src/angular.suffix index 44cddef34d4c..ce80e4b0d02e 100644 --- a/src/angular.suffix +++ b/src/angular.suffix @@ -4,7 +4,7 @@ publishExternalAPI(angular); - jqLiteWrap(document).ready(function() { + JQLite(document).ready(function() { angularInit(document, bootstrap); }); diff --git a/src/jqLite.js b/src/jqLite.js index 2dd1aeb7728c..986d0002ae33 100644 --- a/src/jqLite.js +++ b/src/jqLite.js @@ -168,17 +168,18 @@ function JQLitePatchJQueryRemove(name, dispatchThis) { } ///////////////////////////////////////////// -function jqLiteWrap(element) { - if (isString(element) && element.charAt(0) != '<') { - throw new Error('selectors not implemented'); - } - return new JQLite(element); -} - function JQLite(element) { if (element instanceof JQLite) { return element; - } else if (isString(element)) { + } + if (!(this instanceof JQLite)) { + if (isString(element) && element.charAt(0) != '<') { + throw Error('selectors not implemented'); + } + return new JQLite(element); + } + + if (isString(element)) { var div = document.createElement('div'); // Read about the NoScope elements here: // http://msdn.microsoft.com/en-us/library/ms533897(VS.85).aspx @@ -299,7 +300,7 @@ var JQLitePrototype = JQLite.prototype = { this.bind('DOMContentLoaded', trigger); // works for modern browsers and IE9 // we can not use jqLite since we are not done loading and jQuery could be loaded later. - jqLiteWrap(window).bind('load', trigger); // fallback to window.onload for others + JQLite(window).bind('load', trigger); // fallback to window.onload for others }, toString: function() { var value = []; diff --git a/src/scenario/angular.suffix b/src/scenario/angular.suffix index c75c5cca152c..846dbe176b39 100644 --- a/src/scenario/angular.suffix +++ b/src/scenario/angular.suffix @@ -14,7 +14,7 @@ angular.forEach(script.attributes, function(attr) { }); if (config.autotest) { - jqLiteWrap(document).ready(function() { + JQLite(document).ready(function() { angular.scenario.setUpAndRun(config); }); } diff --git a/src/service/compiler.js b/src/service/compiler.js index 8aba28e422b9..6cbfc9d3834c 100644 --- a/src/service/compiler.js +++ b/src/service/compiler.js @@ -225,7 +225,10 @@ function $CompileProvider($provide) { //================================ function compile(templateElement, transcludeFn, maxPriority) { - templateElement = jqLite(templateElement); + if (!(templateElement instanceof jqLite)) { + // jquery always rewraps, where as we need to preserve the original selector so that we can modify it. + templateElement = jqLite(templateElement); + } // We can not compile top level text elements since text nodes can be merged and we will // not be able to attach scope data to them, so we will wrap them in forEach(templateElement, function(node, index){ @@ -488,7 +491,7 @@ function $CompileProvider($provide) { template = jqLite(templateNode); templateNode = (element = templateAttrs.$element = jqLite( ''))[0]; - template.replaceWith(templateNode); + replaceWith(rootElement, jqLite(template[0]), templateNode); childTranscludeFn = compile(template, transcludeFn, terminalPriority); } else { template = jqLite(JQLiteClone(templateNode)); diff --git a/test/jqLiteSpec.js b/test/jqLiteSpec.js index 5cad8c24b528..417b912c2682 100644 --- a/test/jqLiteSpec.js +++ b/test/jqLiteSpec.js @@ -39,7 +39,7 @@ describe('jqLite', function() { it('should be jqLite when jqLiteMode is on, otherwise jQuery', function() { - expect(jqLite).toBe(_jqLiteMode ? jqLiteWrap : _jQuery); + expect(jqLite).toBe(_jqLiteMode ? JQLite : _jQuery); }); diff --git a/test/service/compilerSpec.js b/test/service/compilerSpec.js index 28c9149297e1..f977294b0257 100644 --- a/test/service/compilerSpec.js +++ b/test/service/compilerSpec.js @@ -1751,5 +1751,27 @@ describe('$compile', function() { }); }); + + it('should support transcluded element on root content', function() { + var comment; + module(function($compileProvider) { + $compileProvider.directive('transclude', valueFn({ + transclude: 'element', + compile: function(element, attr, linker) { + return function(scope, element, attr) { + comment = element; + }; + } + })); + }); + inject(function($compile, $rootScope) { + var element = jqLite('
before
after
').contents(); + expect(element.length).toEqual(3); + expect(nodeName_(element[1])).toBe('DIV'); + $compile(element)($rootScope); + expect(nodeName_(element[1])).toBe('#comment'); + expect(nodeName_(comment)).toBe('#comment'); + }); + }); }); });