X Tutup
Skip to content

Commit d599fd3

Browse files
committed
fix(Compiler): fix text nodes after content tags
fixes #2095
1 parent b2e6ad8 commit d599fd3

File tree

4 files changed

+59
-30
lines changed

4 files changed

+59
-30
lines changed

modules/angular2/src/render/dom/shadow_dom/content_tag.ts

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as ldModule from './light_dom';
22
import {DOM} from 'angular2/src/dom/dom_adapter';
3-
import {isPresent} from 'angular2/src/facade/lang';
3+
import {isPresent, isBlank} from 'angular2/src/facade/lang';
44
import {List, ListWrapper} from 'angular2/src/facade/collection';
55

66
class ContentStrategy {
@@ -20,23 +20,30 @@ class RenderedContent extends ContentStrategy {
2020
constructor(contentEl) {
2121
super();
2222
this.beginScript = contentEl;
23-
this.endScript = DOM.nextSibling(this.beginScript);
2423
this.nodes = [];
2524
}
2625

2726
// Inserts the nodes in between the start and end scripts.
2827
// Previous content is removed.
2928
insert(nodes: List</*node*/ any>) {
3029
this.nodes = nodes;
30+
31+
if (isBlank(this.endScript)) {
32+
// On first invocation, we need to create the end marker
33+
this.endScript = DOM.createScriptTag('type', 'ng/contentEnd');
34+
DOM.insertAfter(this.beginScript, this.endScript);
35+
} else {
36+
// On subsequent invocations, only remove all the nodes between the start end end markers
37+
this._removeNodes();
38+
}
39+
3140
DOM.insertAllBefore(this.endScript, nodes);
32-
this._removeNodesUntil(ListWrapper.isEmpty(nodes) ? this.endScript : nodes[0]);
3341
}
3442

35-
_removeNodesUntil(node) {
36-
var p = DOM.parentElement(this.beginScript);
37-
for (var next = DOM.nextSibling(this.beginScript); next !== node;
38-
next = DOM.nextSibling(this.beginScript)) {
39-
DOM.removeChild(p, next);
43+
_removeNodes() {
44+
for (var node = DOM.nextSibling(this.beginScript); node !== this.endScript;
45+
node = DOM.nextSibling(this.beginScript)) {
46+
DOM.remove(node);
4047
}
4148
}
4249
}

modules/angular2/src/render/dom/shadow_dom/shadow_dom_compile_step.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,15 @@ export class ShadowDomCompileStep implements CompileStep {
4747
var selector = MapWrapper.get(attrs, 'select');
4848
selector = isPresent(selector) ? selector : '';
4949

50+
// The content tag should be replaced by a pair of marker tags (start & end).
51+
// The end marker creation is delayed to keep the number of elements constant.
52+
// Creating the end marker here would invalidate the parent's textNodeIndices for the subsequent
53+
// text nodes
5054
var contentStart = DOM.createScriptTag('type', 'ng/contentStart');
5155
if (assertionsEnabled()) {
5256
DOM.setAttribute(contentStart, 'select', selector);
5357
}
54-
var contentEnd = DOM.createScriptTag('type', 'ng/contentEnd');
5558
DOM.insertBefore(current.element, contentStart);
56-
DOM.insertBefore(current.element, contentEnd);
5759
DOM.remove(current.element);
5860

5961
current.element = contentStart;

modules/angular2/test/render/dom/shadow_dom/content_tag_spec.ts

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -13,43 +13,39 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
1313
import {Content} from 'angular2/src/render/dom/shadow_dom/content_tag';
1414

1515
var _scriptStart = `<script start=""></script>`;
16-
var _scriptEnd = `<script end=""></script>`;
1716

1817
export function main() {
1918
describe('Content', function() {
2019
var parent;
2120
var content;
2221

2322
beforeEach(() => {
24-
parent = el(`<div>${_scriptStart}${_scriptEnd}`);
25-
content = DOM.firstChild(parent);
23+
parent = el(`<div>${_scriptStart}</div>`);
24+
let contentStartMarker = DOM.firstChild(parent);
25+
content = new Content(contentStartMarker, '');
2626
});
2727

2828
it("should insert the nodes", () => {
29-
var c = new Content(content, '');
30-
c.init(null);
31-
c.insert([el("<a></a>"), el("<b></b>")])
29+
content.init(null);
30+
content.insert([el("<a>A</a>"), el("<b>B</b>")]);
3231

33-
expect(DOM.getInnerHTML(parent))
34-
.toEqual(`${_scriptStart}<a></a><b></b>${_scriptEnd}`);
32+
expect(parent).toHaveText('AB');
3533
});
3634

3735
it("should remove the nodes from the previous insertion", () => {
38-
var c = new Content(content, '');
39-
c.init(null);
40-
c.insert([el("<a></a>")]);
41-
c.insert([el("<b></b>")]);
36+
content.init(null);
37+
content.insert([el("<a>A</a>")]);
38+
content.insert([el("<b>B</b>")]);
4239

43-
expect(DOM.getInnerHTML(parent)).toEqual(`${_scriptStart}<b></b>${_scriptEnd}`);
40+
expect(parent).toHaveText('B');
4441
});
4542

46-
it("should insert empty list", () => {
47-
var c = new Content(content, '');
48-
c.init(null);
49-
c.insert([el("<a></a>")]);
50-
c.insert([]);
43+
it("should clear nodes on inserting an empty list", () => {
44+
content.init(null);
45+
content.insert([el("<a>A</a>")]);
46+
content.insert([]);
5147

52-
expect(DOM.getInnerHTML(parent)).toEqual(`${_scriptStart}${_scriptEnd}`);
48+
expect(parent).toHaveText('');
5349
});
5450
});
5551
}

modules/angular2/test/render/dom/shadow_dom_emulation_integration_spec.ts

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import {
1010
it,
1111
xit,
1212
beforeEachBindings,
13-
SpyObject,
13+
SpyObject
1414
} from 'angular2/test_lib';
1515

1616
import {bind} from 'angular2/di';
@@ -34,6 +34,10 @@ import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner';
3434

3535
import {DomTestbed} from './dom_testbed';
3636

37+
import {Injectable} from 'angular2/di';
38+
39+
import {Component, View} from 'angular2/annotations';
40+
3741
export function main() {
3842
describe('ShadowDom integration tests', function() {
3943
var strategies = {
@@ -60,6 +64,26 @@ export function main() {
6064
beforeEachBindings(() => { return [strategyBinding, DomTestbed]; });
6165

6266
describe(`${name} shadow dom strategy`, () => {
67+
// GH-2095 - https://github.com/angular/angular/issues/2095
68+
it('should support text nodes after content tags',
69+
inject([DomTestbed, AsyncTestCompleter], (tb, async) => {
70+
tb.compileAll([
71+
simple,
72+
new ViewDefinition({
73+
componentId: 'simple',
74+
template: '<content></content><p>P,</p>{{a}}',
75+
directives: []
76+
})
77+
])
78+
.then((protoViewDtos) => {
79+
var rootView = tb.createRootView(protoViewDtos[0]);
80+
var cmpView = tb.createComponentView(rootView.viewRef, 0, protoViewDtos[1]);
81+
82+
tb.renderer.setText(cmpView.viewRef, 0, 'text');
83+
expect(tb.rootEl).toHaveText('P,text');
84+
async.done();
85+
});
86+
}));
6387

6488
it('should support simple components',
6589
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {

0 commit comments

Comments
 (0)
X Tutup