fix(ViewContainer) removeChild called with null parent

In view_container.js, templateElement.parentNode can be null
when two template tags are nested in one another.
Accessing the parent node through view.nodes[0].parentNode fixes
the problem.

closes #997

Closes #999
This commit is contained in:
David-Emmanuel Divernois 2015-03-19 10:04:42 +01:00 committed by Misko Hevery
parent b61b8d60b7
commit bd48c927d0
3 changed files with 66 additions and 3 deletions

View File

@ -130,7 +130,7 @@ export class ViewContainer {
var detachedView = this.get(atIndex); var detachedView = this.get(atIndex);
ListWrapper.removeAt(this._views, atIndex); ListWrapper.removeAt(this._views, atIndex);
if (isBlank(this._lightDom)) { if (isBlank(this._lightDom)) {
ViewContainer.removeViewNodesFromParent(this.templateElement.parentNode, detachedView); ViewContainer.removeViewNodes(detachedView);
} else { } else {
this._lightDom.redistribute(); this._lightDom.redistribute();
} }
@ -173,8 +173,11 @@ export class ViewContainer {
} }
} }
static removeViewNodesFromParent(parent, view) { static removeViewNodes(view) {
for (var i = view.nodes.length - 1; i >= 0; --i) { var len = view.nodes.length;
if (len == 0) return;
var parent = view.nodes[0].parentNode;
for (var i = len - 1; i >= 0; --i) {
DOM.removeChild(parent, view.nodes[i]); DOM.removeChild(parent, view.nodes[i]);
} }
} }

View File

@ -214,6 +214,31 @@ export function main() {
cd.detectChanges(); cd.detectChanges();
cd.detectChanges(); cd.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('a-2;b-2;|c-1;|'); expect(DOM.getText(view.nodes[0])).toEqual('a-2;b-2;|c-1;|');
component.items = [['e'], ['f', 'g']];
cd.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('e-1;|f-2;g-2;|');
async.done();
});
}));
it('should repeat over nested arrays with no intermediate element', inject([AsyncTestCompleter], (async) => {
compileWithTemplate(
'<div><template [for] #item [of]="items">' +
'<div template="for #subitem of item">' +
'{{subitem}}-{{item.length}};' +
'</div></template></div>'
).then((pv) => {
createView(pv);
component.items = [['a', 'b'], ['c']];
cd.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('a-2;b-2;c-1;');
component.items = [['e'], ['f', 'g']];
cd.detectChanges();
expect(DOM.getText(view.nodes[0])).toEqual('e-1;f-2;g-2;');
async.done(); async.done();
}); });
})); }));

View File

@ -116,6 +116,39 @@ export function main() {
}); });
})); }));
it('should handle nested if correctly', inject([AsyncTestCompleter], (async) => {
compileWithTemplate('<div><template [if]="booleanCondition"><copy-me *if="nestedBooleanCondition">hello</copy-me></template></div>').then((pv) => {
createView(pv);
component.booleanCondition = false;
cd.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.nodes[0])).toEqual('');
component.booleanCondition = true;
cd.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.nodes[0])).toEqual('hello');
component.nestedBooleanCondition = false;
cd.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.nodes[0])).toEqual('');
component.nestedBooleanCondition = true;
cd.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(1);
expect(DOM.getText(view.nodes[0])).toEqual('hello');
component.booleanCondition = false;
cd.detectChanges();
expect(DOM.querySelectorAll(view.nodes[0], 'copy-me').length).toEqual(0);
expect(DOM.getText(view.nodes[0])).toEqual('');
async.done();
});
}));
it('should update several nodes with if', inject([AsyncTestCompleter], (async) => { it('should update several nodes with if', inject([AsyncTestCompleter], (async) => {
var templateString = var templateString =
'<div>' + '<div>' +
@ -195,11 +228,13 @@ export function main() {
@Component({selector: 'test-cmp'}) @Component({selector: 'test-cmp'})
class TestComponent { class TestComponent {
booleanCondition: boolean; booleanCondition: boolean;
nestedBooleanCondition: boolean;
numberCondition: number; numberCondition: number;
stringCondition: string; stringCondition: string;
functionCondition: Function; functionCondition: Function;
constructor() { constructor() {
this.booleanCondition = true; this.booleanCondition = true;
this.nestedBooleanCondition = true;
this.numberCondition = 1; this.numberCondition = 1;
this.stringCondition = "foo"; this.stringCondition = "foo";
this.functionCondition = function(s, n){ this.functionCondition = function(s, n){