fix(view): remove dynamic components when the parent view is dehydrated

Also adds a bunch of unit tests for affected parts.

Fixes #1201
This commit is contained in:
Tobias Bosch
2015-04-14 18:01:44 -07:00
parent 6ecaa9aebb
commit 213dabdceb
16 changed files with 813 additions and 117 deletions

View File

@ -43,14 +43,12 @@ export class ComponentRef {
export class DynamicComponentLoader {
_compiler:Compiler;
_viewFactory:ViewFactory;
_renderer:Renderer;
_directiveMetadataReader:DirectiveMetadataReader;
constructor(compiler:Compiler, directiveMetadataReader:DirectiveMetadataReader,
renderer:Renderer, viewFactory:ViewFactory) {
this._compiler = compiler;
this._directiveMetadataReader = directiveMetadataReader;
this._renderer = renderer;
this._viewFactory = viewFactory
}
@ -67,16 +65,13 @@ export class DynamicComponentLoader {
var hostEi = location.elementInjector;
var hostView = location.hostView;
return this._compiler.compile(type).then(componentProtoView => {
var component = hostEi.dynamicallyCreateComponent(type, directiveMetadata.annotation, inj);
var componentView = this._instantiateAndHydrateView(componentProtoView, injector, hostEi, component);
//TODO(vsavkin): do not use component child views as we need to clear the dynamically created views
//same problem exists on the render side
hostView.addComponentChildView(componentView);
this._renderer.setDynamicComponentView(hostView.render, location.boundElementIndex, componentView.render);
hostView.setDynamicComponentChildView(location.boundElementIndex, componentView);
// TODO(vsavkin): return a component ref that dehydrates the component view and removes it
// from the component child views

View File

@ -1,4 +1,4 @@
import {int, isBlank, BaseException} from 'angular2/src/facade/lang';
import {int, isBlank, isPresent, BaseException} from 'angular2/src/facade/lang';
import * as eiModule from './element_injector';
import {DirectiveBinding} from './element_injector';
import {List, StringMap} from 'angular2/src/facade/collection';
@ -32,4 +32,12 @@ export class ElementBinder {
// updated later, so we are able to resolve cycles
this.nestedProtoView = null;
}
hasStaticComponent() {
return isPresent(this.componentDirective) && isPresent(this.nestedProtoView);
}
hasDynamicComponent() {
return isPresent(this.componentDirective) && isBlank(this.nestedProtoView);
}
}

View File

@ -143,7 +143,6 @@ export class AppView {
}
var binders = this.proto.elementBinders;
var componentChildViewIndex = 0;
for (var i = 0; i < binders.length; ++i) {
var componentDirective = binders[i].componentDirective;
var shadowDomAppInjector = null;
@ -176,8 +175,8 @@ export class AppView {
}
}
if (isPresent(binders[i].nestedProtoView) && isPresent(componentDirective)) {
renderComponentIndex = this.componentChildViews[componentChildViewIndex].internalHydrateRecurse(
if (binders[i].hasStaticComponent()) {
renderComponentIndex = this.componentChildViews[i].internalHydrateRecurse(
renderComponentViewRefs,
renderComponentIndex,
shadowDomAppInjector,
@ -185,7 +184,6 @@ export class AppView {
elementInjector.getComponent(),
null
);
componentChildViewIndex++;
}
}
this._hydrateChangeDetector();
@ -198,7 +196,15 @@ export class AppView {
// componentChildViews
for (var i = 0; i < this.componentChildViews.length; i++) {
this.componentChildViews[i].internalDehydrateRecurse();
var componentView = this.componentChildViews[i];
if (isPresent(componentView)) {
componentView.internalDehydrateRecurse();
var binder = this.proto.elementBinders[i];
if (binder.hasDynamicComponent()) {
this.componentChildViews[i] = null;
this.changeDetector.removeShadowDomChild(componentView.changeDetector);
}
}
}
// elementInjectors
@ -255,9 +261,16 @@ export class AppView {
return elementInjector.getDirectiveAtIndex(directive.directiveIndex);
}
addComponentChildView(view:AppView) {
ListWrapper.push(this.componentChildViews, view);
setDynamicComponentChildView(boundElementIndex, view:AppView) {
if (!this.proto.elementBinders[boundElementIndex].hasDynamicComponent()) {
throw new BaseException(`There is no dynamic component directive at element ${boundElementIndex}`);
}
if (isPresent(this.componentChildViews[boundElementIndex])) {
throw new BaseException(`There already is a bound component at element ${boundElementIndex}`);
}
this.componentChildViews[boundElementIndex] = view;
this.changeDetector.addShadowDomChild(view.changeDetector);
this.proto.renderer.setDynamicComponentView(this.render, boundElementIndex, view.render);
}
// implementation of EventDispatcher#dispatchEvent

View File

@ -57,7 +57,7 @@ export class ViewFactory {
var rootElementInjectors = [];
var preBuiltObjects = ListWrapper.createFixedSize(binders.length);
var viewContainers = ListWrapper.createFixedSize(binders.length);
var componentChildViews = [];
var componentChildViews = ListWrapper.createFixedSize(binders.length);
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
var binder = binders[binderIdx];
@ -78,13 +78,13 @@ export class ViewFactory {
// componentChildViews
var bindingPropagationConfig = null;
if (isPresent(binder.nestedProtoView) && isPresent(binder.componentDirective)) {
if (binder.hasStaticComponent()) {
var childView = this._createView(binder.nestedProtoView);
changeDetector.addChild(childView.changeDetector);
changeDetector.addShadowDomChild(childView.changeDetector);
bindingPropagationConfig = new BindingPropagationConfig(childView.changeDetector);
ListWrapper.push(componentChildViews, childView);
componentChildViews[binderIdx] = childView;
}
// viewContainers