diff --git a/src/ng/compile.js b/src/ng/compile.js index 4a18b1a7edf..6a6ffc3c53a 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -145,7 +145,7 @@ function $CompileProvider($provide) { Suffix = 'Directive', COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/, CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/, - HAS_ROOT_ELEMENT = /^\<[\s\S]*\>$/; + MULTI_ROOT_TEMPLATE_ERROR = 'Template must have exactly one root element. was: '; this.directive = function registerDirective(name, directiveFactory) { @@ -587,8 +587,14 @@ function $CompileProvider($provide) { assertNoDuplicate('template', templateDirective, directive, $compileNode); templateDirective = directive; - compileNode = jqLite(directiveValue)[0]; + $template = jqLite('
' + trim(directiveValue) + '
').contents(); + compileNode = $template[0]; + if (directive.replace) { + if ($template.length != 1 || compileNode.nodeType !== 1) { + throw new Error(MULTI_ROOT_TEMPLATE_ERROR + directiveValue); + } + replaceWith($rootElement, $compileNode, compileNode); var newTemplateAttrs = {$attr: {}}; @@ -840,15 +846,17 @@ function $CompileProvider($provide) { $http.get(origAsyncDirective.templateUrl, {cache: $templateCache}). success(function(content) { - if (replace && !content.match(HAS_ROOT_ELEMENT)) { - throw Error('Template must have exactly one root element: ' + content); - } - - var compileNode, tempTemplateAttrs; + var compileNode, tempTemplateAttrs, $template; if (replace) { + $template = jqLite('
' + trim(content) + '
').contents(); + compileNode = $template[0]; + + if ($template.length != 1 || compileNode.nodeType !== 1) { + throw new Error(MULTI_ROOT_TEMPLATE_ERROR + content); + } + tempTemplateAttrs = {$attr: {}}; - compileNode = jqLite(content)[0]; replaceWith($rootElement, $compileNode, compileNode); collectDirectives(compileNode, directives, tempTemplateAttrs); mergeTemplateAttributes(tAttrs, tempTemplateAttrs); diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index b2a6856b50b..c55928168c0 100644 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -487,10 +487,48 @@ describe('$compile', function() { expect(child).toHaveClass('three'); expect(child).toHaveClass('log'); // merged from replace directive template })); + + it("should fail if replacing and template doesn't have a single root element", function() { + module(function($compileProvider) { + $compileProvider.directive('noRootElem', function() { + return { + replace: true, + template: 'dada' + } + }); + $compileProvider.directive('multiRootElem', function() { + return { + replace: true, + template: '
' + } + }); + $compileProvider.directive('singleRootWithWhiteSpace', function() { + return { + replace: true, + template: '
\n' + } + }); + }); + + inject(function($compile) { + expect(function() { + $compile('

'); + }).toThrow('Template must have exactly one root element. was: dada'); + + expect(function() { + $compile('

'); + }).toThrow('Template must have exactly one root element. was:
'); + + // ws is ok + expect(function() { + $compile('

'); + }).not.toThrow(); + }); + }); }); - describe('async templates', function() { + describe('templateUrl', function() { beforeEach(module( function($compileProvider) { @@ -916,15 +954,6 @@ describe('$compile', function() { }); - it('should check that template has root element', inject(function($compile, $httpBackend) { - $httpBackend.expect('GET', 'hello.html').respond('before mid after'); - $compile('
'); - expect(function(){ - $httpBackend.flush(); - }).toThrow('Template must have exactly one root element: before mid after'); - })); - - it('should allow multiple elements in template', inject(function($compile, $httpBackend) { $httpBackend.expect('GET', 'hello.html').respond('before mid after'); element = jqLite('
'); @@ -958,6 +987,42 @@ describe('$compile', function() { expect(element.text()).toEqual('i=1;i=2;'); } )); + + + it("should fail if replacing and template doesn't have a single root element", function() { + module(function($exceptionHandlerProvider, $compileProvider) { + $exceptionHandlerProvider.mode('log'); + + $compileProvider.directive('template', function() { + return { + replace: true, + templateUrl: 'template.html' + } + }); + }); + + inject(function($compile, $templateCache, $rootScope, $exceptionHandler) { + // no root element + $templateCache.put('template.html', 'dada'); + $compile('

'); + $rootScope.$digest(); + expect($exceptionHandler.errors.pop().message). + toBe('Template must have exactly one root element. was: dada'); + + // multi root + $templateCache.put('template.html', '
'); + $compile('

'); + $rootScope.$digest(); + expect($exceptionHandler.errors.pop().message). + toBe('Template must have exactly one root element. was:
'); + + // ws is ok + $templateCache.put('template.html', '
\n'); + $compile('

'); + $rootScope.$apply(); + expect($exceptionHandler.errors).toEqual([]); + }); + }); });