diff --git a/modules/angular2/src/core/application.js b/modules/angular2/src/core/application.js index c829d562fe..2aa0405a6d 100644 --- a/modules/angular2/src/core/application.js +++ b/modules/angular2/src/core/application.js @@ -29,11 +29,13 @@ import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner'; import {ComponentRef, DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader'; import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/testability'; import {ViewFactory, VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_factory'; +import {AppViewHydrator} from 'angular2/src/core/compiler/view_hydrator'; import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; import {Renderer} from 'angular2/src/render/api'; import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer'; import * as rc from 'angular2/src/render/dom/compiler/compiler'; import * as rvf from 'angular2/src/render/dom/view/view_factory'; +import * as rvh from 'angular2/src/render/dom/view/view_hydrator'; import { appComponentRefToken, @@ -98,14 +100,16 @@ function _injectorBindings(appComponentType): List { [rvf.VIEW_POOL_CAPACITY, EventManager, ShadowDomStrategy] ), bind(rvf.VIEW_POOL_CAPACITY).toValue(10000), + rvh.RenderViewHydrator, ProtoViewFactory, // TODO(tbosch): We need an explicit factory here, as // we are getting errors in dart2js with mirrors... bind(ViewFactory).toFactory( - (capacity) => new ViewFactory(capacity), - [VIEW_POOL_CAPACITY] + (capacity, renderer) => new ViewFactory(capacity, renderer), + [VIEW_POOL_CAPACITY, Renderer] ), bind(VIEW_POOL_CAPACITY).toValue(10000), + AppViewHydrator, Compiler, CompilerCache, TemplateResolver, diff --git a/modules/angular2/src/core/compiler/compiler.js b/modules/angular2/src/core/compiler/compiler.js index f3da148f96..738c32c17b 100644 --- a/modules/angular2/src/core/compiler/compiler.js +++ b/modules/angular2/src/core/compiler/compiler.js @@ -81,11 +81,11 @@ export class Compiler { return DirectiveBinding.createFromType(meta.type, meta.annotation); } - // Create a rootView as if the compiler encountered . + // Create a hostView as if the compiler encountered . // Used for bootstrapping. - compileRoot(elementOrSelector, componentTypeOrBinding:any):Promise { - return this._renderer.createRootProtoView(elementOrSelector, 'root').then( (rootRenderPv) => { - return this._compileNestedProtoViews(null, rootRenderPv, [this._bindDirective(componentTypeOrBinding)], true); + compileInHost(componentTypeOrBinding:any):Promise { + return this._renderer.createHostProtoView('host').then( (hostRenderPv) => { + return this._compileNestedProtoViews(null, hostRenderPv, [this._bindDirective(componentTypeOrBinding)], true); }); } diff --git a/modules/angular2/src/core/compiler/dynamic_component_loader.js b/modules/angular2/src/core/compiler/dynamic_component_loader.js index ef113fe0b6..ec9036f190 100644 --- a/modules/angular2/src/core/compiler/dynamic_component_loader.js +++ b/modules/angular2/src/core/compiler/dynamic_component_loader.js @@ -2,12 +2,11 @@ import {Key, Injector, Injectable, ResolvedBinding} from 'angular2/di' import {Compiler} from './compiler'; import {DirectiveMetadataReader} from './directive_metadata_reader'; import {Type, BaseException, stringify, isPresent} from 'angular2/src/facade/lang'; -import {List} from 'angular2/src/facade/collection'; import {Promise} from 'angular2/src/facade/async'; import {Component} from 'angular2/src/core/annotations/annotations'; import {ViewFactory} from 'angular2/src/core/compiler/view_factory'; -import {Renderer} from 'angular2/src/render/api'; -import {ElementRef} from './element_injector'; +import {AppViewHydrator} from 'angular2/src/core/compiler/view_hydrator'; +import {ElementRef, DirectiveBinding} from './element_injector'; import {AppView} from './view'; /** @@ -43,13 +42,15 @@ export class ComponentRef { export class DynamicComponentLoader { _compiler:Compiler; _viewFactory:ViewFactory; + _viewHydrator:AppViewHydrator; _directiveMetadataReader:DirectiveMetadataReader; constructor(compiler:Compiler, directiveMetadataReader:DirectiveMetadataReader, - renderer:Renderer, viewFactory:ViewFactory) { + viewFactory:ViewFactory, viewHydrator:AppViewHydrator) { this._compiler = compiler; this._directiveMetadataReader = directiveMetadataReader; - this._viewFactory = viewFactory + this._viewFactory = viewFactory; + this._viewHydrator = viewHydrator; } /** @@ -58,61 +59,44 @@ export class DynamicComponentLoader { */ loadIntoExistingLocation(type:Type, location:ElementRef, injector:Injector = null):Promise { this._assertTypeIsComponent(type); + var annotation = this._directiveMetadataReader.read(type).annotation; + var componentBinding = DirectiveBinding.createFromType(type, annotation); - var directiveMetadata = this._directiveMetadataReader.read(type); - - var inj = this._componentAppInjector(location, injector, directiveMetadata.resolvedInjectables); - - 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.setDynamicComponentChildView(location.boundElementIndex, componentView); + var componentView = this._viewFactory.getView(componentProtoView); + var hostView = location.hostView; + this._viewHydrator.hydrateDynamicComponentView( + hostView, location.boundElementIndex, componentView, componentBinding, injector); // TODO(vsavkin): return a component ref that dehydrates the component view and removes it // from the component child views - return new ComponentRef(location, component, componentView); + // See ViewFactory.returnView + // See AppViewHydrator.dehydrateDynamicComponentView + return new ComponentRef(location, location.elementInjector.getDynamicallyLoadedComponent(), componentView); }); } /** * Loads a component as a child of the View given by the provided ElementRef. The loaded * component receives injection normally as a hosted view. - * - * TODO(vsavkin, jelbourn): remove protoViewFactory after render layer exists. */ loadIntoNewLocation(elementOrSelector:any, type:Type, location:ElementRef, injector:Injector = null):Promise { this._assertTypeIsComponent(type); - var inj = this._componentAppInjector(location, injector, null); - - //TODO(tbosch) this should always be a selector - return this._compiler.compileRoot(elementOrSelector, type).then(pv => { - var hostView = this._instantiateAndHydrateView(pv, inj, null, new Object()); + return this._compiler.compileInHost(type).then(hostProtoView => { + var hostView = this._viewFactory.getView(hostProtoView); + this._viewHydrator.hydrateInPlaceHostView(null, elementOrSelector, hostView, injector); // TODO(vsavkin): return a component ref that dehydrates the host view + // See ViewFactory.returnView + // See AppViewHydrator.dehydrateInPlaceHostView var newLocation = new ElementRef(hostView.elementInjectors[0]); var component = hostView.elementInjectors[0].getComponent(); return new ComponentRef(newLocation, component, hostView.componentChildViews[0]); }); } - _componentAppInjector(location, injector:Injector, resolvedBindings:List) { - var inj = isPresent(injector) ? injector : location.injector; - return isPresent(resolvedBindings) ? inj.createChildFromResolved(resolvedBindings) : inj; - } - - _instantiateAndHydrateView(protoView, injector, hostElementInjector, context) { - var componentView = this._viewFactory.getView(protoView); - componentView.hydrate(injector, hostElementInjector, context, null); - return componentView; - } - /** Asserts that the type being dynamically instantiated is a Component. */ _assertTypeIsComponent(type:Type) { var annotation = this._directiveMetadataReader.read(type).annotation; diff --git a/modules/angular2/src/core/compiler/element_injector.js b/modules/angular2/src/core/compiler/element_injector.js index 3cd5b01afb..7748a9aac2 100644 --- a/modules/angular2/src/core/compiler/element_injector.js +++ b/modules/angular2/src/core/compiler/element_injector.js @@ -575,9 +575,9 @@ export class ElementInjector extends TreeNode { if (isPresent(p._keyId9)) this._getDirectiveByKeyId(p._keyId9); } - dynamicallyCreateComponent(componentType:Type, annotation:Directive, injector:Injector) { + dynamicallyCreateComponent(directiveBinding, injector:Injector) { this._shadowDomAppInjector = injector; - this._dynamicallyCreatedComponentBinding = DirectiveBinding.createFromType(componentType, annotation); + this._dynamicallyCreatedComponentBinding = directiveBinding; this._dynamicallyCreatedComponent = this._new(this._dynamicallyCreatedComponentBinding); return this._dynamicallyCreatedComponent; } @@ -708,7 +708,7 @@ export class ElementInjector extends TreeNode { _buildPropSetter(dep) { var view = this._getPreBuiltObjectByKeyId(StaticKeys.instance().viewId); - var renderer = view.proto.renderer; + var renderer = view.renderer; var index = this._proto.index; return function(v) { renderer.setElementProperty(view.render, index, dep.propSetterName, v); diff --git a/modules/angular2/src/core/compiler/proto_view_factory.js b/modules/angular2/src/core/compiler/proto_view_factory.js index a0141e2fb6..37be908cbd 100644 --- a/modules/angular2/src/core/compiler/proto_view_factory.js +++ b/modules/angular2/src/core/compiler/proto_view_factory.js @@ -13,11 +13,9 @@ import {ProtoElementInjector, DirectiveBinding} from './element_injector'; @Injectable() export class ProtoViewFactory { _changeDetection:ChangeDetection; - _renderer:renderApi.Renderer; - constructor(changeDetection:ChangeDetection, renderer:renderApi.Renderer) { + constructor(changeDetection:ChangeDetection) { this._changeDetection = changeDetection; - this._renderer = renderer; } createProtoView(componentBinding:DirectiveBinding, renderProtoView: renderApi.ProtoViewDto, directives:List):AppProtoView { @@ -30,7 +28,7 @@ export class ProtoViewFactory { 'dummy', componentAnnotation.changeDetection ); } - var protoView = new AppProtoView(this._renderer, renderProtoView.render, protoChangeDetector); + var protoView = new AppProtoView(renderProtoView.render, protoChangeDetector); for (var i=0; i; @@ -27,6 +27,7 @@ export class AppView { viewContainers: List; preBuiltObjects: List; proto: AppProtoView; + renderer: renderApi.Renderer; /** * The context against which data-binding expressions in this view are evaluated against. @@ -42,7 +43,7 @@ export class AppView { */ locals:Locals; - constructor(proto:AppProtoView, protoLocals:Map) { + constructor(renderer:renderApi.Renderer, proto:AppProtoView, protoLocals:Map) { this.render = null; this.proto = proto; this.changeDetector = null; @@ -53,6 +54,7 @@ export class AppView { this.preBuiltObjects = null; this.context = null; this.locals = new Locals(null, MapWrapper.clone(protoLocals)); //TODO optimize this + this.renderer = renderer; } init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List, @@ -78,157 +80,6 @@ export class AppView { return isPresent(this.context); } - _setContextAndLocals(newContext, locals) { - this.context = newContext; - this.locals.parent = locals; - } - - _hydrateChangeDetector() { - this.changeDetector.hydrate(this.context, this.locals, this); - } - - _dehydrateContext() { - if (isPresent(this.locals)) { - this.locals.clearValues(); - } - this.context = null; - this.changeDetector.dehydrate(); - } - - /** - * A dehydrated view is a state of the view that allows it to be moved around - * the view tree, without incurring the cost of recreating the underlying - * injectors and watch records. - * - * A dehydrated view has the following properties: - * - * - all element injectors are empty. - * - all appInjectors are released. - * - all viewcontainers are empty. - * - all context locals are set to null. - * - the view context is null. - * - * A call to hydrate/dehydrate does not attach/detach the view from the view - * tree. - */ - hydrate(appInjector: Injector, hostElementInjector: ElementInjector, - context: Object, locals:Locals) { - var renderComponentViewRefs = this.proto.renderer.createView(this.proto.render); - this.internalHydrateRecurse(renderComponentViewRefs, 0, appInjector, hostElementInjector, context, locals); - } - - dehydrate() { - var render = this.render; - this.internalDehydrateRecurse(); - this.proto.renderer.destroyView(render); - } - - internalHydrateRecurse( - renderComponentViewRefs:List, - renderComponentIndex:number, - appInjector: Injector, hostElementInjector: ElementInjector, - context: Object, locals:Locals):number { - if (this.hydrated()) throw new BaseException('The view is already hydrated.'); - - this.render = renderComponentViewRefs[renderComponentIndex++]; - - this._setContextAndLocals(context, locals); - - // viewContainers - for (var i = 0; i < this.viewContainers.length; i++) { - var vc = this.viewContainers[i]; - if (isPresent(vc)) { - vc.internalHydrateRecurse(new renderApi.ViewContainerRef(this.render, i), appInjector, hostElementInjector); - } - } - - var binders = this.proto.elementBinders; - for (var i = 0; i < binders.length; ++i) { - var componentDirective = binders[i].componentDirective; - var shadowDomAppInjector = null; - - // shadowDomAppInjector - if (isPresent(componentDirective)) { - var injectables = componentDirective.resolvedInjectables; - if (isPresent(injectables)) - shadowDomAppInjector = appInjector.createChildFromResolved(injectables); - else { - shadowDomAppInjector = appInjector; - } - } else { - shadowDomAppInjector = null; - } - - // elementInjectors - var elementInjector = this.elementInjectors[i]; - if (isPresent(elementInjector)) { - elementInjector.instantiateDirectives(appInjector, hostElementInjector, shadowDomAppInjector, this.preBuiltObjects[i]); - - // The exporting of $implicit is a special case. Since multiple elements will all export - // the different values as $implicit, directly assign $implicit bindings to the variable - // name. - var exportImplicitName = elementInjector.getExportImplicitName(); - if (elementInjector.isExportingComponent()) { - this.locals.set(exportImplicitName, elementInjector.getComponent()); - } else if (elementInjector.isExportingElement()) { - this.locals.set(exportImplicitName, elementInjector.getNgElement()); - } - } - - if (binders[i].hasStaticComponent()) { - renderComponentIndex = this.componentChildViews[i].internalHydrateRecurse( - renderComponentViewRefs, - renderComponentIndex, - shadowDomAppInjector, - elementInjector, - elementInjector.getComponent(), - null - ); - } - } - this._hydrateChangeDetector(); - this.proto.renderer.setEventDispatcher(this.render, this); - return renderComponentIndex; - } - - internalDehydrateRecurse() { - // Note: preserve the opposite order of the hydration process. - - // componentChildViews - for (var i = 0; i < this.componentChildViews.length; i++) { - 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 - for (var i = 0; i < this.elementInjectors.length; i++) { - if (isPresent(this.elementInjectors[i])) { - this.elementInjectors[i].clearDirectives(); - } - } - - // viewContainers - if (isPresent(this.viewContainers)) { - for (var i = 0; i < this.viewContainers.length; i++) { - var vc = this.viewContainers[i]; - if (isPresent(vc)) { - vc.internalDehydrateRecurse(); - } - } - } - - this.render = null; - - this._dehydrateContext(); - } - /** * Triggers the event handlers for the element and the directives. * @@ -247,12 +98,12 @@ export class AppView { // dispatch to element injector or text nodes based on context notifyOnBinding(b:BindingRecord, currentValue:any) { if (b.isElement()) { - this.proto.renderer.setElementProperty( + this.renderer.setElementProperty( this.render, b.elementIndex, b.propertyName, currentValue ); } else { // we know it refers to _textNodes. - this.proto.renderer.setText(this.render, b.elementIndex, currentValue); + this.renderer.setText(this.render, b.elementIndex, currentValue); } } @@ -266,18 +117,6 @@ export class AppView { return elementInjector.getChangeDetector(); } - 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 dispatchEvent( elementIndex:number, eventName:string, locals:Map @@ -319,13 +158,10 @@ export class AppProtoView { _directiveRecordsMap:Map; _directiveRecords:List; render:renderApi.ProtoViewRef; - renderer:renderApi.Renderer; constructor( - renderer:renderApi.Renderer, render:renderApi.ProtoViewRef, protoChangeDetector:ProtoChangeDetector) { - this.renderer = renderer; this.render = render; this.elementBinders = []; this.variableBindings = MapWrapper.create(); diff --git a/modules/angular2/src/core/compiler/view_container.js b/modules/angular2/src/core/compiler/view_container.js index 61afb5ad7d..a6d7605f99 100644 --- a/modules/angular2/src/core/compiler/view_container.js +++ b/modules/angular2/src/core/compiler/view_container.js @@ -7,13 +7,18 @@ import {isPresent, isBlank} from 'angular2/src/facade/lang'; import * as renderApi from 'angular2/src/render/api'; import * as viewModule from './view'; import * as vfModule from './view_factory'; +import * as vhModule from './view_hydrator'; +import {Renderer} from 'angular2/src/render/api'; /** * @exportedAs angular2/template */ export class ViewContainer { - render:renderApi.ViewContainerRef; viewFactory: vfModule.ViewFactory; + viewHydrator: vhModule.AppViewHydrator; + renderer: Renderer; + + render:renderApi.ViewContainerRef; parentView: viewModule.AppView; defaultProtoView: viewModule.AppProtoView; _views: List; @@ -22,10 +27,13 @@ export class ViewContainer { hostElementInjector: eiModule.ElementInjector; constructor(viewFactory:vfModule.ViewFactory, + renderer: Renderer, parentView: viewModule.AppView, defaultProtoView: viewModule.AppProtoView, elementInjector: eiModule.ElementInjector) { this.viewFactory = viewFactory; + this.viewHydrator = null; + this.renderer = renderer; this.render = null; this.parentView = parentView; this.defaultProtoView = defaultProtoView; @@ -37,28 +45,10 @@ export class ViewContainer { this.hostElementInjector = null; } - internalHydrateRecurse(render:renderApi.ViewContainerRef, appInjector: Injector, hostElementInjector: eiModule.ElementInjector) { - this.render = render; - this.appInjector = appInjector; - this.hostElementInjector = hostElementInjector; - } - - internalDehydrateRecurse() { - this.appInjector = null; - this.hostElementInjector = null; - this.render = null; - // Note: We don't call clear here, - // as we don't want to change the render side - // (i.e. don't deattach views on the render side), - // as the render side does its own recursion. + internalClearWithoutRender() { for (var i = this._views.length - 1; i >= 0; i--) { - var view = this._views[i]; - view.changeDetector.remove(); - this._unlinkElementInjectors(view); - view.internalDehydrateRecurse(); - this.viewFactory.returnView(view); + this._detachInjectors(i); } - this._views = []; } clear() { @@ -86,31 +76,31 @@ export class ViewContainer { // TODO(rado): profile and decide whether bounds checks should be added // to the methods below. - create(atIndex=-1): viewModule.AppView { + create(atIndex=-1, protoView:viewModule.AppProtoView = null): viewModule.AppView { + if (atIndex == -1) atIndex = this._views.length; if (!this.hydrated()) throw new BaseException( 'Cannot create views on a dehydrated ViewContainer'); - var newView = this.viewFactory.getView(this.defaultProtoView); + if (isBlank(protoView)) { + protoView = this.defaultProtoView; + } + var newView = this.viewFactory.getView(protoView); // insertion must come before hydration so that element injector trees are attached. - this._insertWithoutRender(newView, atIndex); - // hydration must come before changing the render side, - // as it acquires the render views. - newView.hydrate(this.appInjector, this.hostElementInjector, - this.parentView.context, this.parentView.locals); - this.defaultProtoView.renderer.insertViewIntoContainer(this.render, newView.render, atIndex); + this._insertInjectors(newView, atIndex); + this.viewHydrator.hydrateViewInViewContainer(this, atIndex, newView); return newView; } insert(view, atIndex=-1): viewModule.AppView { - this._insertWithoutRender(view, atIndex); - this.defaultProtoView.renderer.insertViewIntoContainer(this.render, view.render, atIndex); + if (atIndex == -1) atIndex = this._views.length; + this._insertInjectors(view, atIndex); + this.parentView.changeDetector.addChild(view.changeDetector); + this.renderer.insertViewIntoContainer(this.render, atIndex, view.render); return view; } - _insertWithoutRender(view, atIndex=-1): viewModule.AppView { - if (atIndex == -1) atIndex = this._views.length; + _insertInjectors(view, atIndex): viewModule.AppView { ListWrapper.insert(this._views, atIndex, view); - this.parentView.changeDetector.addChild(view.changeDetector); this._linkElementInjectors(this._siblingInjectorToLinkAfter(atIndex), view); return view; @@ -118,8 +108,10 @@ export class ViewContainer { remove(atIndex=-1) { if (atIndex == -1) atIndex = this._views.length - 1; - var view = this.detach(atIndex); - view.dehydrate(); + var view = this._views[atIndex]; + // opposite order as in create + this.viewHydrator.dehydrateViewInViewContainer(this, atIndex, view); + this._detachInjectors(atIndex); this.viewFactory.returnView(view); // view is intentionally not returned to the client. } @@ -130,16 +122,17 @@ export class ViewContainer { */ detach(atIndex=-1): viewModule.AppView { if (atIndex == -1) atIndex = this._views.length - 1; - var detachedView = this.get(atIndex); - ListWrapper.removeAt(this._views, atIndex); - this.defaultProtoView.renderer.detachViewFromContainer(this.render, atIndex); + var detachedView = this._detachInjectors(atIndex); detachedView.changeDetector.remove(); - this._unlinkElementInjectors(detachedView); + this.renderer.detachViewFromContainer(this.render, atIndex); return detachedView; } - contentTagContainers() { - return this._views; + _detachInjectors(atIndex): viewModule.AppView { + var detachedView = this.get(atIndex); + ListWrapper.removeAt(this._views, atIndex); + this._unlinkElementInjectors(detachedView); + return detachedView; } _linkElementInjectors(sibling, view) { diff --git a/modules/angular2/src/core/compiler/view_factory.js b/modules/angular2/src/core/compiler/view_factory.js index 3c0b16028a..2707a90625 100644 --- a/modules/angular2/src/core/compiler/view_factory.js +++ b/modules/angular2/src/core/compiler/view_factory.js @@ -5,6 +5,7 @@ import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang'; import {NgElement} from 'angular2/src/core/compiler/ng_element'; import * as vcModule from './view_container'; import * as viewModule from './view'; +import {Renderer} from 'angular2/src/render/api'; // TODO(tbosch): Make this an OpaqueToken as soon as our transpiler supports this! export const VIEW_POOL_CAPACITY = 'ViewFactory.viewPoolCapacity'; @@ -13,10 +14,12 @@ export const VIEW_POOL_CAPACITY = 'ViewFactory.viewPoolCapacity'; export class ViewFactory { _poolCapacityPerProtoView:number; _pooledViewsPerProtoView:Map>; + _renderer:Renderer; - constructor(@Inject(VIEW_POOL_CAPACITY) poolCapacityPerProtoView) { + constructor(@Inject(VIEW_POOL_CAPACITY) poolCapacityPerProtoView, renderer:Renderer) { this._poolCapacityPerProtoView = poolCapacityPerProtoView; this._pooledViewsPerProtoView = MapWrapper.create(); + this._renderer = renderer; } getView(protoView:viewModule.AppProtoView):viewModule.AppView { @@ -47,7 +50,7 @@ export class ViewFactory { } _createView(protoView:viewModule.AppProtoView): viewModule.AppView { - var view = new viewModule.AppView(protoView, protoView.protoLocals); + var view = new viewModule.AppView(this._renderer, protoView, protoView.protoLocals); var changeDetector = protoView.protoChangeDetector.instantiate(view, protoView.bindings, protoView.getVariableBindings(), protoView.getdirectiveRecords()); @@ -81,14 +84,14 @@ export class ViewFactory { var childView = this._createView(binder.nestedProtoView); childChangeDetector = childView.changeDetector; changeDetector.addShadowDomChild(childChangeDetector); - + componentChildViews[binderIdx] = childView; } // viewContainers var viewContainer = null; if (isPresent(binder.viewportDirective)) { - viewContainer = new vcModule.ViewContainer(this, view, binder.nestedProtoView, elementInjector); + viewContainer = new vcModule.ViewContainer(this, this._renderer, view, binder.nestedProtoView, elementInjector); } viewContainers[binderIdx] = viewContainer; diff --git a/modules/angular2/src/core/compiler/view_hydrator.js b/modules/angular2/src/core/compiler/view_hydrator.js new file mode 100644 index 0000000000..86a0c4d787 --- /dev/null +++ b/modules/angular2/src/core/compiler/view_hydrator.js @@ -0,0 +1,284 @@ +import {Injectable, Inject, OpaqueToken, Injector} from 'angular2/di'; +import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection'; +import * as eli from './element_injector'; +import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang'; +import * as vcModule from './view_container'; +import * as viewModule from './view'; +import {BindingPropagationConfig, Locals} from 'angular2/change_detection'; + +import * as renderApi from 'angular2/src/render/api'; + +/** + * A dehydrated view is a state of the view that allows it to be moved around + * the view tree, without incurring the cost of recreating the underlying + * injectors and watch records. + * + * A dehydrated view has the following properties: + * + * - all element injectors are empty. + * - all appInjectors are released. + * - all viewcontainers are empty. + * - all context locals are set to null. + * - the view context is null. + * + * A call to hydrate/dehydrate is called whenever a view is attached/detached, + * but it does not do the attach/detach itself. + */ +@Injectable() +export class AppViewHydrator { + _renderer:renderApi.Renderer; + + constructor(renderer:renderApi.Renderer) { + this._renderer = renderer; + } + + hydrateDynamicComponentView(hostView:viewModule.AppView, boundElementIndex:number, + componentView:viewModule.AppView, componentDirective:eli.DirectiveBinding, injector:Injector) { + var binder = hostView.proto.elementBinders[boundElementIndex]; + if (!binder.hasDynamicComponent()) { + throw new BaseException(`There is no dynamic component directive at element ${boundElementIndex}`); + } + if (isPresent(hostView.componentChildViews[boundElementIndex])) { + throw new BaseException(`There already is a bound component at element ${boundElementIndex}`); + } + var hostElementInjector = hostView.elementInjectors[boundElementIndex]; + if (isBlank(injector)) { + // TODO: We should have another way of accesing the app injector at hostView place. + injector = new eli.ElementRef(hostElementInjector).injector; + } + + // shadowDomAppInjector + var shadowDomAppInjector = this._createShadowDomAppInjector(componentDirective, injector); + // Needed to make rtts-assert happy in unit tests... + if (isBlank(shadowDomAppInjector)) { + shadowDomAppInjector = null; + } + // create component instance + var component = hostElementInjector.dynamicallyCreateComponent(componentDirective, shadowDomAppInjector); + + // componentView + hostView.componentChildViews[boundElementIndex] = componentView; + hostView.changeDetector.addShadowDomChild(componentView.changeDetector); + + // render views + var renderViewRefs = this._renderer.createDynamicComponentView(hostView.render, boundElementIndex, componentView.proto.render); + + this._viewHydrateRecurse( + componentView, renderViewRefs, 0, shadowDomAppInjector, hostElementInjector, component, null + ); + } + + dehydrateDynamicComponentView(parentView:viewModule.AppView, boundElementIndex:number) { + throw new BaseException('Not yet implemented!'); + // Something along these lines: + // var binder = parentView.proto.elementBinders[boundElementIndex]; + // if (!binder.hasDynamicComponent()) { + // throw new BaseException(`There is no dynamic component directive at element ${boundElementIndex}`); + // } + // var componentView = parentView.componentChildViews[boundElementIndex]; + // if (isBlank(componentView)) { + // throw new BaseException(`There is no bound component at element ${boundElementIndex}`); + // } + // this._viewDehydrateRecurse(componentView); + // parentView.changeDetector.removeShadowDomChild(componentView.changeDetector); + // this._renderer.destroyDynamicComponentChildView(parentView.render, boundElementIndex); + // parentView.componentChildViews[boundElementIndex] = null; + } + + hydrateInPlaceHostView(parentView:viewModule.AppView, hostElementSelector, hostView:viewModule.AppView, injector:Injector) { + var parentRenderViewRef = null; + if (isPresent(parentView)) { + // Needed for user views + throw new BaseException('Not yet supported'); + } + var binder = hostView.proto.elementBinders[0]; + var shadowDomAppInjector = this._createShadowDomAppInjector(binder.componentDirective, injector); + + // render views + var renderViewRefs = this._renderer.createInPlaceHostView(parentRenderViewRef, hostElementSelector, hostView.proto.render); + + this._viewHydrateRecurse( + hostView, renderViewRefs, 0, shadowDomAppInjector, null, new Object(), null + ); + } + + dehydrateInPlaceHostView(parentView:viewModule.AppView, hostView:viewModule.AppView) { + var parentRenderViewRef = null; + if (isPresent(parentView)) { + // Needed for user views + throw new BaseException('Not yet supported'); + } + var render = hostView.render; + this._viewDehydrateRecurse(hostView); + this._renderer.destroyInPlaceHostView(parentRenderViewRef, render); + } + + hydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, atIndex:number, view:viewModule.AppView) { + if (!viewContainer.hydrated()) throw new BaseException( + 'Cannot create views on a dehydrated ViewContainer'); + var renderViewRefs = this._renderer.createViewInContainer(viewContainer.render, atIndex, view.proto.render); + viewContainer.parentView.changeDetector.addChild(view.changeDetector); + this._viewHydrateRecurse(view, renderViewRefs, 0, viewContainer.appInjector, viewContainer.hostElementInjector, + viewContainer.parentView.context, viewContainer.parentView.locals); + } + + dehydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, atIndex:number, view:viewModule.AppView) { + view.changeDetector.remove(); + this._viewDehydrateRecurse(view); + this._renderer.destroyViewInContainer(viewContainer.render, atIndex); + } + + _viewHydrateRecurse( + view:viewModule.AppView, + renderComponentViewRefs:List, + renderComponentIndex:number, + appInjector: Injector, hostElementInjector: eli.ElementInjector, + context: Object, locals:Locals):number { + if (view.hydrated()) throw new BaseException('The view is already hydrated.'); + + view.render = renderComponentViewRefs[renderComponentIndex++]; + + view.context = context; + view.locals.parent = locals; + + // viewContainers + for (var i = 0; i < view.viewContainers.length; i++) { + var vc = view.viewContainers[i]; + if (isPresent(vc)) { + this._viewContainerHydrateRecurse(vc, new renderApi.ViewContainerRef(view.render, i), appInjector, hostElementInjector); + } + } + + var binders = view.proto.elementBinders; + for (var i = 0; i < binders.length; ++i) { + var componentDirective = binders[i].componentDirective; + var shadowDomAppInjector = null; + + // shadowDomAppInjector + if (isPresent(componentDirective)) { + shadowDomAppInjector = this._createShadowDomAppInjector(componentDirective, appInjector); + } else { + shadowDomAppInjector = null; + } + + // elementInjectors + var elementInjector = view.elementInjectors[i]; + if (isPresent(elementInjector)) { + elementInjector.instantiateDirectives(appInjector, hostElementInjector, shadowDomAppInjector, view.preBuiltObjects[i]); + + // The exporting of $implicit is a special case. Since multiple elements will all export + // the different values as $implicit, directly assign $implicit bindings to the variable + // name. + var exportImplicitName = elementInjector.getExportImplicitName(); + if (elementInjector.isExportingComponent()) { + view.locals.set(exportImplicitName, elementInjector.getComponent()); + } else if (elementInjector.isExportingElement()) { + view.locals.set(exportImplicitName, elementInjector.getNgElement()); + } + } + + if (binders[i].hasStaticComponent()) { + renderComponentIndex = this._viewHydrateRecurse( + view.componentChildViews[i], + renderComponentViewRefs, + renderComponentIndex, + shadowDomAppInjector, + elementInjector, + elementInjector.getComponent(), + null + ); + } + } + view.changeDetector.hydrate(view.context, view.locals, view); + view.renderer.setEventDispatcher(view.render, view); + return renderComponentIndex; + } + + /** + * This should only be called by View or ViewContainer. + */ + _viewDehydrateRecurse(view:viewModule.AppView) { + // Note: preserve the opposite order of the hydration process. + + // componentChildViews + for (var i = 0; i < view.componentChildViews.length; i++) { + var componentView = view.componentChildViews[i]; + if (isPresent(componentView)) { + this._viewDehydrateRecurse(componentView); + var binder = view.proto.elementBinders[i]; + if (binder.hasDynamicComponent()) { + view.componentChildViews[i] = null; + view.changeDetector.removeShadowDomChild(componentView.changeDetector); + } + } + } + + // elementInjectors + for (var i = 0; i < view.elementInjectors.length; i++) { + if (isPresent(view.elementInjectors[i])) { + view.elementInjectors[i].clearDirectives(); + } + } + + // viewContainers + if (isPresent(view.viewContainers)) { + for (var i = 0; i < view.viewContainers.length; i++) { + var vc = view.viewContainers[i]; + if (isPresent(vc)) { + this._viewContainerDehydrateRecurse(vc); + } + } + } + + view.render = null; + + if (isPresent(view.locals)) { + view.locals.clearValues(); + } + view.context = null; + view.changeDetector.dehydrate(); + } + + _createShadowDomAppInjector(componentDirective, appInjector) { + var shadowDomAppInjector = null; + + // shadowDomAppInjector + var injectables = componentDirective.resolvedInjectables; + if (isPresent(injectables)) { + shadowDomAppInjector = appInjector.createChildFromResolved(injectables); + } else { + shadowDomAppInjector = appInjector; + } + return shadowDomAppInjector; + } + + /** + * This should only be called by View or ViewContainer. + */ + _viewContainerHydrateRecurse(viewContainer:vcModule.ViewContainer, render:renderApi.ViewContainerRef, appInjector: Injector, hostElementInjector: eli.ElementInjector) { + viewContainer.viewHydrator = this; + viewContainer.render = render; + viewContainer.appInjector = appInjector; + viewContainer.hostElementInjector = hostElementInjector; + } + + /** + * This should only be called by View or ViewContainer. + */ + _viewContainerDehydrateRecurse(viewContainer:vcModule.ViewContainer) { + for (var i=0; i; variableBindings: Map; + type: number; - constructor({render, elementBinders, variableBindings}={}) { + constructor({render, elementBinders, variableBindings, type}={}) { this.render = render; this.elementBinders = elementBinders; this.variableBindings = variableBindings; + this.type = type; } } @@ -143,6 +160,11 @@ export class ViewDefinition { } export class Renderer { + /** + * Creats a ProtoViewDto that contains a single nested component with the given componentId. + */ + createHostProtoView(componentId):Promise { return null; } + /** * Compiles a single RenderProtoView. Non recursive so that * we don't need to serialize all possible components over the wire, @@ -160,35 +182,62 @@ export class Renderer { mergeChildComponentProtoViews(protoViewRef:ProtoViewRef, componentProtoViewRefs:List) { return null; } /** - * Creats a RenderProtoView that will create a root view for the given element, - * i.e. it will not clone the element but only attach other proto views to it. - * Contains a single nested component with the given componentId. + * Creates a view and inserts it into a ViewContainer. + * @param {ViewContainerRef} viewContainerRef + * @param {ProtoViewRef} protoViewRef A ProtoViewRef of type ProtoViewDto.HOST_VIEW_TYPE or ProtoViewDto.EMBEDDED_VIEW_TYPE + * @param {number} atIndex + * @return {List} the view and all of its nested child component views */ - createRootProtoView(selectorOrElement, componentId):Promise { return null; } + createViewInContainer(vcRef:ViewContainerRef, atIndex:number, protoViewRef:ProtoViewRef):List { return null; } /** - * Creates a view and all of its nested child components. - * @return {List} depth first list of nested child components + * Destroys the view in the given ViewContainer */ - createView(protoView:ProtoViewRef):List { return null; } - - /** - * Destroys a view and returns it back into the pool. - */ - destroyView(view:ViewRef):void {} + destroyViewInContainer(vcRef:ViewContainerRef, atIndex:number):void {} /** * Inserts a detached view into a viewContainer. */ - insertViewIntoContainer(vcRef:ViewContainerRef, view:ViewRef, atIndex):void {} + insertViewIntoContainer(vcRef:ViewContainerRef, atIndex:number, view:ViewRef):void {} /** * Detaches a view from a container so that it can be inserted later on - * Note: We are not return the ViewRef as this can't be done in sync, - * so we assume that the caller knows which view is in which spot... */ detachViewFromContainer(vcRef:ViewContainerRef, atIndex:number):void {} + /** + * Creates a view and + * installs it as a shadow view for an element. + * + * Note: only allowed if there is a dynamic component directive at this place. + * @param {ViewRef} hostView + * @param {number} elementIndex + * @param {ProtoViewRef} componentProtoViewRef A ProtoViewRef of type ProtoViewDto.COMPONENT_VIEW_TYPE + * @return {List} the view and all of its nested child component views + */ + createDynamicComponentView(hostViewRef:ViewRef, elementIndex:number, componentProtoViewRef:ProtoViewRef):List { return null; } + + /** + * Destroys the component view at the given index + * + * Note: only allowed if there is a dynamic component directive at this place. + */ + destroyDynamicComponentView(hostViewRef:ViewRef, elementIndex:number):void {} + + /** + * Creates a host view that includes the given element. + * @param {ViewRef} parentViewRef (might be null) + * @param {any} hostElementSelector element or css selector for the host element + * @param {ProtoViewRef} hostProtoView a ProtoViewRef of type ProtoViewDto.HOST_VIEW_TYPE + * @return {List} the view and all of its nested child component views + */ + createInPlaceHostView(parentViewRef:ViewRef, hostElementSelector, hostProtoViewRef:ProtoViewRef):List { return null; } + + /** + * Destroys the given host view in the given parent view. + */ + destroyInPlaceHostView(parentViewRef:ViewRef, hostViewRef:ViewRef):void {} + /** * Sets a property on an element. * Note: This will fail if the property was not mentioned previously as a propertySetter @@ -196,12 +245,6 @@ export class Renderer { */ setElementProperty(view:ViewRef, elementIndex:number, propertyName:string, propertyValue:any):void {} - /** - * Installs a nested component in another view. - * Note: only allowed if there is a dynamic component directive - */ - setDynamicComponentView(view:ViewRef, elementIndex:number, nestedViewRef:ViewRef):void {} - /** * This will set the value for a text node. * Note: This needs to be separate from setElementProperty as we don't have ElementBinders diff --git a/modules/angular2/src/render/dom/direct_dom_renderer.js b/modules/angular2/src/render/dom/direct_dom_renderer.js index 68fed47f34..aea9fa5a68 100644 --- a/modules/angular2/src/render/dom/direct_dom_renderer.js +++ b/modules/angular2/src/render/dom/direct_dom_renderer.js @@ -1,15 +1,17 @@ import {Injectable} from 'angular2/di'; import {Promise, PromiseWrapper} from 'angular2/src/facade/async'; import {List, ListWrapper} from 'angular2/src/facade/collection'; -import {isBlank, isPresent} from 'angular2/src/facade/lang'; +import {isBlank, isPresent, BaseException} from 'angular2/src/facade/lang'; import * as api from '../api'; import {RenderView} from './view/view'; import {RenderProtoView} from './view/proto_view'; import {ViewFactory} from './view/view_factory'; +import {RenderViewHydrator} from './view/view_hydrator'; import {Compiler} from './compiler/compiler'; import {ShadowDomStrategy} from './shadow_dom/shadow_dom_strategy'; import {ProtoViewBuilder} from './view/proto_view_builder'; +import {DOM} from 'angular2/src/dom/dom_adapter'; function _resolveViewContainer(vc:api.ViewContainerRef) { return _resolveView(vc.view).viewContainers[vc.elementIndex]; @@ -65,16 +67,29 @@ export class DirectDomViewRef extends api.ViewRef { export class DirectDomRenderer extends api.Renderer { _compiler: Compiler; _viewFactory: ViewFactory; + _viewHydrator: RenderViewHydrator; _shadowDomStrategy: ShadowDomStrategy; constructor( - compiler: Compiler, viewFactory: ViewFactory, shadowDomStrategy: ShadowDomStrategy) { + compiler: Compiler, viewFactory: ViewFactory, viewHydrator: RenderViewHydrator, shadowDomStrategy: ShadowDomStrategy) { super(); this._compiler = compiler; this._viewFactory = viewFactory; + this._viewHydrator = viewHydrator; this._shadowDomStrategy = shadowDomStrategy; } + createHostProtoView(componentId):Promise { + var rootElement = DOM.createElement('div'); + var hostProtoViewBuilder = new ProtoViewBuilder(rootElement); + var elBinder = hostProtoViewBuilder.bindElement(rootElement, 'root element'); + elBinder.setComponentId(componentId); + elBinder.bindDirective(0); + + this._shadowDomStrategy.processElement(null, componentId, rootElement); + return PromiseWrapper.resolve(hostProtoViewBuilder.build()); + } + compile(template:api.ViewDefinition):Promise { // Note: compiler already uses a DirectDomProtoViewRef, so we don't // need to do anything here @@ -87,29 +102,22 @@ export class DirectDomRenderer extends api.Renderer { ); } - createRootProtoView(selectorOrElement, componentId):Promise { - var element = selectorOrElement; // TODO: select the element if it is not a real element... - var rootProtoViewBuilder = new ProtoViewBuilder(element); - rootProtoViewBuilder.setIsRootView(true); - var elBinder = rootProtoViewBuilder.bindElement(element, 'root element'); - elBinder.setComponentId(componentId); - elBinder.bindDirective(0); - - this._shadowDomStrategy.processElement(null, componentId, element); - return PromiseWrapper.resolve(rootProtoViewBuilder.build()); + createViewInContainer(vcRef:api.ViewContainerRef, atIndex:number, protoViewRef:api.ProtoViewRef):List { + var view = this._viewFactory.getView(_resolveProtoView(protoViewRef)); + var vc = _resolveViewContainer(vcRef); + this._viewHydrator.hydrateViewInViewContainer(vc, view); + vc.insert(view, atIndex); + return _collectComponentChildViewRefs(view); } - createView(protoViewRef:api.ProtoViewRef):List { - return _collectComponentChildViewRefs( - this._viewFactory.getView(_resolveProtoView(protoViewRef)) - ); + destroyViewInContainer(vcRef:api.ViewContainerRef, atIndex:number):void { + var vc = _resolveViewContainer(vcRef); + var view = vc.detach(atIndex); + this._viewHydrator.dehydrateViewInViewContainer(vc, view); + this._viewFactory.returnView(view); } - destroyView(viewRef:api.ViewRef) { - this._viewFactory.returnView(_resolveView(viewRef)); - } - - insertViewIntoContainer(vcRef:api.ViewContainerRef, viewRef:api.ViewRef, atIndex=-1):void { + insertViewIntoContainer(vcRef:api.ViewContainerRef, atIndex=-1, viewRef:api.ViewRef):void { _resolveViewContainer(vcRef).insert(_resolveView(viewRef), atIndex); } @@ -117,16 +125,39 @@ export class DirectDomRenderer extends api.Renderer { _resolveViewContainer(vcRef).detach(atIndex); } - setElementProperty(viewRef:api.ViewRef, elementIndex:number, propertyName:string, propertyValue:any):void { - _resolveView(viewRef).setElementProperty(elementIndex, propertyName, propertyValue); + createDynamicComponentView(hostViewRef:api.ViewRef, elementIndex:number, componentViewRef:api.ProtoViewRef):List { + var hostView = _resolveView(hostViewRef); + var componentView = this._viewFactory.getView(_resolveProtoView(componentViewRef)); + this._viewHydrator.hydrateDynamicComponentView(hostView, elementIndex, componentView); + return _collectComponentChildViewRefs(componentView); } - setDynamicComponentView(viewRef:api.ViewRef, elementIndex:number, nestedViewRef:api.ViewRef):void { - _resolveView(viewRef).setComponentView( - this._shadowDomStrategy, - elementIndex, - _resolveView(nestedViewRef) - ); + destroyDynamicComponentView(hostViewRef:api.ViewRef, elementIndex:number):void { + throw new BaseException('Not supported yet'); + // Something along these lines: + // var hostView = _resolveView(hostViewRef); + // var componentView = hostView.childComponentViews[elementIndex]; + // this._viewHydrator.dehydrateDynamicComponentView(hostView, componentView); + } + + createInPlaceHostView(parentViewRef:api.ViewRef, hostElementSelector, hostProtoViewRef:api.ProtoViewRef):List { + var parentView = _resolveView(parentViewRef); + var hostView = this._viewFactory.createInPlaceHostView(hostElementSelector, _resolveProtoView(hostProtoViewRef)); + this._viewHydrator.hydrateInPlaceHostView(parentView, hostView); + return _collectComponentChildViewRefs(hostView); + } + + /** + * Destroys the given host view in the given parent view. + */ + destroyInPlaceHostView(parentViewRef:api.ViewRef, hostViewRef:api.ViewRef):void { + var parentView = _resolveView(parentViewRef); + var hostView = _resolveView(hostViewRef); + this._viewHydrator.dehydrateInPlaceHostView(parentView, hostView); + } + + setElementProperty(viewRef:api.ViewRef, elementIndex:number, propertyName:string, propertyValue:any):void { + _resolveView(viewRef).setElementProperty(elementIndex, propertyName, propertyValue); } setText(viewRef:api.ViewRef, textNodeIndex:number, text:string):void { diff --git a/modules/angular2/src/render/dom/view/proto_view.js b/modules/angular2/src/render/dom/view/proto_view.js index 9120a0daa2..714734a39c 100644 --- a/modules/angular2/src/render/dom/view/proto_view.js +++ b/modules/angular2/src/render/dom/view/proto_view.js @@ -10,18 +10,15 @@ export class RenderProtoView { element; elementBinders:List; isTemplateElement:boolean; - isRootView:boolean; rootBindingOffset:int; constructor({ elementBinders, - element, - isRootView + element }) { this.element = element; this.elementBinders = elementBinders; this.isTemplateElement = DOM.isTemplateElement(this.element); - this.isRootView = isRootView; this.rootBindingOffset = (isPresent(this.element) && DOM.hasClass(this.element, NG_BINDING_CLASS)) ? 1 : 0; } diff --git a/modules/angular2/src/render/dom/view/proto_view_builder.js b/modules/angular2/src/render/dom/view/proto_view_builder.js index 28a194097e..7ac7301965 100644 --- a/modules/angular2/src/render/dom/view/proto_view_builder.js +++ b/modules/angular2/src/render/dom/view/proto_view_builder.js @@ -25,7 +25,6 @@ export class ProtoViewBuilder { constructor(rootElement) { this.rootElement = rootElement; this.elements = []; - this.isRootView = false; this.variableBindings = MapWrapper.create(); } @@ -46,10 +45,6 @@ export class ProtoViewBuilder { MapWrapper.set(this.variableBindings, value, name); } - setIsRootView(value) { - this.isRootView = value; - } - build():api.ProtoViewDto { var renderElementBinders = []; @@ -95,8 +90,7 @@ export class ProtoViewBuilder { return new api.ProtoViewDto({ render: new directDomRenderer.DirectDomProtoViewRef(new RenderProtoView({ element: this.rootElement, - elementBinders: renderElementBinders, - isRootView: this.isRootView + elementBinders: renderElementBinders })), elementBinders: apiElementBinders, variableBindings: this.variableBindings @@ -257,9 +251,9 @@ export class EventBuilder extends AstTransformer { var result = new api.EventBinding(fullName, new ASTWithSource(adjustedAst, source.source, '')); var event = new Event(name, target, fullName); if (isBlank(target)) { - ListWrapper.push(this.localEvents, event); + ListWrapper.push(this.localEvents, event); } else { - ListWrapper.push(this.globalEvents, event); + ListWrapper.push(this.globalEvents, event); } return result; } diff --git a/modules/angular2/src/render/dom/view/view.js b/modules/angular2/src/render/dom/view/view.js index b30c423083..4b301568ad 100644 --- a/modules/angular2/src/render/dom/view/view.js +++ b/modules/angular2/src/render/dom/view/view.js @@ -6,9 +6,6 @@ import {ViewContainer} from './view_container'; import {RenderProtoView} from './proto_view'; import {LightDom} from '../shadow_dom/light_dom'; import {Content} from '../shadow_dom/content_tag'; -import {EventManager} from 'angular2/src/render/dom/events/event_manager'; - -import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy'; // import {EventDispatcher} from '../../api'; @@ -30,14 +27,13 @@ export class RenderView { contentTags: List; lightDoms: List; proto: RenderProtoView; - eventManager: EventManager; - _hydrated: boolean; + hydrated: boolean; _eventDispatcher: any/*EventDispatcher*/; - _eventHandlerRemovers: List; + eventHandlerRemovers: List; constructor( proto:RenderProtoView, rootNodes:List, - boundTextNodes: List, boundElements:List, viewContainers:List, contentTags:List, eventManager: EventManager) { + boundTextNodes: List, boundElements:List, viewContainers:List, contentTags:List) { this.proto = proto; this.rootNodes = rootNodes; this.boundTextNodes = boundTextNodes; @@ -45,15 +41,10 @@ export class RenderView { this.viewContainers = viewContainers; this.contentTags = contentTags; this.lightDoms = ListWrapper.createFixedSize(boundElements.length); - this.eventManager = eventManager; ListWrapper.fill(this.lightDoms, null); this.componentChildViews = ListWrapper.createFixedSize(boundElements.length); - this._hydrated = false; - this._eventHandlerRemovers = null; - } - - hydrated() { - return this._hydrated; + this.hydrated = false; + this.eventHandlerRemovers = null; } setElementProperty(elementIndex:number, propertyName:string, value:any) { @@ -65,138 +56,10 @@ export class RenderView { DOM.setText(this.boundTextNodes[textIndex], value); } - setComponentView(strategy: ShadowDomStrategy, - elementIndex:number, childView:RenderView) { - var element = this.boundElements[elementIndex]; - var lightDom = strategy.constructLightDom(this, childView, element); - strategy.attachTemplate(element, childView); - this.lightDoms[elementIndex] = lightDom; - this.componentChildViews[elementIndex] = childView; - if (this._hydrated) { - childView.hydrate(lightDom); - if (isPresent(lightDom)) { - lightDom.redistribute(); - } - } - } - getViewContainer(index:number):ViewContainer { return this.viewContainers[index]; } - _getDestLightDom(binderIndex) { - var binder = this.proto.elementBinders[binderIndex]; - var destLightDom = null; - if (binder.parentIndex !== -1 && binder.distanceToParent === 1) { - destLightDom = this.lightDoms[binder.parentIndex]; - } - return destLightDom; - } - - /** - * A dehydrated view is a state of the view that allows it to be moved around - * the view tree. - * - * A dehydrated view has the following properties: - * - * - all viewcontainers are empty. - * - * A call to hydrate/dehydrate does not attach/detach the view from the view - * tree. - */ - hydrate(hostLightDom: LightDom) { - if (this._hydrated) throw new BaseException('The view is already hydrated.'); - this._hydrated = true; - - // viewContainers and content tags - for (var i = 0; i < this.viewContainers.length; i++) { - var vc = this.viewContainers[i]; - var destLightDom = this._getDestLightDom(i); - if (isPresent(vc)) { - vc.hydrate(destLightDom, hostLightDom); - } - var ct = this.contentTags[i]; - if (isPresent(ct)) { - ct.hydrate(destLightDom); - } - } - - // componentChildViews - for (var i = 0; i < this.componentChildViews.length; i++) { - var cv = this.componentChildViews[i]; - if (isPresent(cv)) { - cv.hydrate(this.lightDoms[i]); - } - } - - for (var i = 0; i < this.lightDoms.length; ++i) { - var lightDom = this.lightDoms[i]; - if (isPresent(lightDom)) { - lightDom.redistribute(); - } - } - - //add global events - this._eventHandlerRemovers = ListWrapper.create(); - var binders = this.proto.elementBinders; - for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) { - var binder = binders[binderIdx]; - if (isPresent(binder.globalEvents)) { - for (var i = 0; i < binder.globalEvents.length; i++) { - var globalEvent = binder.globalEvents[i]; - var remover = this._createGlobalEventListener(binderIdx, globalEvent.name, globalEvent.target, globalEvent.fullName); - ListWrapper.push(this._eventHandlerRemovers, remover); - } - } - } - } - - _createGlobalEventListener(elementIndex, eventName, eventTarget, fullName): Function { - return this.eventManager.addGlobalEventListener(eventTarget, eventName, (event) => { - this.dispatchEvent(elementIndex, fullName, event); - }); - } - - dehydrate() { - // Note: preserve the opposite order of the hydration process. - - // componentChildViews - for (var i = 0; i < this.componentChildViews.length; i++) { - var cv = this.componentChildViews[i]; - if (isPresent(cv)) { - cv.dehydrate(); - if (this.proto.elementBinders[i].hasDynamicComponent()) { - ViewContainer.removeViewNodes(cv); - this.lightDoms[i] = null; - this.componentChildViews[i] = null; - } - } - } - - // viewContainers and content tags - if (isPresent(this.viewContainers)) { - for (var i = 0; i < this.viewContainers.length; i++) { - var vc = this.viewContainers[i]; - if (isPresent(vc)) { - vc.dehydrate(); - } - var ct = this.contentTags[i]; - if (isPresent(ct)) { - ct.dehydrate(); - } - } - } - - //remove global events - for (var i = 0; i < this._eventHandlerRemovers.length; i++) { - this._eventHandlerRemovers[i](); - } - - this._eventHandlerRemovers = null; - this._eventDispatcher = null; - this._hydrated = false; - } - setEventDispatcher(dispatcher:any/*EventDispatcher*/) { this._eventDispatcher = dispatcher; } diff --git a/modules/angular2/src/render/dom/view/view_container.js b/modules/angular2/src/render/dom/view/view_container.js index 62b25325a0..d80d587a25 100644 --- a/modules/angular2/src/render/dom/view/view_container.js +++ b/modules/angular2/src/render/dom/view/view_container.js @@ -4,88 +4,64 @@ import {DOM} from 'angular2/src/dom/dom_adapter'; import * as viewModule from './view'; import * as ldModule from '../shadow_dom/light_dom'; -import * as vfModule from './view_factory'; export class ViewContainer { - _viewFactory: vfModule.ViewFactory; templateElement; - _views: List; - _lightDom: ldModule.LightDom; - _hostLightDom: ldModule.LightDom; - _hydrated: boolean; + views: List; + lightDom: ldModule.LightDom; + hostLightDom: ldModule.LightDom; + hydrated: boolean; - constructor(viewFactory: vfModule.ViewFactory, - templateElement) { - this._viewFactory = viewFactory; + constructor(templateElement) { this.templateElement = templateElement; // The order in this list matches the DOM order. - this._views = []; - this._hostLightDom = null; - this._hydrated = false; - } - - hydrate(destLightDom: ldModule.LightDom, hostLightDom: ldModule.LightDom) { - this._hydrated = true; - this._hostLightDom = hostLightDom; - this._lightDom = destLightDom; - } - - dehydrate() { - if (isBlank(this._lightDom)) { - for (var i = this._views.length - 1; i >= 0; i--) { - var view = this._views[i]; - ViewContainer.removeViewNodes(view); - this._viewFactory.returnView(view); - } - this._views = []; - } else { - for (var i=0; i=0; i--) { + this.detach(i); } - if (atIndex == -1) atIndex = this._views.length; - ListWrapper.insert(this._views, atIndex, view); + if (isPresent(this.lightDom)) { + this.lightDom.redistribute(); + } + } - if (isBlank(this._lightDom)) { + insert(view, atIndex=-1): viewModule.RenderView { + this._checkHydrated(); + if (atIndex == -1) atIndex = this.views.length; + ListWrapper.insert(this.views, atIndex, view); + + if (isBlank(this.lightDom)) { ViewContainer.moveViewNodesAfterSibling(this._siblingToInsertAfter(atIndex), view); } else { - this._lightDom.redistribute(); + this.lightDom.redistribute(); } // new content tags might have appeared, we need to redistribute. - if (isPresent(this._hostLightDom)) { - this._hostLightDom.redistribute(); + if (isPresent(this.hostLightDom)) { + this.hostLightDom.redistribute(); } return view; } @@ -97,32 +73,31 @@ export class ViewContainer { detach(atIndex:number) { this._checkHydrated(); var detachedView = this.get(atIndex); - ListWrapper.removeAt(this._views, atIndex); - if (isBlank(this._lightDom)) { + ListWrapper.removeAt(this.views, atIndex); + if (isBlank(this.lightDom)) { ViewContainer.removeViewNodes(detachedView); } else { - this._lightDom.redistribute(); + this.lightDom.redistribute(); } // content tags might have disappeared we need to do redistribution. - if (isPresent(this._hostLightDom)) { - this._hostLightDom.redistribute(); + if (isPresent(this.hostLightDom)) { + this.hostLightDom.redistribute(); } return detachedView; } contentTagContainers() { - return this._views; + return this.views; } nodes():List { var r = []; - for (var i = 0; i < this._views.length; ++i) { - r = ListWrapper.concat(r, this._views[i].rootNodes); + for (var i = 0; i < this.views.length; ++i) { + r = ListWrapper.concat(r, this.views[i].rootNodes); } return r; } - static moveViewNodesAfterSibling(sibling, view) { for (var i = view.rootNodes.length - 1; i >= 0; --i) { DOM.insertAfter(sibling, view.rootNodes[i]); diff --git a/modules/angular2/src/render/dom/view/view_factory.js b/modules/angular2/src/render/dom/view/view_factory.js index 77910dfb21..9ac2494555 100644 --- a/modules/angular2/src/render/dom/view/view_factory.js +++ b/modules/angular2/src/render/dom/view/view_factory.js @@ -31,6 +31,10 @@ export class ViewFactory { this._shadowDomStrategy = shadowDomStrategy; } + createInPlaceHostView(hostElementSelector, hostProtoView:pvModule.RenderProtoView):viewModule.RenderView { + return this._createView(hostProtoView, hostElementSelector); + } + getView(protoView:pvModule.RenderProtoView):viewModule.RenderView { var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView); if (isPresent(pooledViews)) { @@ -40,12 +44,12 @@ export class ViewFactory { } return result; } - return this._createView(protoView); + return this._createView(protoView, null); } returnView(view:viewModule.RenderView) { - if (view.hydrated()) { - view.dehydrate(); + if (view.hydrated) { + throw new BaseException('View is still hydrated'); } var protoView = view.proto; var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView); @@ -58,8 +62,8 @@ export class ViewFactory { } } - _createView(protoView:pvModule.RenderProtoView): viewModule.RenderView { - var rootElementClone = protoView.isRootView ? protoView.element : DOM.importIntoDoc(protoView.element); + _createView(protoView:pvModule.RenderProtoView, inplaceElement): viewModule.RenderView { + var rootElementClone = isPresent(inplaceElement) ? inplaceElement : DOM.importIntoDoc(protoView.element); var elementsWithBindingsDynamic; if (protoView.isTemplateElement) { elementsWithBindingsDynamic = DOM.querySelectorAll(DOM.content(rootElementClone), NG_BINDING_CLASS_SELECTOR); @@ -110,7 +114,7 @@ export class ViewFactory { // viewContainers var viewContainer = null; if (isBlank(binder.componentId) && isPresent(binder.nestedProtoView)) { - viewContainer = new vcModule.ViewContainer(this, element); + viewContainer = new vcModule.ViewContainer(element); } viewContainers[binderIdx] = viewContainer; @@ -124,7 +128,7 @@ export class ViewFactory { var view = new viewModule.RenderView( protoView, viewRootNodes, - boundTextNodes, boundElements, viewContainers, contentTags, this._eventManager + boundTextNodes, boundElements, viewContainers, contentTags ); for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) { @@ -133,8 +137,8 @@ export class ViewFactory { // static child components if (binder.hasStaticComponent()) { - var childView = this._createView(binder.nestedProtoView); - view.setComponentView(this._shadowDomStrategy, binderIdx, childView); + var childView = this._createView(binder.nestedProtoView, null); + this.setComponentView(view, binderIdx, childView); } // events @@ -145,10 +149,6 @@ export class ViewFactory { } } - if (protoView.isRootView) { - view.hydrate(null); - } - return view; } @@ -157,4 +157,15 @@ export class ViewFactory { view.dispatchEvent(elementIndex, eventName, event); }); } + + // This method is used by the ViewFactory and the ViewHydrator + // TODO(tbosch): change shadow dom emulation so that LightDom + // instances don't need to be recreated by instead hydrated/dehydrated + setComponentView(hostView:viewModule.RenderView, elementIndex:number, componentView:viewModule.RenderView) { + var element = hostView.boundElements[elementIndex]; + var lightDom = this._shadowDomStrategy.constructLightDom(hostView, componentView, element); + this._shadowDomStrategy.attachTemplate(element, componentView); + hostView.lightDoms[elementIndex] = lightDom; + hostView.componentChildViews[elementIndex] = componentView; + } } diff --git a/modules/angular2/src/render/dom/view/view_hydrator.js b/modules/angular2/src/render/dom/view/view_hydrator.js new file mode 100644 index 0000000000..2d81a2123e --- /dev/null +++ b/modules/angular2/src/render/dom/view/view_hydrator.js @@ -0,0 +1,192 @@ +import {Injectable} from 'angular2/di'; +import {int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang'; +import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection'; + +import * as ldModule from '../shadow_dom/light_dom'; +import {EventManager} from '../events/event_manager'; +import {ViewFactory} from './view_factory'; +import * as vcModule from './view_container'; +import * as viewModule from './view'; + +/** + * A dehydrated view is a state of the view that allows it to be moved around + * the view tree. + * + * A dehydrated view has the following properties: + * + * - all viewcontainers are empty. + * + * A call to hydrate/dehydrate is called whenever a view is attached/detached, + * but it does not do the attach/detach itself. + */ +@Injectable() +export class RenderViewHydrator { + _eventManager:EventManager; + _viewFactory:ViewFactory; + + constructor(eventManager:EventManager, viewFactory:ViewFactory) { + this._eventManager = eventManager; + this._viewFactory = viewFactory; + } + + hydrateDynamicComponentView(hostView:viewModule.RenderView, boundElementIndex:number, componentView:viewModule.RenderView) { + this._viewFactory.setComponentView(hostView, boundElementIndex, componentView); + var lightDom = hostView.lightDoms[boundElementIndex]; + this._viewHydrateRecurse(componentView, lightDom); + if (isPresent(lightDom)) { + lightDom.redistribute(); + } + } + + dehydrateDynamicComponentView(parentView:viewModule.RenderView, boundElementIndex:number) { + throw new BaseException('Not supported yet'); + // Something along these lines: + // var componentView = parentView.componentChildViews[boundElementIndex]; + // vcModule.ViewContainer.removeViewNodes(componentView); + // parentView.componentChildViews[boundElementIndex] = null; + // parentView.lightDoms[boundElementIndex] = null; + // this._viewDehydrateRecurse(componentView); + } + + hydrateInPlaceHostView(parentView:viewModule.RenderView, hostView:viewModule.RenderView) { + if (isPresent(parentView)) { + throw new BaseException('Not supported yet'); + } + this._viewHydrateRecurse(hostView, null); + } + + dehydrateInPlaceHostView(parentView:viewModule.RenderView, hostView:viewModule.RenderView) { + if (isPresent(parentView)) { + throw new BaseException('Not supported yet'); + } + this._viewDehydrateRecurse(hostView); + } + + hydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, view:viewModule.RenderView) { + this._viewHydrateRecurse(view, viewContainer.hostLightDom); + } + + dehydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, view:viewModule.RenderView) { + this._viewDehydrateRecurse(view); + } + + _getViewDestLightDom(view, binderIndex) { + var binder = view.proto.elementBinders[binderIndex]; + var destLightDom = null; + if (binder.parentIndex !== -1 && binder.distanceToParent === 1) { + destLightDom = view.lightDoms[binder.parentIndex]; + } + return destLightDom; + } + + _viewHydrateRecurse(view, hostLightDom: ldModule.LightDom) { + if (view.hydrated) throw new BaseException('The view is already hydrated.'); + view.hydrated = true; + + // viewContainers and content tags + for (var i = 0; i < view.viewContainers.length; i++) { + var vc = view.viewContainers[i]; + var destLightDom = this._getViewDestLightDom(view, i); + if (isPresent(vc)) { + this._viewContainerHydrateRecurse(vc, destLightDom, hostLightDom); + } + var ct = view.contentTags[i]; + if (isPresent(ct)) { + ct.hydrate(destLightDom); + } + } + + // componentChildViews + for (var i = 0; i < view.componentChildViews.length; i++) { + var cv = view.componentChildViews[i]; + if (isPresent(cv)) { + this._viewHydrateRecurse(cv, view.lightDoms[i]); + } + } + + for (var i = 0; i < view.lightDoms.length; ++i) { + var lightDom = view.lightDoms[i]; + if (isPresent(lightDom)) { + lightDom.redistribute(); + } + } + + //add global events + view.eventHandlerRemovers = ListWrapper.create(); + var binders = view.proto.elementBinders; + for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) { + var binder = binders[binderIdx]; + if (isPresent(binder.globalEvents)) { + for (var i = 0; i < binder.globalEvents.length; i++) { + var globalEvent = binder.globalEvents[i]; + var remover = this._createGlobalEventListener(view, binderIdx, globalEvent.name, globalEvent.target, globalEvent.fullName); + ListWrapper.push(view.eventHandlerRemovers, remover); + } + } + } + } + + _createGlobalEventListener(view, elementIndex, eventName, eventTarget, fullName): Function { + return this._eventManager.addGlobalEventListener(eventTarget, eventName, (event) => { + view.dispatchEvent(elementIndex, fullName, event); + }); + } + + _viewDehydrateRecurse(view) { + // Note: preserve the opposite order of the hydration process. + + // componentChildViews + for (var i = 0; i < view.componentChildViews.length; i++) { + var cv = view.componentChildViews[i]; + if (isPresent(cv)) { + this._viewDehydrateRecurse(cv); + if (view.proto.elementBinders[i].hasDynamicComponent()) { + vcModule.ViewContainer.removeViewNodes(cv); + view.lightDoms[i] = null; + view.componentChildViews[i] = null; + } + } + } + + // viewContainers and content tags + if (isPresent(view.viewContainers)) { + for (var i = 0; i < view.viewContainers.length; i++) { + var vc = view.viewContainers[i]; + if (isPresent(vc)) { + this._viewContainerDehydrateRecurse(vc); + } + var ct = view.contentTags[i]; + if (isPresent(ct)) { + ct.dehydrate(); + } + } + } + + //remove global events + for (var i = 0; i < view.eventHandlerRemovers.length; i++) { + view.eventHandlerRemovers[i](); + } + + view.eventHandlerRemovers = null; + view.setEventDispatcher(null); + view.hydrated = false; + } + + _viewContainerHydrateRecurse(viewContainer, destLightDom: ldModule.LightDom, hostLightDom: ldModule.LightDom) { + viewContainer.hydrated = true; + viewContainer.hostLightDom = hostLightDom; + viewContainer.lightDom = destLightDom; + } + + _viewContainerDehydrateRecurse(viewContainer) { + for (var i=0; i { + return this._injector.get(Compiler).compileInHost(componentBinding).then((pv) => { var viewFactory = this._injector.get(ViewFactory); - var view = viewFactory.getView(pv); - view.hydrate(this._injector, null, context, null); - return new ViewProxy(view.componentChildViews[0]); + var viewHydrator = this._injector.get(AppViewHydrator); + var hostView = viewFactory.getView(pv); + viewHydrator.hydrateInPlaceHostView(null, rootEl, hostView, this._injector); + + return new ViewProxy(this._injector, hostView.componentChildViews[0]); }); } } @@ -110,9 +113,11 @@ export class TestBed { */ export class ViewProxy { _view: AppView; + _injector: Injector; - constructor(view: AppView) { + constructor(injector: Injector, view: AppView) { this._view = view; + this._injector = injector; } get context(): any { @@ -131,6 +136,11 @@ export class ViewProxy { return queryView(this._view, selector); } + destroy() { + var viewHydrator = this._injector.get(AppViewHydrator); + viewHydrator.dehydrateInPlaceHostView(null, this._view); + } + /** * @returns {AppView} return the underlying [AppView]. * diff --git a/modules/angular2/src/test_lib/test_injector.js b/modules/angular2/src/test_lib/test_injector.js index 467733da1e..8a89d5a069 100644 --- a/modules/angular2/src/test_lib/test_injector.js +++ b/modules/angular2/src/test_lib/test_injector.js @@ -35,11 +35,13 @@ import {List, ListWrapper} from 'angular2/src/facade/collection'; import {FunctionWrapper} from 'angular2/src/facade/lang'; import {ViewFactory, VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_factory'; +import {AppViewHydrator} from 'angular2/src/core/compiler/view_hydrator'; import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; import {Renderer} from 'angular2/src/render/api'; import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer'; import * as rc from 'angular2/src/render/dom/compiler/compiler'; import * as rvf from 'angular2/src/render/dom/view/view_factory'; +import * as rvh from 'angular2/src/render/dom/view/view_hydrator'; /** * Returns the root injector bindings. @@ -79,9 +81,11 @@ function _getAppBindings() { bind(Renderer).toClass(DirectDomRenderer), bind(rc.Compiler).toClass(rc.DefaultCompiler), rvf.ViewFactory, + rvh.RenderViewHydrator, bind(rvf.VIEW_POOL_CAPACITY).toValue(500), ProtoViewFactory, ViewFactory, + AppViewHydrator, bind(VIEW_POOL_CAPACITY).toValue(500), Compiler, CompilerCache, diff --git a/modules/angular2/test/core/compiler/compiler_spec.js b/modules/angular2/test/core/compiler/compiler_spec.js index 1b0b0d9c6b..71d708b762 100644 --- a/modules/angular2/test/core/compiler/compiler_spec.js +++ b/modules/angular2/test/core/compiler/compiler_spec.js @@ -361,7 +361,7 @@ export function main() { }); })); - it('should create root proto views', inject([AsyncTestCompleter], (async) => { + it('should create host proto views', inject([AsyncTestCompleter], (async) => { tplResolver.setView(MainComponent, new View({template: '
'})); var rootProtoView = createProtoView([ createComponentElementBinder(reader, MainComponent) @@ -373,7 +373,7 @@ export function main() { ], [rootProtoView, mainProtoView] ); - compiler.compileRoot(null, MainComponent).then( (protoView) => { + compiler.compileInHost(MainComponent).then( (protoView) => { expect(protoView).toBe(rootProtoView); expect(rootProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView); async.done(); @@ -388,7 +388,7 @@ function createDirectiveBinding(reader, type) { } function createProtoView(elementBinders = null) { - var pv = new AppProtoView(null, null, null); + var pv = new AppProtoView(null, null); if (isBlank(elementBinders)) { elementBinders = []; } @@ -497,7 +497,7 @@ class FakeRenderer extends renderApi.Renderer { return PromiseWrapper.resolve(ListWrapper.removeAt(this._results, 0)); } - createRootProtoView(elementOrSelector, componentId):Promise { + createHostProtoView(componentId):Promise { return PromiseWrapper.resolve( createRenderProtoView([createRenderComponentElementBinder(0)]) ); @@ -545,7 +545,7 @@ class FakeProtoViewFactory extends ProtoViewFactory { _results:List; constructor(results) { - super(null, null); + super(null); this.requests = []; this._results = results; } diff --git a/modules/angular2/test/core/compiler/dynamic_component_loader_spec.js b/modules/angular2/test/core/compiler/dynamic_component_loader_spec.js index ea6583f4b3..06f214bd2b 100644 --- a/modules/angular2/test/core/compiler/dynamic_component_loader_spec.js +++ b/modules/angular2/test/core/compiler/dynamic_component_loader_spec.js @@ -24,26 +24,32 @@ import {ElementRef, ElementInjector, ProtoElementInjector, PreBuiltObjects} from import {Compiler} from 'angular2/src/core/compiler/compiler'; import {AppProtoView, AppView} from 'angular2/src/core/compiler/view'; import {ViewFactory} from 'angular2/src/core/compiler/view_factory' -import {Renderer} from 'angular2/src/render/api'; +import {AppViewHydrator} from 'angular2/src/core/compiler/view_hydrator'; export function main() { describe("DynamicComponentLoader", () => { var compiler; var viewFactory; var directiveMetadataReader; - var renderer; + var viewHydrator; var loader; beforeEach( () => { compiler = new SpyCompiler(); viewFactory = new SpyViewFactory(); - renderer = new SpyRenderer(); + viewHydrator = new SpyAppViewHydrator(); directiveMetadataReader = new DirectiveMetadataReader(); - loader = new DynamicComponentLoader(compiler, directiveMetadataReader, renderer, viewFactory);; + loader = new DynamicComponentLoader(compiler, directiveMetadataReader, viewFactory, viewHydrator); }); function createProtoView() { - return new AppProtoView(null, null, null); + return new AppProtoView(null, null); + } + + function createEmptyView() { + var view = new AppView(null, createProtoView(), MapWrapper.create()); + view.init(null, [], [], [], [], []); + return view; } function createElementRef(view, boundElementIndex) { @@ -67,23 +73,25 @@ export function main() { }); }); - it('should add the child view into the host view', inject([AsyncTestCompleter], (async) => { + it('should compile, create and hydrate the view', inject([AsyncTestCompleter], (async) => { var log = []; - var hostView = new SpyAppView(); - var childView = new SpyAppView(); - hostView.spy('setDynamicComponentChildView').andCallFake( (boundElementIndex, childView) => { - ListWrapper.push(log, ['setDynamicComponentChildView', boundElementIndex, childView]); + var protoView = createProtoView(); + var hostView = createEmptyView(); + var childView = createEmptyView(); + viewHydrator.spy('hydrateDynamicComponentView').andCallFake( (hostView, boundElementIndex, + componentView, componentDirective, injector) => { + ListWrapper.push(log, ['hydrateDynamicComponentView', hostView, boundElementIndex, componentView]); }); - childView.spy('hydrate').andCallFake( (appInjector, hostElementInjector, context, locals) => { - ListWrapper.push(log, 'hydrate'); + viewFactory.spy('getView').andCallFake( (protoView) => { + ListWrapper.push(log, ['getView', protoView]); + return childView; }); - compiler.spy('compile').andCallFake( (_) => PromiseWrapper.resolve(createProtoView())); - viewFactory.spy('getView').andCallFake( (_) => childView); + compiler.spy('compile').andCallFake( (_) => PromiseWrapper.resolve(protoView)); var elementRef = createElementRef(hostView, 23); loader.loadIntoExistingLocation(SomeComponent, elementRef).then( (componentRef) => { - expect(log[0]).toEqual('hydrate'); - expect(log[1]).toEqual(['setDynamicComponentChildView', 23, childView]); + expect(log[0]).toEqual(['getView', protoView]); + expect(log[1]).toEqual(['hydrateDynamicComponentView', hostView, 23, childView]); async.done(); }); })); @@ -112,8 +120,8 @@ class SpyCompiler extends SpyObject {noSuchMethod(m){return super.noSuchMethod(m class SpyViewFactory extends SpyObject {noSuchMethod(m){return super.noSuchMethod(m)}} @proxy -@IMPLEMENTS(Renderer) -class SpyRenderer extends SpyObject {noSuchMethod(m){return super.noSuchMethod(m)}} +@IMPLEMENTS(AppViewHydrator) +class SpyAppViewHydrator extends SpyObject {noSuchMethod(m){return super.noSuchMethod(m)}} @proxy @IMPLEMENTS(AppView) diff --git a/modules/angular2/test/core/compiler/element_injector_spec.js b/modules/angular2/test/core/compiler/element_injector_spec.js index a2efbcd96f..a402799bb4 100644 --- a/modules/angular2/test/core/compiler/element_injector_spec.js +++ b/modules/angular2/test/core/compiler/element_injector_spec.js @@ -614,7 +614,7 @@ export function main() { }); it('should return viewContainer', function () { - var viewContainer = new ViewContainer(null, null, null, null); + var viewContainer = new ViewContainer(null, null, null, null, null); var inj = injector([], null, null, new PreBuiltObjects(null, null, viewContainer, null)); expect(inj.get(ViewContainer)).toEqual(viewContainer); @@ -631,21 +631,21 @@ export function main() { describe("dynamicallyCreateComponent", () => { it("should create a component dynamically", () => { var inj = injector([]); - inj.dynamicallyCreateComponent(SimpleDirective, null, null); + inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null), null); expect(inj.getDynamicallyLoadedComponent()).toBeAnInstanceOf(SimpleDirective); expect(inj.get(SimpleDirective)).toBeAnInstanceOf(SimpleDirective); }); it("should inject parent dependencies into the dynamically-loaded component", () => { var inj = parentChildInjectors([SimpleDirective], []); - inj.dynamicallyCreateComponent(NeedDirectiveFromAncestor, null, null); + inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(NeedDirectiveFromAncestor, null), null); expect(inj.getDynamicallyLoadedComponent()).toBeAnInstanceOf(NeedDirectiveFromAncestor); expect(inj.getDynamicallyLoadedComponent().dependency).toBeAnInstanceOf(SimpleDirective); }); it("should not inject the proxy component into the children of the dynamically-loaded component", () => { var injWithDynamicallyLoadedComponent = injector([SimpleDirective]); - injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(SomeOtherDirective, null, null); + injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(DirectiveBinding.createFromType(SomeOtherDirective, null), null); var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false); var shadowDomInj = shadowDomProtoInjector.instantiate(null); @@ -658,14 +658,14 @@ export function main() { it("should not inject the dynamically-loaded component into directives on the same element", () => { var proto = new ProtoElementInjector(null, 0, [NeedsDirective], false); var inj = proto.instantiate(null); - inj.dynamicallyCreateComponent(SimpleDirective, null, null); + inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null), null); expect(() => inj.instantiateDirectives(null, null, null, null)).toThrowError(); }); it("should inject the dynamically-loaded component into the children of the dynamically-loaded component", () => { var injWithDynamicallyLoadedComponent = injector([]); - injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(SimpleDirective, null, null); + injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null), null); var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false); var shadowDomInjector = shadowDomProtoInjector.instantiate(null); @@ -678,8 +678,10 @@ export function main() { it("should remove the dynamically-loaded component when dehydrating", () => { var inj = injector([]); inj.dynamicallyCreateComponent( - DirectiveWithDestroy, - new DummyDirective({lifecycle: [onDestroy]}), + DirectiveBinding.createFromType( + DirectiveWithDestroy, + new DummyDirective({lifecycle: [onDestroy]}) + ), null); var dir = inj.getDynamicallyLoadedComponent(); @@ -696,7 +698,7 @@ export function main() { it("should inject services of the dynamically-loaded component", () => { var inj = injector([]); var appInjector = Injector.resolveAndCreate([bind("service").toValue("Service")]); - inj.dynamicallyCreateComponent(NeedsService, null, appInjector); + inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(NeedsService, null), appInjector); expect(inj.getDynamicallyLoadedComponent().service).toEqual("Service"); }); }); @@ -706,13 +708,13 @@ export function main() { function createpreBuildObject(eventName, eventHandler) { var handlers = StringMapWrapper.create(); StringMapWrapper.set(handlers, eventName, eventHandler); - var pv = new AppProtoView(null, null, null); + var pv = new AppProtoView(null, null); pv.bindElement(null, 0, null, null, null); var eventBindings = ListWrapper.create(); ListWrapper.push(eventBindings, new EventBinding(eventName, new Parser(new Lexer()).parseAction('handler()', ''))); pv.bindEvent(eventBindings); - var view = new AppView(pv, MapWrapper.create()); + var view = new AppView(null, pv, MapWrapper.create()); view.context = new ContextWithHandler(eventHandler); return new PreBuiltObjects(view, null, null, null); } @@ -751,8 +753,8 @@ export function main() { beforeEach( () => { renderer = new FakeRenderer(); - var protoView = new AppProtoView(renderer, null, null); - view = new AppView(protoView, MapWrapper.create()); + var protoView = new AppProtoView(null, null); + view = new AppView(renderer, protoView, MapWrapper.create()); view.render = new ViewRef(); }); diff --git a/modules/angular2/test/core/compiler/integration_spec.js b/modules/angular2/test/core/compiler/integration_spec.js index 83e5c88d02..8487e96c75 100644 --- a/modules/angular2/test/core/compiler/integration_spec.js +++ b/modules/angular2/test/core/compiler/integration_spec.js @@ -300,7 +300,6 @@ export function main() { })); tb.createView(MyComp, {context: ctx}).then((view) => { - view.detectChanges(); var childNodesOfWrapper = view.rootNodes[0].childNodes; @@ -582,7 +581,7 @@ export function main() { dispatchEvent(DOM.getGlobalEventTarget("document"), 'domEvent'); expect(listener.eventType).toEqual('document_domEvent'); - view.rawView.dehydrate(); + view.destroy(); listener = injector.get(DecoratorListeningDomEvent); dispatchEvent(DOM.getGlobalEventTarget("body"), 'domEvent'); expect(listener.eventType).toEqual(''); diff --git a/modules/angular2/test/core/compiler/view_factory_spec.js b/modules/angular2/test/core/compiler/view_factory_spec.js index 94b9c50cfd..6fa30dc226 100644 --- a/modules/angular2/test/core/compiler/view_factory_spec.js +++ b/modules/angular2/test/core/compiler/view_factory_spec.js @@ -16,6 +16,7 @@ import { } from 'angular2/test_lib'; import {IMPLEMENTS, isBlank} from 'angular2/src/facade/lang'; import {ViewFactory} from 'angular2/src/core/compiler/view_factory'; +import {Renderer, ViewRef} from 'angular2/src/render/api'; import {AppProtoView, AppView} from 'angular2/src/core/compiler/view'; import {DirectiveBinding, ElementInjector} from 'angular2/src/core/compiler/element_injector'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; @@ -26,13 +27,15 @@ import {ChangeDetector, ProtoChangeDetector} from 'angular2/change_detection'; export function main() { describe('AppViewFactory', () => { var reader; + var renderer; beforeEach( () => { + renderer = new SpyRenderer(); reader = new DirectiveMetadataReader(); }); function createViewFactory({capacity}):ViewFactory { - return new ViewFactory(capacity); + return new ViewFactory(capacity, renderer); } function createProtoChangeDetector() { @@ -47,7 +50,7 @@ export function main() { if (isBlank(binders)) { binders = []; } - var pv = new AppProtoView(null, null, createProtoChangeDetector()); + var pv = new AppProtoView(null, createProtoChangeDetector()); pv.elementBinders = binders; return pv; } @@ -154,6 +157,13 @@ export function main() { @Component({ selector: 'someComponent' }) class SomeComponent {} +@proxy +@IMPLEMENTS(Renderer) +class SpyRenderer extends SpyObject { + constructor(){super(Renderer);} + noSuchMethod(m){return super.noSuchMethod(m)} +} + @proxy @IMPLEMENTS(ChangeDetector) class SpyChangeDetector extends SpyObject { diff --git a/modules/angular2/test/core/compiler/view_spec.js b/modules/angular2/test/core/compiler/view_hydrator_spec.js similarity index 72% rename from modules/angular2/test/core/compiler/view_spec.js rename to modules/angular2/test/core/compiler/view_hydrator_spec.js index 08daf38b30..995f10644d 100644 --- a/modules/angular2/test/core/compiler/view_spec.js +++ b/modules/angular2/test/core/compiler/view_hydrator_spec.js @@ -24,15 +24,18 @@ import {ElementBinder} from 'angular2/src/core/compiler/element_binder'; import {DirectiveBinding, ElementInjector} from 'angular2/src/core/compiler/element_injector'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; import {Component} from 'angular2/src/core/annotations/annotations'; +import {AppViewHydrator} from 'angular2/src/core/compiler/view_hydrator'; export function main() { - describe('AppView', () => { + describe('AppViewHydrator', () => { var renderer; var reader; + var hydrator; beforeEach( () => { renderer = new SpyRenderer(); reader = new DirectiveMetadataReader(); + hydrator = new AppViewHydrator(renderer); }); function createDirectiveBinding(type) { @@ -61,7 +64,7 @@ export function main() { if (isBlank(binders)) { binders = []; } - var res = new AppProtoView(renderer, null, null); + var res = new AppProtoView(null, null); res.elementBinders = binders; return res; } @@ -75,8 +78,15 @@ export function main() { ]); } + function createEmptyView() { + var view = new AppView(renderer, createProtoView(), MapWrapper.create()); + var changeDetector = new SpyChangeDetector(); + view.init(changeDetector, [], [], [], [], []); + return view; + } + function createHostView(pv, shadowView, componentInstance) { - var view = new AppView(pv, MapWrapper.create()); + var view = new AppView(renderer, pv, MapWrapper.create()); var changeDetector = new SpyChangeDetector(); var eij = createElementInjector(); eij.spy('getComponent').andCallFake( () => componentInstance ); @@ -85,84 +95,93 @@ export function main() { return view; } - describe('setDynamicComponentChildView', () => { + function hydrate(view) { + hydrator.hydrateInPlaceHostView(null, null, view, null); + } + + function dehydrate(view) { + hydrator.dehydrateInPlaceHostView(null, view); + } + + describe('hydrateDynamicComponentView', () => { it('should not allow to use non component indices', () => { var pv = createProtoView([createEmptyElBinder()]); var view = createHostView(pv, null, null); - var shadowView = new FakeAppView(); + var shadowView = createEmptyView(); expect( - () => view.setDynamicComponentChildView(0, shadowView) + () => hydrator.hydrateDynamicComponentView(view, 0, shadowView, null, null) ).toThrowError('There is no dynamic component directive at element 0'); }); it('should not allow to use static component indices', () => { var pv = createHostProtoView(createProtoView()); var view = createHostView(pv, null, null); - var shadowView = new FakeAppView(); + var shadowView = createEmptyView(); expect( - () => view.setDynamicComponentChildView(0, shadowView) + () => hydrator.hydrateDynamicComponentView(view, 0, shadowView, null, null) ).toThrowError('There is no dynamic component directive at element 0'); }); it('should not allow to overwrite an existing component', () => { var pv = createHostProtoView(null); - var shadowView = new FakeAppView(); + var shadowView = createEmptyView(); var view = createHostView(pv, null, null); - view.setDynamicComponentChildView(0, shadowView); + renderer.spy('createDynamicComponentView').andCallFake( (a,b,c) => { + return [new ViewRef(), new ViewRef()]; + }); + hydrator.hydrateDynamicComponentView(view, 0, shadowView, createDirectiveBinding(SomeComponent), null); expect( - () => view.setDynamicComponentChildView(0, shadowView) + () => hydrator.hydrateDynamicComponentView(view, 0, shadowView, null, null) ).toThrowError('There already is a bound component at element 0'); }); }); - describe('hydrate', () => { + describe('hydrate... shared functionality', () => { it('should hydrate existing child components', () => { var hostPv = createHostProtoView(createProtoView()); var componentInstance = {}; - var shadowView = new FakeAppView(); + var shadowView = createEmptyView(); var hostView = createHostView(hostPv, shadowView, componentInstance); - renderer.spy('createView').andCallFake( (_) => { + renderer.spy('createInPlaceHostView').andCallFake( (a,b,c) => { return [new ViewRef(), new ViewRef()]; }); - hostView.hydrate(null, null, null, null); + hydrate(hostView); - expect(shadowView.spy('hydrate')).not.toHaveBeenCalled(); - expect(shadowView.spy('internalHydrateRecurse')).toHaveBeenCalled(); + expect(shadowView.hydrated()).toBe(true); }); }); - describe('dehydrate', () => { + describe('dehydrate... shared functionality', () => { var hostView; var shadowView; function createAndHydrate(nestedProtoView) { var componentInstance = {}; - shadowView = new FakeAppView(); + shadowView = createEmptyView(); var hostPv = createHostProtoView(nestedProtoView); hostView = createHostView(hostPv, shadowView, componentInstance); - renderer.spy('createView').andCallFake( (_) => { + renderer.spy('createInPlaceHostView').andCallFake( (a,b,c) => { return [new ViewRef(), new ViewRef()]; }); - hostView.hydrate(null, null, null, null); + hydrate(hostView); } it('should dehydrate child components', () => { createAndHydrate(createProtoView()); - hostView.dehydrate(); + dehydrate(hostView); - expect(shadowView.spy('dehydrate')).not.toHaveBeenCalled(); - expect(shadowView.spy('internalDehydrateRecurse')).toHaveBeenCalled(); + expect(shadowView.hydrated()).toBe(false); }); it('should not clear static child components', () => { createAndHydrate(createProtoView()); - hostView.dehydrate(); + dehydrate(hostView); expect(hostView.componentChildViews[0]).toBe(shadowView); expect(hostView.changeDetector.spy('removeShadowDomChild')).not.toHaveBeenCalled(); @@ -170,7 +189,7 @@ export function main() { it('should clear dynamic child components', () => { createAndHydrate(null); - hostView.dehydrate(); + dehydrate(hostView); expect(hostView.componentChildViews[0]).toBe(null); expect(hostView.changeDetector.spy('removeShadowDomChild')).toHaveBeenCalledWith(shadowView.changeDetector); @@ -204,10 +223,3 @@ class SpyElementInjector extends SpyObject { constructor(){super(ElementInjector);} noSuchMethod(m){return super.noSuchMethod(m)} } - -@proxy -@IMPLEMENTS(AppView) -class FakeAppView extends SpyObject { - constructor(){super(AppView);} - noSuchMethod(m){return super.noSuchMethod(m)} -} diff --git a/modules/angular2/test/render/dom/direct_dom_renderer_integration_spec.js b/modules/angular2/test/render/dom/direct_dom_renderer_integration_spec.js index 67c09c65e8..89b6a56fc4 100644 --- a/modules/angular2/test/render/dom/direct_dom_renderer_integration_spec.js +++ b/modules/angular2/test/render/dom/direct_dom_renderer_integration_spec.js @@ -36,14 +36,14 @@ export function main() { }); renderer = testbed.renderer; eventPlugin = testbed.eventPlugin; - compileRoot = (rootEl, componentId) => testbed.compileRoot(rootEl, componentId); + compileRoot = (componentId) => testbed.compileRoot(componentId); } - it('should create root views while using the given elements in place', inject([AsyncTestCompleter], (async) => { + it('should create host views while using the given elements in place', inject([AsyncTestCompleter], (async) => { createRenderer(); - renderer.createRootProtoView(rootEl, 'someComponentId').then( (rootProtoView) => { + renderer.createHostProtoView('someComponentId').then( (rootProtoView) => { expect(rootProtoView.elementBinders[0].directives[0].directiveIndex).toBe(0); - var viewRefs = renderer.createView(rootProtoView.render); + var viewRefs = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render); expect(viewRefs.length).toBe(1); expect(viewRefs[0].delegate.rootNodes[0]).toEqual(rootEl); async.done(); @@ -52,7 +52,7 @@ export function main() { it('should add a static component', inject([AsyncTestCompleter], (async) => { createRenderer(); - renderer.createRootProtoView(rootEl, 'someComponentId').then( (rootProtoView) => { + renderer.createHostProtoView('someComponentId').then( (rootProtoView) => { var template = new ViewDefinition({ componentId: 'someComponent', template: 'hello', @@ -60,7 +60,7 @@ export function main() { }); renderer.compile(template).then( (pv) => { renderer.mergeChildComponentProtoViews(rootProtoView.render, [pv.render]); - renderer.createView(rootProtoView.render); + renderer.createInPlaceHostView(null, rootEl, rootProtoView.render); expect(rootEl).toHaveText('hello'); async.done(); }); @@ -69,16 +69,15 @@ export function main() { it('should add a a dynamic component', inject([AsyncTestCompleter], (async) => { createRenderer(); - renderer.createRootProtoView(rootEl, 'someComponentId').then( (rootProtoView) => { + renderer.createHostProtoView('someComponentId').then( (rootProtoView) => { var template = new ViewDefinition({ componentId: 'someComponent', template: 'hello', directives: [] }); renderer.compile(template).then( (pv) => { - var rootViewRef = renderer.createView(rootProtoView.render)[0]; - var childComponentViewRef = renderer.createView(pv.render)[0]; - renderer.setDynamicComponentView(rootViewRef, 0, childComponentViewRef); + var rootViewRef = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render)[0]; + renderer.createDynamicComponentView(rootViewRef, 0, pv.render)[0]; expect(rootEl).toHaveText('hello'); async.done(); }); @@ -93,8 +92,8 @@ export function main() { directives: [] })] }); - compileRoot(rootEl, 'someComponent').then( (rootProtoView) => { - var viewRefs = renderer.createView(rootProtoView.render); + compileRoot('someComponent').then( (rootProtoView) => { + var viewRefs = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render); renderer.setText(viewRefs[1], 0, 'hello'); expect(rootEl).toHaveText('hello'); async.done(); @@ -109,8 +108,8 @@ export function main() { directives: [] })] }); - compileRoot(rootEl, 'someComponent').then( (rootProtoView) => { - var viewRefs = renderer.createView(rootProtoView.render); + compileRoot('someComponent').then( (rootProtoView) => { + var viewRefs = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render); renderer.setElementProperty(viewRefs[1], 0, 'value', 'hello'); expect(DOM.childNodes(rootEl)[0].value).toEqual('hello'); async.done(); @@ -125,18 +124,20 @@ export function main() { directives: [] })] }); - compileRoot(rootEl, 'someComponent').then( (rootProtoView) => { - var viewRef = renderer.createView(rootProtoView.render)[1]; + compileRoot('someComponent').then( (rootProtoView) => { + var viewRef = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render)[1]; var vcProtoViewRef = rootProtoView.elementBinders[0] .nestedProtoView.elementBinders[0].nestedProtoView.render; var vcRef = new ViewContainerRef(viewRef, 0); - var childViewRef = renderer.createView(vcProtoViewRef)[0]; - expect(rootEl).toHaveText(''); - renderer.insertViewIntoContainer(vcRef, childViewRef); + var childViewRef = renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0]; expect(rootEl).toHaveText('hello'); renderer.detachViewFromContainer(vcRef, 0); expect(rootEl).toHaveText(''); + renderer.insertViewIntoContainer(vcRef, 0, childViewRef); + expect(rootEl).toHaveText('hello'); + renderer.destroyViewInContainer(vcRef, 0); + expect(rootEl).toHaveText(''); async.done(); }); @@ -151,14 +152,16 @@ export function main() { })], viewCacheCapacity: 2 }); - compileRoot(rootEl, 'someComponent').then( (rootProtoView) => { + compileRoot('someComponent').then( (rootProtoView) => { + var viewRef = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render)[1]; var vcProtoViewRef = rootProtoView.elementBinders[0] .nestedProtoView.elementBinders[0].nestedProtoView.render; + var vcRef = new ViewContainerRef(viewRef, 0); - var viewRef1 = renderer.createView(vcProtoViewRef)[0]; - renderer.destroyView(viewRef1); - var viewRef2 = renderer.createView(vcProtoViewRef)[0]; - var viewRef3 = renderer.createView(vcProtoViewRef)[0]; + var viewRef1 = renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0]; + renderer.destroyViewInContainer(vcRef, 0); + var viewRef2 = renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0]; + var viewRef3 = renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0]; expect(viewRef2.delegate).toBe(viewRef1.delegate); expect(viewRef3.delegate).not.toBe(viewRef1.delegate); @@ -176,8 +179,8 @@ export function main() { directives: [] })] }); - compileRoot(rootEl, 'someComponent').then( (rootProtoView) => { - var viewRef = renderer.createView(rootProtoView.render)[1]; + compileRoot('someComponent').then( (rootProtoView) => { + var viewRef = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render)[1]; var dispatcher = new LoggingEventDispatcher(); renderer.setEventDispatcher(viewRef, dispatcher); var inputEl = DOM.childNodes(rootEl)[0]; diff --git a/modules/angular2/test/render/dom/integration_testbed.js b/modules/angular2/test/render/dom/integration_testbed.js index 5e4850d193..33719997db 100644 --- a/modules/angular2/test/render/dom/integration_testbed.js +++ b/modules/angular2/test/render/dom/integration_testbed.js @@ -15,6 +15,7 @@ import {EventManager, EventManagerPlugin} from 'angular2/src/render/dom/events/e import {VmTurnZone} from 'angular2/src/core/zone/vm_turn_zone'; import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver'; import {ViewFactory} from 'angular2/src/render/dom/view/view_factory'; +import {RenderViewHydrator} from 'angular2/src/render/dom/view/view_hydrator'; export class IntegrationTestbed { renderer; @@ -45,11 +46,12 @@ export class IntegrationTestbed { this.eventPlugin = new FakeEventManagerPlugin(); var eventManager = new EventManager([this.eventPlugin], new FakeVmTurnZone()); var viewFactory = new ViewFactory(viewCacheCapacity, eventManager, shadowDomStrategy); - this.renderer = new DirectDomRenderer(compiler, viewFactory, shadowDomStrategy); + var viewHydrator = new RenderViewHydrator(eventManager, viewFactory); + this.renderer = new DirectDomRenderer(compiler, viewFactory, viewHydrator, shadowDomStrategy); } - compileRoot(rootEl, componentId):Promise { - return this.renderer.createRootProtoView(rootEl, componentId).then( (rootProtoView) => { + compileRoot(componentId):Promise { + return this.renderer.createHostProtoView(componentId).then( (rootProtoView) => { return this._compileNestedProtoViews(rootProtoView, [ new DirectiveMetadata({ type: DirectiveMetadata.COMPONENT_TYPE, diff --git a/modules/angular2/test/render/dom/shadow_dom/emulated_scoped_shadow_dom_strategy_spec.js b/modules/angular2/test/render/dom/shadow_dom/emulated_scoped_shadow_dom_strategy_spec.js index 9b1324b898..36eeb57e34 100644 --- a/modules/angular2/test/render/dom/shadow_dom/emulated_scoped_shadow_dom_strategy_spec.js +++ b/modules/angular2/test/render/dom/shadow_dom/emulated_scoped_shadow_dom_strategy_spec.js @@ -31,7 +31,7 @@ import {StyleInliner} from 'angular2/src/render/dom/shadow_dom/style_inliner'; import {RenderView} from 'angular2/src/render/dom/view/view'; export function main() { - describe('EmulatedScoped', () => { + describe('EmulatedScopedShadowDomStrategy', () => { var xhr, styleHost, strategy; beforeEach(() => { @@ -47,7 +47,7 @@ export function main() { it('should attach the view nodes as child of the host element', () => { var host = el('
original content
'); var nodes = el('
view
'); - var view = new RenderView(null, [nodes], [], [], [], [], null); + var view = new RenderView(null, [nodes], [], [], [], []); strategy.attachTemplate(host, view); var firstChild = DOM.firstChild(host); diff --git a/modules/angular2/test/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy_spec.js b/modules/angular2/test/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy_spec.js index 5eff1cc29f..776a083a54 100644 --- a/modules/angular2/test/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy_spec.js +++ b/modules/angular2/test/render/dom/shadow_dom/emulated_unscoped_shadow_dom_strategy_spec.js @@ -28,7 +28,7 @@ import {RenderView} from 'angular2/src/render/dom/view/view'; export function main() { var strategy; - describe('EmulatedUnscoped', () => { + describe('EmulatedUnscopedShadowDomStrategy', () => { var styleHost; beforeEach(() => { @@ -42,7 +42,7 @@ export function main() { it('should attach the view nodes as child of the host element', () => { var host = el('
original content
'); var nodes = el('
view
'); - var view = new RenderView(null, [nodes], [], [], [], [], null); + var view = new RenderView(null, [nodes], [], [], [], []); strategy.attachTemplate(host, view); var firstChild = DOM.firstChild(host); diff --git a/modules/angular2/test/render/dom/shadow_dom/native_shadow_dom_strategy_spec.js b/modules/angular2/test/render/dom/shadow_dom/native_shadow_dom_strategy_spec.js index ab13611b75..21f3f28020 100644 --- a/modules/angular2/test/render/dom/shadow_dom/native_shadow_dom_strategy_spec.js +++ b/modules/angular2/test/render/dom/shadow_dom/native_shadow_dom_strategy_spec.js @@ -35,7 +35,7 @@ export function main() { it('should attach the view nodes to the shadow root', () => { var host = el('
original content
'); var nodes = el('
view
'); - var view = new RenderView(null, [nodes], [], [], [], [], null); + var view = new RenderView(null, [nodes], [], [], [], []); strategy.attachTemplate(host, view); var shadowRoot = DOM.getShadowRoot(host); diff --git a/modules/angular2/test/render/dom/shadow_dom_emulation_integration_spec.js b/modules/angular2/test/render/dom/shadow_dom_emulation_integration_spec.js index c4e7c299b2..4c1e139903 100644 --- a/modules/angular2/test/render/dom/shadow_dom_emulation_integration_spec.js +++ b/modules/angular2/test/render/dom/shadow_dom_emulation_integration_spec.js @@ -60,7 +60,7 @@ export function main() { templates: ListWrapper.concat(templates, componentTemplates) }); renderer = testbed.renderer; - compileRoot = (rootEl, componentId) => testbed.compileRoot(rootEl, componentId); + compileRoot = (rootEl) => testbed.compileRoot(rootEl); compile = (componentId) => testbed.compile(componentId); } @@ -78,8 +78,8 @@ export function main() { directives: [simple] })] }); - compileRoot(rootEl, 'main').then( (pv) => { - renderer.createView(pv.render); + compileRoot('main').then( (pv) => { + renderer.createInPlaceHostView(null, rootEl, pv.render); expect(rootEl).toHaveText('SIMPLE(A)'); @@ -97,11 +97,10 @@ export function main() { directives: [dynamicComponent] })] }); - compileRoot(rootEl, 'main').then( (rootPv) => { + compileRoot('main').then( (rootPv) => { compile('simple').then( (simplePv) => { - var views = renderer.createView(rootPv.render); - var simpleViews = renderer.createView(simplePv.render); - renderer.setDynamicComponentView(views[1], 0, simpleViews[0]); + var views = renderer.createInPlaceHostView(null, rootEl, rootPv.render); + renderer.createDynamicComponentView(views[1], 0, simplePv.render); expect(rootEl).toHaveText('SIMPLE(A)'); @@ -122,8 +121,8 @@ export function main() { directives: [multipleContentTagsComponent] })] }); - compileRoot(rootEl, 'main').then( (pv) => { - renderer.createView(pv.render); + compileRoot('main').then( (pv) => { + renderer.createInPlaceHostView(null, rootEl, pv.render); expect(rootEl).toHaveText('(A, BC)'); @@ -142,8 +141,8 @@ export function main() { directives: [multipleContentTagsComponent] })] }); - compileRoot(rootEl, 'main').then( (pv) => { - renderer.createView(pv.render); + compileRoot('main').then( (pv) => { + renderer.createInPlaceHostView(null, rootEl, pv.render); expect(rootEl).toHaveText('(, BAC)'); @@ -162,20 +161,18 @@ export function main() { directives: [multipleContentTagsComponent, manualViewportDirective] })] }); - compileRoot(rootEl, 'main').then( (pv) => { - var viewRefs = renderer.createView(pv.render); + compileRoot('main').then( (pv) => { + var viewRefs = renderer.createInPlaceHostView(null, rootEl, pv.render); var vcRef = new ViewContainerRef(viewRefs[1], 1); var vcProtoViewRef = pv.elementBinders[0].nestedProtoView .elementBinders[1].nestedProtoView.render; - var childViewRef = renderer.createView(vcProtoViewRef)[0]; - expect(rootEl).toHaveText('(, B)'); - renderer.insertViewIntoContainer(vcRef, childViewRef); + renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0]; expect(rootEl).toHaveText('(, AB)'); - renderer.detachViewFromContainer(vcRef, 0); + renderer.destroyViewInContainer(vcRef, 0); expect(rootEl).toHaveText('(, B)'); @@ -194,20 +191,18 @@ export function main() { directives: [multipleContentTagsComponent, manualViewportDirective] })] }); - compileRoot(rootEl, 'main').then( (pv) => { - var viewRefs = renderer.createView(pv.render); + compileRoot('main').then( (pv) => { + var viewRefs = renderer.createInPlaceHostView(null, rootEl, pv.render); var vcRef = new ViewContainerRef(viewRefs[1], 1); var vcProtoViewRef = pv.elementBinders[0].nestedProtoView .elementBinders[1].nestedProtoView.render; - var childViewRef = renderer.createView(vcProtoViewRef)[0]; - expect(rootEl).toHaveText('(, B)'); - renderer.insertViewIntoContainer(vcRef, childViewRef); + renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0]; expect(rootEl).toHaveText('(A, B)'); - renderer.detachViewFromContainer(vcRef, 0); + renderer.destroyViewInContainer(vcRef, 0); expect(rootEl).toHaveText('(, B)'); @@ -226,8 +221,8 @@ export function main() { directives: [outerWithIndirectNestedComponent] })] }); - compileRoot(rootEl, 'main').then( (pv) => { - renderer.createView(pv.render); + compileRoot('main').then( (pv) => { + renderer.createInPlaceHostView(null, rootEl, pv.render); expect(rootEl).toHaveText('OUTER(SIMPLE(AB))'); @@ -247,16 +242,14 @@ export function main() { directives: [outerComponent, manualViewportDirective] })] }); - compileRoot(rootEl, 'main').then( (pv) => { - var viewRefs = renderer.createView(pv.render); + compileRoot('main').then( (pv) => { + var viewRefs = renderer.createInPlaceHostView(null, rootEl, pv.render); var vcRef = new ViewContainerRef(viewRefs[1], 1); var vcProtoViewRef = pv.elementBinders[0].nestedProtoView .elementBinders[1].nestedProtoView.render; - var childViewRef = renderer.createView(vcProtoViewRef)[0]; - expect(rootEl).toHaveText('OUTER(INNER(INNERINNER(,BC)))'); - renderer.insertViewIntoContainer(vcRef, childViewRef); + renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0]; expect(rootEl).toHaveText('OUTER(INNER(INNERINNER(A,BC)))'); async.done(); @@ -275,21 +268,20 @@ export function main() { directives: [conditionalContentComponent] })] }); - compileRoot(rootEl, 'main').then( (pv) => { - var viewRefs = renderer.createView(pv.render); + compileRoot('main').then( (pv) => { + var viewRefs = renderer.createInPlaceHostView(null, rootEl, pv.render); var vcRef = new ViewContainerRef(viewRefs[2], 0); var vcProtoViewRef = pv.elementBinders[0].nestedProtoView .elementBinders[0].nestedProtoView .elementBinders[0].nestedProtoView.render; - var childViewRef = renderer.createView(vcProtoViewRef)[0]; expect(rootEl).toHaveText('(, ABC)'); - renderer.insertViewIntoContainer(vcRef, childViewRef); + renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0]; expect(rootEl).toHaveText('(A, BC)'); - renderer.detachViewFromContainer(vcRef, 0); + renderer.destroyViewInContainer(vcRef, 0); expect(rootEl).toHaveText('(, ABC)'); diff --git a/modules/angular2/test/render/dom/view/view_factory_spec.js b/modules/angular2/test/render/dom/view/view_factory_spec.js index 3be319fed6..8c15768b8e 100644 --- a/modules/angular2/test/render/dom/view/view_factory_spec.js +++ b/modules/angular2/test/render/dom/view/view_factory_spec.js @@ -42,7 +42,6 @@ export function main() { } return new RenderProtoView({ element: rootEl, - isRootView: false, elementBinders: binders }); } diff --git a/modules/angular2/test/render/dom/view/view_spec.js b/modules/angular2/test/render/dom/view/view_hydrator_spec.js similarity index 71% rename from modules/angular2/test/render/dom/view/view_spec.js rename to modules/angular2/test/render/dom/view/view_hydrator_spec.js index 191eb11d0f..fd37ef4630 100644 --- a/modules/angular2/test/render/dom/view/view_spec.js +++ b/modules/angular2/test/render/dom/view/view_hydrator_spec.js @@ -23,12 +23,16 @@ import {ShadowDomStrategy} from 'angular2/src/render/dom/shadow_dom/shadow_dom_s import {LightDom} from 'angular2/src/render/dom/shadow_dom/light_dom'; import {EventManager} from 'angular2/src/render/dom/events/event_manager'; import {DOM} from 'angular2/src/dom/dom_adapter'; +import {ViewFactory} from 'angular2/src/render/dom/view/view_factory'; +import {RenderViewHydrator} from 'angular2/src/render/dom/view/view_hydrator'; export function main() { - describe('RenderView', () => { + describe('RenderViewHydrator', () => { var shadowDomStrategy; var eventManager; + var viewFactory; + var viewHydrator; function createProtoView({rootEl, binders}={}) { if (isBlank(rootEl)) { @@ -39,7 +43,6 @@ export function main() { } return new RenderProtoView({ element: rootEl, - isRootView: false, elementBinders: binders }); } @@ -67,83 +70,85 @@ export function main() { function createEmptyView() { var root = el('
'); return new RenderView(createProtoView(), [DOM.childNodes(root)[0]], - [], [], [], [], eventManager); + [], [], [], []); } function createHostView(pv, shadowDomView) { var view = new RenderView(pv, [el('
')], - [], [el('
')], [], [], eventManager); - view.setComponentView(shadowDomStrategy, 0, shadowDomView); + [], [el('
')], [], []); + viewFactory.setComponentView(view, 0, shadowDomView); return view; } + function hydrate(view) { + viewHydrator.hydrateInPlaceHostView(null, view); + } + + function dehydrate(view) { + viewHydrator.dehydrateInPlaceHostView(null, view); + } + beforeEach( () => { eventManager = new SpyEventManager(); shadowDomStrategy = new SpyShadowDomStrategy(); shadowDomStrategy.spy('constructLightDom').andCallFake( (lightDomView, shadowDomView, el) => { return new SpyLightDom(); }); + viewFactory = new ViewFactory(1, eventManager, shadowDomStrategy); + viewHydrator = new RenderViewHydrator(eventManager, viewFactory); }); - describe('setComponentView', () => { + describe('hydrateDynamicComponentView', () => { - it('should redistribute when a component is added to a hydrated view', () => { - var shadowView = new SpyRenderView(); + it('should redistribute', () => { + var shadowView = createEmptyView(); var hostPv = createHostProtoView(createProtoView()); var hostView = createHostView(hostPv, shadowView); - hostView.hydrate(null); - hostView.setComponentView(shadowDomStrategy, 0, shadowView); + viewHydrator.hydrateDynamicComponentView(hostView, 0, shadowView); var lightDomSpy:SpyLightDom = hostView.lightDoms[0]; expect(lightDomSpy.spy('redistribute')).toHaveBeenCalled(); }); - it('should not redistribute when a component is added to a dehydrated view', () => { - var shadowView = new SpyRenderView(); - var hostPv = createHostProtoView(createProtoView()); - var hostView = createHostView(hostPv, shadowView); - hostView.setComponentView(shadowDomStrategy, 0, shadowView); - var lightDomSpy:SpyLightDom = hostView.lightDoms[0]; - expect(lightDomSpy.spy('redistribute')).not.toHaveBeenCalled(); - }); - }); - describe('hydrate', () => { + describe('hydrate... shared functionality', () => { it('should hydrate existing child components', () => { var hostPv = createHostProtoView(createProtoView()); - var shadowView = new SpyRenderView(); - var hostView = createHostView(hostPv, shadowView); + var shadowView = createEmptyView(); + createHostView(hostPv, shadowView); - hostView.hydrate(null); + hydrate(shadowView); - expect(shadowView.spy('hydrate')).toHaveBeenCalled(); + expect(shadowView.hydrated).toBe(true); }); }); - describe('dehydrate', () => { + describe('dehydrate... shared functionality', () => { var hostView; function createAndHydrate(nestedProtoView, shadowView) { var hostPv = createHostProtoView(nestedProtoView); hostView = createHostView(hostPv, shadowView); - hostView.hydrate(null); + hydrate(hostView); } it('should dehydrate child components', () => { - var shadowView = new SpyRenderView(); + var shadowView = createEmptyView(); createAndHydrate(createProtoView(), shadowView); - hostView.dehydrate(); - expect(shadowView.spy('dehydrate')).toHaveBeenCalled(); + expect(shadowView.hydrated).toBe(true); + dehydrate(hostView); + + expect(shadowView.hydrated).toBe(false); }); it('should not clear static child components', () => { var shadowView = createEmptyView(); createAndHydrate(createProtoView(), shadowView); - hostView.dehydrate(); + dehydrate(hostView); expect(hostView.componentChildViews[0]).toBe(shadowView); expect(shadowView.rootNodes[0].parentNode).toBeTruthy(); @@ -152,7 +157,7 @@ export function main() { it('should clear dynamic child components', () => { var shadowView = createEmptyView(); createAndHydrate(null, shadowView); - hostView.dehydrate(); + dehydrate(hostView); expect(hostView.componentChildViews[0]).toBe(null); expect(shadowView.rootNodes[0].parentNode).toBe(null); @@ -184,10 +189,3 @@ class SpyLightDom extends SpyObject { noSuchMethod(m){return super.noSuchMethod(m)} } -@proxy -@IMPLEMENTS(RenderView) -class SpyRenderView extends SpyObject { - constructor(){super(RenderView);} - noSuchMethod(m){return super.noSuchMethod(m)} -} - diff --git a/modules/benchmarks/src/compiler/compiler_benchmark.js b/modules/benchmarks/src/compiler/compiler_benchmark.js index 9e0bb68837..7974896304 100644 --- a/modules/benchmarks/src/compiler/compiler_benchmark.js +++ b/modules/benchmarks/src/compiler/compiler_benchmark.js @@ -72,6 +72,7 @@ export function main() { new Parser(new Lexer()), shadowDomStrategy, new TemplateLoader(null, urlResolver) ), null, + null, shadowDomStrategy ); var compiler = new Compiler( @@ -81,7 +82,7 @@ export function main() { new ComponentUrlMapper(), urlResolver, renderer, - new ProtoViewFactory(dynamicChangeDetection, renderer) + new ProtoViewFactory(dynamicChangeDetection) ); var templateNoBindings = createTemplateHtml('templateNoBindings', count); var templateWithBindings = createTemplateHtml('templateWithBindings', count); diff --git a/modules/examples/src/hello_world/index_static.js b/modules/examples/src/hello_world/index_static.js index b3ef782b95..48c2ab0b1f 100644 --- a/modules/examples/src/hello_world/index_static.js +++ b/modules/examples/src/hello_world/index_static.js @@ -25,11 +25,13 @@ import {TestabilityRegistry, Testability} from 'angular2/src/core/testability/te import {reflector} from 'angular2/src/reflection/reflection'; import {ViewFactory, VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_factory'; +import {AppViewHydrator} from 'angular2/src/core/compiler/view_hydrator'; import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory'; import {Renderer} from 'angular2/src/render/api'; import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer'; import * as rc from 'angular2/src/render/dom/compiler/compiler'; import * as rvf from 'angular2/src/render/dom/view/view_factory'; +import * as rvh from 'angular2/src/render/dom/view/view_hydrator'; import {Inject} from 'angular2/di'; function setup() { @@ -174,16 +176,9 @@ function setup() { }); reflector.registerType(DynamicComponentLoader, { - "factory": (compiler, reader, renderer, viewFactory) => - new DynamicComponentLoader(compiler, reader, renderer, viewFactory), - "parameters": [[Compiler], [DirectiveMetadataReader], [Renderer], [ViewFactory]], - "annotations": [] - }); - - reflector.registerType(DirectDomRenderer, { - "factory": (renderCompiler, renderViewFactory, shadowDomStrategy) => - new DirectDomRenderer(renderCompiler, renderViewFactory, shadowDomStrategy), - "parameters": [[rc.Compiler], [rvf.ViewFactory], [ShadowDomStrategy]], + "factory": (compiler, reader, viewFactory, appViewHydrator) => + new DynamicComponentLoader(compiler, reader, viewFactory, appViewHydrator), + "parameters": [[Compiler], [DirectiveMetadataReader], [ViewFactory], [AppViewHydrator]], "annotations": [] }); @@ -207,17 +202,31 @@ function setup() { "annotations": [] }); + reflector.registerType(rvh.RenderViewHydrator, { + "factory": (eventManager, viewFactory) => + new rvh.RenderViewHydrator(eventManager, viewFactory), + "parameters": [[rvf.ViewFactory], [EventManager]], + "annotations": [] + }); + reflector.registerType(ProtoViewFactory, { - "factory": (changeDetection, renderer) => - new ProtoViewFactory(changeDetection, renderer), - "parameters": [[ChangeDetection], [Renderer]], + "factory": (changeDetection) => + new ProtoViewFactory(changeDetection), + "parameters": [[ChangeDetection]], "annotations": [] }); reflector.registerType(ViewFactory, { - "factory": (capacity) => - new ViewFactory(capacity), - "parameters": [[new Inject(VIEW_POOL_CAPACITY)]], + "factory": (capacity, renderer) => + new ViewFactory(capacity, renderer), + "parameters": [[new Inject(VIEW_POOL_CAPACITY)],[Renderer]], + "annotations": [] + }); + + reflector.registerType(AppViewHydrator, { + "factory": (renderer) => + new AppViewHydrator(renderer), + "parameters": [[Renderer]], "annotations": [] }); @@ -240,9 +249,9 @@ function setup() { }); reflector.registerType(DirectDomRenderer, { - "factory": (renderCompiler, renderViewFactory, shadowDomStrategy) => - new DirectDomRenderer(renderCompiler, renderViewFactory, shadowDomStrategy), - "parameters": [[rc.Compiler], [rvf.ViewFactory], [ShadowDomStrategy]], + "factory": (renderCompiler, renderViewFactory, renderViewHydrator, shadowDomStrategy) => + new DirectDomRenderer(renderCompiler, renderViewFactory, renderViewHydrator, shadowDomStrategy), + "parameters": [[rc.Compiler], [rvf.ViewFactory], [rvh.RenderViewHydrator], [ShadowDomStrategy]], "annotations": [] }); @@ -266,17 +275,10 @@ function setup() { "annotations": [] }); - reflector.registerType(ProtoViewFactory, { - "factory": (changeDetection, renderer) => - new ProtoViewFactory(changeDetection, renderer), - "parameters": [[ChangeDetection], [Renderer]], - "annotations": [] - }); - reflector.registerType(ViewFactory, { - "factory": (capacity) => - new ViewFactory(capacity), - "parameters": [[new Inject(VIEW_POOL_CAPACITY)]], + "factory": (capacity, renderer) => + new ViewFactory(capacity, renderer), + "parameters": [[new Inject(VIEW_POOL_CAPACITY)],[Renderer]], "annotations": [] });