Skip to content
This repository was archived by the owner on Apr 12, 2024. It is now read-only.

Commit 31c450b

Browse files
Caitlin Pottercaitp
Caitlin Potter
authored andcommitted
fix($compile) support templates with table content root nodes
If the first element in a template is a <tr>, <th>, <td>, or <tbody> tag, the HTML compiler will ensure that the template is wrapped in a <table> element so that the table content is not discarded. Closes #2848 Closes #1459 Closes #3647 Closes #3241
1 parent a9fcb0d commit 31c450b

File tree

2 files changed

+123
-5
lines changed

2 files changed

+123
-5
lines changed

src/ng/compile.js

+26-5
Original file line numberDiff line numberDiff line change
@@ -502,7 +502,8 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
502502
var hasDirectives = {},
503503
Suffix = 'Directive',
504504
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\d\w\-_]+)\s+(.*)$/,
505-
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/;
505+
CLASS_DIRECTIVE_REGEXP = /(([\d\w\-_]+)(?:\:([^;]+))?;?)/,
506+
TABLE_CONTENT_REGEXP = /^<\s*(tr|th|td|tbody)(\s+[^>]*)?>/i;
506507

507508
// Ref: http://developers.whatwg.org/webappapis.html#event-handler-idl-attributes
508509
// The assumption is that future DOM event attribute names will begin with
@@ -1243,9 +1244,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
12431244

12441245
if (directive.replace) {
12451246
replaceDirective = directive;
1246-
$template = jqLite('<div>' +
1247-
trim(directiveValue) +
1248-
'</div>').contents();
1247+
$template = directiveTemplateContents(directiveValue);
12491248
compileNode = $template[0];
12501249

12511250
if ($template.length != 1 || compileNode.nodeType !== 1) {
@@ -1644,6 +1643,28 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16441643
}
16451644

16461645

1646+
function directiveTemplateContents(template) {
1647+
var type;
1648+
template = trim(template);
1649+
if ((type = TABLE_CONTENT_REGEXP.exec(template))) {
1650+
type = type[1].toLowerCase();
1651+
var table = jqLite('<table>' + template + '</table>'),
1652+
tbody = table.children('tbody'),
1653+
leaf = /(td|th)/.test(type) && table.find('tr');
1654+
if (tbody.length && type !== 'tbody') {
1655+
table = tbody;
1656+
}
1657+
if (leaf && leaf.length) {
1658+
table = leaf;
1659+
}
1660+
return table.contents();
1661+
}
1662+
return jqLite('<div>' +
1663+
template +
1664+
'</div>').contents();
1665+
}
1666+
1667+
16471668
function compileTemplateUrl(directives, $compileNode, tAttrs,
16481669
$rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
16491670
var linkQueue = [],
@@ -1668,7 +1689,7 @@ function $CompileProvider($provide, $$sanitizeUriProvider) {
16681689
content = denormalizeTemplate(content);
16691690

16701691
if (origAsyncDirective.replace) {
1671-
$template = jqLite('<div>' + trim(content) + '</div>').contents();
1692+
$template = directiveTemplateContents(content);
16721693
compileNode = $template[0];
16731694

16741695
if ($template.length != 1 || compileNode.nodeType !== 1) {

test/ng/compileSpec.js

+97
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,22 @@ describe('$compile', function() {
517517
expect(element).toBe(attr.$$element);
518518
}
519519
}));
520+
directive('replaceWithTr', valueFn({
521+
replace: true,
522+
template: '<tr><td>TR</td></tr>'
523+
}));
524+
directive('replaceWithTd', valueFn({
525+
replace: true,
526+
template: '<td>TD</td>'
527+
}));
528+
directive('replaceWithTh', valueFn({
529+
replace: true,
530+
template: '<th>TH</th>'
531+
}));
532+
directive('replaceWithTbody', valueFn({
533+
replace: true,
534+
template: '<tbody><tr><td>TD</td></tr></tbody>'
535+
}));
520536
}));
521537

522538

@@ -680,6 +696,34 @@ describe('$compile', function() {
680696
}).not.toThrow();
681697
});
682698
});
699+
700+
it('should support templates with root <tr> tags', inject(function($compile, $rootScope) {
701+
expect(function() {
702+
element = $compile('<div replace-with-tr></div>')($rootScope);
703+
}).not.toThrow();
704+
expect(nodeName_(element)).toMatch(/tr/i);
705+
}));
706+
707+
it('should support templates with root <td> tags', inject(function($compile, $rootScope) {
708+
expect(function() {
709+
element = $compile('<div replace-with-td></div>')($rootScope);
710+
}).not.toThrow();
711+
expect(nodeName_(element)).toMatch(/td/i);
712+
}));
713+
714+
it('should support templates with root <th> tags', inject(function($compile, $rootScope) {
715+
expect(function() {
716+
element = $compile('<div replace-with-th></div>')($rootScope);
717+
}).not.toThrow();
718+
expect(nodeName_(element)).toMatch(/th/i);
719+
}));
720+
721+
it('should support templates with root <tbody> tags', inject(function($compile, $rootScope) {
722+
expect(function() {
723+
element = $compile('<div replace-with-tbody></div>')($rootScope);
724+
}).not.toThrow();
725+
expect(nodeName_(element)).toMatch(/tbody/i);
726+
}));
683727
});
684728

685729

@@ -776,6 +820,23 @@ describe('$compile', function() {
776820
replace: true,
777821
template: '<span>Hello, {{name}}!</span>'
778822
}));
823+
824+
directive('replaceWithTr', valueFn({
825+
replace: true,
826+
templateUrl: 'tr.html'
827+
}));
828+
directive('replaceWithTd', valueFn({
829+
replace: true,
830+
templateUrl: 'td.html'
831+
}));
832+
directive('replaceWithTh', valueFn({
833+
replace: true,
834+
templateUrl: 'th.html'
835+
}));
836+
directive('replaceWithTbody', valueFn({
837+
replace: true,
838+
templateUrl: 'tbody.html'
839+
}));
779840
}
780841
));
781842

@@ -1411,6 +1472,42 @@ describe('$compile', function() {
14111472
expect(element.html()).toContain('i = 1');
14121473
});
14131474
});
1475+
1476+
it('should support templates with root <tr> tags', inject(function($compile, $rootScope, $templateCache) {
1477+
$templateCache.put('tr.html', '<tr><td>TR</td></tr>');
1478+
expect(function() {
1479+
element = $compile('<div replace-with-tr></div>')($rootScope);
1480+
}).not.toThrow();
1481+
$rootScope.$digest();
1482+
expect(nodeName_(element)).toMatch(/tr/i);
1483+
}));
1484+
1485+
it('should support templates with root <td> tags', inject(function($compile, $rootScope, $templateCache) {
1486+
$templateCache.put('td.html', '<td>TD</td>');
1487+
expect(function() {
1488+
element = $compile('<div replace-with-td></div>')($rootScope);
1489+
}).not.toThrow();
1490+
$rootScope.$digest();
1491+
expect(nodeName_(element)).toMatch(/td/i);
1492+
}));
1493+
1494+
it('should support templates with root <th> tags', inject(function($compile, $rootScope, $templateCache) {
1495+
$templateCache.put('th.html', '<th>TH</th>');
1496+
expect(function() {
1497+
element = $compile('<div replace-with-th></div>')($rootScope);
1498+
}).not.toThrow();
1499+
$rootScope.$digest();
1500+
expect(nodeName_(element)).toMatch(/th/i);
1501+
}));
1502+
1503+
it('should support templates with root <tbody> tags', inject(function($compile, $rootScope, $templateCache) {
1504+
$templateCache.put('tbody.html', '<tbody><tr><td>TD</td></tr></tbody>');
1505+
expect(function() {
1506+
element = $compile('<div replace-with-tbody></div>')($rootScope);
1507+
}).not.toThrow();
1508+
$rootScope.$digest();
1509+
expect(nodeName_(element)).toMatch(/tbody/i);
1510+
}));
14141511
});
14151512

14161513

0 commit comments

Comments
 (0)