From 31c450bcee53d0a3827b7e0a611e9013b2496506 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Mon, 2 Dec 2013 15:05:21 -0500 Subject: [PATCH] fix($compile) support templates with table content root nodes If the first element in a template is a , , , or tag, the HTML compiler will ensure that the template is wrapped in a element so that the table content is not discarded. Closes #2848 Closes #1459 Closes #3647 Closes #3241 --- src/ng/compile.js | 31 +++++++++++--- test/ng/compileSpec.js | 97 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+), 5 deletions(-) diff --git a/src/ng/compile.js b/src/ng/compile.js index de65c83e2c2c..e4bf230ed68b 100644 --- a/src/ng/compile.js +++ b/src/ng/compile.js @@ -502,7 +502,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { var hasDirectives = {}, Suffix = 'Directive', COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/, - CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/; + CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/, + TABLE_CONTENT_REGEXP = /^<\s*(tr|th|td|tbody)(\s+[^>]*)?>/i; // Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes // The assumption is that future DOM event attribute names will begin with @@ -1243,9 +1244,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { if (directive.replace) { replaceDirective = directive; - $template = jqLite('
' + - trim(directiveValue) + - '
').contents(); + $template = directiveTemplateContents(directiveValue); compileNode = $template[0]; if ($template.length != 1 || compileNode.nodeType !== 1) { @@ -1644,6 +1643,28 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { } + function directiveTemplateContents(template) { + var type; + template = trim(template); + if ((type = TABLE_CONTENT_REGEXP.exec(template))) { + type = type[1].toLowerCase(); + var table = jqLite('
' + template + '
'), + tbody = table.children('tbody'), + leaf = /(td|th)/.test(type) && table.find('tr'); + if (tbody.length && type !== 'tbody') { + table = tbody; + } + if (leaf && leaf.length) { + table = leaf; + } + return table.contents(); + } + return jqLite('
' + + template + + '
').contents(); + } + + function compileTemplateUrl(directives, $compileNode, tAttrs, $rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) { var linkQueue = [], @@ -1668,7 +1689,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) { content = denormalizeTemplate(content); if (origAsyncDirective.replace) { - $template = jqLite('
' + trim(content) + '
').contents(); + $template = directiveTemplateContents(content); compileNode = $template[0]; if ($template.length != 1 || compileNode.nodeType !== 1) { diff --git a/test/ng/compileSpec.js b/test/ng/compileSpec.js index 60b1024bf97f..e9ab15e4b712 100755 --- a/test/ng/compileSpec.js +++ b/test/ng/compileSpec.js @@ -517,6 +517,22 @@ describe('$compile', function() { expect(element).toBe(attr.$$element); } })); + directive('replaceWithTr', valueFn({ + replace: true, + template: 'TR' + })); + directive('replaceWithTd', valueFn({ + replace: true, + template: 'TD' + })); + directive('replaceWithTh', valueFn({ + replace: true, + template: 'TH' + })); + directive('replaceWithTbody', valueFn({ + replace: true, + template: 'TD' + })); })); @@ -680,6 +696,34 @@ describe('$compile', function() { }).not.toThrow(); }); }); + + it('should support templates with root tags', inject(function($compile, $rootScope) { + expect(function() { + element = $compile('
')($rootScope); + }).not.toThrow(); + expect(nodeName_(element)).toMatch(/tr/i); + })); + + it('should support templates with root tags', inject(function($compile, $rootScope) { + expect(function() { + element = $compile('
')($rootScope); + }).not.toThrow(); + expect(nodeName_(element)).toMatch(/td/i); + })); + + it('should support templates with root tags', inject(function($compile, $rootScope) { + expect(function() { + element = $compile('
')($rootScope); + }).not.toThrow(); + expect(nodeName_(element)).toMatch(/th/i); + })); + + it('should support templates with root tags', inject(function($compile, $rootScope) { + expect(function() { + element = $compile('
')($rootScope); + }).not.toThrow(); + expect(nodeName_(element)).toMatch(/tbody/i); + })); }); @@ -776,6 +820,23 @@ describe('$compile', function() { replace: true, template: 'Hello, {{name}}!' })); + + directive('replaceWithTr', valueFn({ + replace: true, + templateUrl: 'tr.html' + })); + directive('replaceWithTd', valueFn({ + replace: true, + templateUrl: 'td.html' + })); + directive('replaceWithTh', valueFn({ + replace: true, + templateUrl: 'th.html' + })); + directive('replaceWithTbody', valueFn({ + replace: true, + templateUrl: 'tbody.html' + })); } )); @@ -1411,6 +1472,42 @@ describe('$compile', function() { expect(element.html()).toContain('i = 1'); }); }); + + it('should support templates with root tags', inject(function($compile, $rootScope, $templateCache) { + $templateCache.put('tr.html', 'TR'); + expect(function() { + element = $compile('
')($rootScope); + }).not.toThrow(); + $rootScope.$digest(); + expect(nodeName_(element)).toMatch(/tr/i); + })); + + it('should support templates with root tags', inject(function($compile, $rootScope, $templateCache) { + $templateCache.put('td.html', 'TD'); + expect(function() { + element = $compile('
')($rootScope); + }).not.toThrow(); + $rootScope.$digest(); + expect(nodeName_(element)).toMatch(/td/i); + })); + + it('should support templates with root tags', inject(function($compile, $rootScope, $templateCache) { + $templateCache.put('th.html', 'TH'); + expect(function() { + element = $compile('
')($rootScope); + }).not.toThrow(); + $rootScope.$digest(); + expect(nodeName_(element)).toMatch(/th/i); + })); + + it('should support templates with root tags', inject(function($compile, $rootScope, $templateCache) { + $templateCache.put('tbody.html', 'TD'); + expect(function() { + element = $compile('
')($rootScope); + }).not.toThrow(); + $rootScope.$digest(); + expect(nodeName_(element)).toMatch(/tbody/i); + })); });