From f830cfca12d7b90c44a4a9bbe8884b0dafb8b00a Mon Sep 17 00:00:00 2001 From: Tobias Bosch Date: Thu, 16 Apr 2015 15:38:28 -0700 Subject: [PATCH] refactor(view): provide ViewContainers dynamically on any element --- modules/angular2/src/core/application.js | 4 +- .../src/core/compiler/element_injector.js | 23 +++- modules/angular2/src/core/compiler/view.js | 23 +++- .../src/core/compiler/view_container.js | 42 +++---- .../src/core/compiler/view_factory.js | 21 ++-- .../src/core/compiler/view_hydrator.js | 36 ++---- .../src/render/dom/direct_dom_renderer.js | 2 +- .../src/render/dom/shadow_dom/light_dom.js | 46 ++++--- .../src/render/dom/view/element_binder.js | 2 +- modules/angular2/src/render/dom/view/view.js | 24 +++- .../src/render/dom/view/view_container.js | 43 ++++--- .../src/render/dom/view/view_factory.js | 11 +- .../src/render/dom/view/view_hydrator.js | 33 +---- .../compiler/dynamic_component_loader_spec.js | 6 +- .../core/compiler/element_injector_spec.js | 36 ++++-- .../test/core/compiler/integration_spec.js | 50 +++++++- .../test/core/compiler/view_factory_spec.js | 2 +- .../test/core/compiler/view_hydrator_spec.js | 8 +- .../angular2/test/core/compiler/view_spec.js | 115 ++++++++++++++++++ ...mulated_scoped_shadow_dom_strategy_spec.js | 2 +- ...lated_unscoped_shadow_dom_strategy_spec.js | 2 +- .../render/dom/shadow_dom/light_dom_spec.js | 20 +-- .../native_shadow_dom_strategy_spec.js | 2 +- .../render/dom/view/view_hydrator_spec.js | 4 +- .../test/render/dom/view/view_spec.js | 109 +++++++++++++++++ .../examples/src/hello_world/index_static.js | 11 +- 26 files changed, 467 insertions(+), 210 deletions(-) create mode 100644 modules/angular2/test/core/compiler/view_spec.js create mode 100644 modules/angular2/test/render/dom/view/view_spec.js diff --git a/modules/angular2/src/core/application.js b/modules/angular2/src/core/application.js index 333ec5e4e8..27a5fcb2dc 100644 --- a/modules/angular2/src/core/application.js +++ b/modules/angular2/src/core/application.js @@ -105,8 +105,8 @@ function _injectorBindings(appComponentType): List { // TODO(tbosch): We need an explicit factory here, as // we are getting errors in dart2js with mirrors... bind(ViewFactory).toFactory( - (capacity, renderer) => new ViewFactory(capacity, renderer), - [VIEW_POOL_CAPACITY, Renderer] + (capacity, renderer, appViewHydrator) => new ViewFactory(capacity, renderer, appViewHydrator), + [VIEW_POOL_CAPACITY, Renderer, AppViewHydrator] ), bind(VIEW_POOL_CAPACITY).toValue(10000), AppViewHydrator, diff --git a/modules/angular2/src/core/compiler/element_injector.js b/modules/angular2/src/core/compiler/element_injector.js index 921b819990..8674e03636 100644 --- a/modules/angular2/src/core/compiler/element_injector.js +++ b/modules/angular2/src/core/compiler/element_injector.js @@ -35,6 +35,10 @@ export class ElementRef { return this.elementInjector._preBuiltObjects.view; } + get viewContainer() { + return this.hostView.getOrCreateViewContainer(this.boundElementIndex); + } + get injector() { return this.elementInjector._lightDomAppInjector; } @@ -298,13 +302,10 @@ export class DirectiveBinding extends ResolvedBinding { export class PreBuiltObjects { view:viewModule.AppView; element:NgElement; - viewContainer:ViewContainer; changeDetector:ChangeDetector; - constructor(view, element:NgElement, viewContainer:ViewContainer, - changeDetector:ChangeDetector) { + constructor(view, element:NgElement, changeDetector:ChangeDetector) { this.view = view; this.element = element; - this.viewContainer = viewContainer; this.changeDetector = changeDetector; } } @@ -929,7 +930,7 @@ export class ElementInjector extends TreeNode { // TODO: AppView should not be injectable. Remove it. if (keyId === staticKeys.viewId) return this._preBuiltObjects.view; if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element; - if (keyId === staticKeys.viewContainerId) return this._preBuiltObjects.viewContainer; + if (keyId === staticKeys.viewContainerId) return this._preBuiltObjects.view.getOrCreateViewContainer(this._proto.index); if (keyId === staticKeys.changeDetectorRefId) return this._preBuiltObjects.changeDetector.ref; //TODO add other objects as needed @@ -984,6 +985,18 @@ export class ElementInjector extends TreeNode { getExportImplicitName() { return this._proto.exportImplicitName; } + + getLightDomAppInjector() { + return this._lightDomAppInjector; + } + + getHost() { + return this._host; + } + + getBoundElementIndex() { + return this._proto.index; + } } class OutOfBoundsAccess extends Error { diff --git a/modules/angular2/src/core/compiler/view.js b/modules/angular2/src/core/compiler/view.js index bd705b85e5..5bbca54a1e 100644 --- a/modules/angular2/src/core/compiler/view.js +++ b/modules/angular2/src/core/compiler/view.js @@ -8,6 +8,8 @@ import {SetterFn} from 'angular2/src/reflection/types'; import {IMPLEMENTS, int, isPresent, isBlank, BaseException} from 'angular2/src/facade/lang'; import {ViewContainer} from './view_container'; import * as renderApi from 'angular2/src/render/api'; +import * as vfModule from './view_factory'; +import * as vhModule from './view_hydrator'; /** * Const of making objects: http://jsperf.com/instantiate-size-of-object @@ -17,7 +19,6 @@ import * as renderApi from 'angular2/src/render/api'; // TODO(tbosch): this is not supported in dart2js (no '.' is allowed) // @IMPLEMENTS(renderApi.EventDispatcher) export class AppView { - render:renderApi.ViewRef; /// This list matches the _nodes list. It is sparse, since only Elements have ElementInjector rootElementInjectors:List; @@ -28,6 +29,8 @@ export class AppView { preBuiltObjects: List; proto: AppProtoView; renderer: renderApi.Renderer; + viewFactory: vfModule.ViewFactory; + viewHydrator: vhModule.AppViewHydrator; /** * The context against which data-binding expressions in this view are evaluated against. @@ -43,30 +46,40 @@ export class AppView { */ locals:Locals; - constructor(renderer:renderApi.Renderer, proto:AppProtoView, protoLocals:Map) { + constructor(renderer:renderApi.Renderer, viewFactory:vfModule.ViewFactory, viewHydrator:vhModule.AppViewHydrator, proto:AppProtoView, protoLocals:Map) { this.render = null; this.proto = proto; this.changeDetector = null; this.elementInjectors = null; this.rootElementInjectors = null; this.componentChildViews = null; - this.viewContainers = null; + this.viewContainers = ListWrapper.createFixedSize(this.proto.elementBinders.length); this.preBuiltObjects = null; this.context = null; this.locals = new Locals(null, MapWrapper.clone(protoLocals)); //TODO optimize this this.renderer = renderer; + this.viewFactory = viewFactory; + this.viewHydrator = viewHydrator; } init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List, - viewContainers:List, preBuiltObjects:List, componentChildViews:List) { + preBuiltObjects:List, componentChildViews:List) { this.changeDetector = changeDetector; this.elementInjectors = elementInjectors; this.rootElementInjectors = rootElementInjectors; - this.viewContainers = viewContainers; this.preBuiltObjects = preBuiltObjects; this.componentChildViews = componentChildViews; } + getOrCreateViewContainer(boundElementIndex:number) { + var viewContainer = this.viewContainers[boundElementIndex]; + if (isBlank(viewContainer)) { + viewContainer = new ViewContainer(this, this.proto.elementBinders[boundElementIndex].nestedProtoView, this.elementInjectors[boundElementIndex]); + this.viewContainers[boundElementIndex] = viewContainer; + } + return viewContainer; + } + setLocal(contextName: string, value) { if (!this.hydrated()) throw new BaseException('Cannot set locals on dehydrated view.'); if (!MapWrapper.contains(this.proto.variableBindings, contextName)) { diff --git a/modules/angular2/src/core/compiler/view_container.js b/modules/angular2/src/core/compiler/view_container.js index 74a51d4cee..a7af234bb0 100644 --- a/modules/angular2/src/core/compiler/view_container.js +++ b/modules/angular2/src/core/compiler/view_container.js @@ -4,45 +4,31 @@ import {Injector} from 'angular2/di'; import * as eiModule from 'angular2/src/core/compiler/element_injector'; 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'; +import {ViewContainerRef} from 'angular2/src/render/api'; /** * @exportedAs angular2/view */ export class ViewContainer { - viewFactory: vfModule.ViewFactory; - viewHydrator: vhModule.AppViewHydrator; - renderer: Renderer; - - render:renderApi.ViewContainerRef; parentView: viewModule.AppView; defaultProtoView: viewModule.AppProtoView; _views: List; elementInjector: eiModule.ElementInjector; - appInjector: Injector; - hostElementInjector: eiModule.ElementInjector; - constructor(viewFactory:vfModule.ViewFactory, - renderer: Renderer, - parentView: viewModule.AppView, + constructor(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; this.elementInjector = elementInjector; // The order in this list matches the DOM order. this._views = []; - this.appInjector = null; - this.hostElementInjector = null; + } + + getRender() { + return new ViewContainerRef(this.parentView.render, this.elementInjector.getBoundElementIndex()); } internalClearWithoutRender() { @@ -71,22 +57,22 @@ export class ViewContainer { } hydrated() { - return isPresent(this.appInjector); + return this.parentView.hydrated(); } // TODO(rado): profile and decide whether bounds checks should be added // to the methods below. - create(atIndex=-1, protoView:viewModule.AppProtoView = null): viewModule.AppView { + create(atIndex=-1, protoView:viewModule.AppProtoView = null, injector:Injector = null): viewModule.AppView { if (atIndex == -1) atIndex = this._views.length; if (!this.hydrated()) throw new BaseException( 'Cannot create views on a dehydrated ViewContainer'); if (isBlank(protoView)) { protoView = this.defaultProtoView; } - var newView = this.viewFactory.getView(protoView); + var newView = this.parentView.viewFactory.getView(protoView); // insertion must come before hydration so that element injector trees are attached. this._insertInjectors(newView, atIndex); - this.viewHydrator.hydrateViewInViewContainer(this, atIndex, newView); + this.parentView.viewHydrator.hydrateViewInViewContainer(this, atIndex, newView, injector); return newView; } @@ -95,7 +81,7 @@ export class ViewContainer { 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); + this.parentView.renderer.insertViewIntoContainer(this.getRender(), atIndex, view.render); return view; } @@ -110,9 +96,9 @@ export class ViewContainer { if (atIndex == -1) atIndex = this._views.length - 1; var view = this._views[atIndex]; // opposite order as in create - this.viewHydrator.dehydrateViewInViewContainer(this, atIndex, view); + this.parentView.viewHydrator.dehydrateViewInViewContainer(this, atIndex, view); this._detachInjectors(atIndex); - this.viewFactory.returnView(view); + this.parentView.viewFactory.returnView(view); // view is intentionally not returned to the client. } @@ -124,7 +110,7 @@ export class ViewContainer { if (atIndex == -1) atIndex = this._views.length - 1; var detachedView = this._detachInjectors(atIndex); detachedView.changeDetector.remove(); - this.renderer.detachViewFromContainer(this.render, atIndex); + this.parentView.renderer.detachViewFromContainer(this.getRender(), atIndex); return detachedView; } diff --git a/modules/angular2/src/core/compiler/view_factory.js b/modules/angular2/src/core/compiler/view_factory.js index 2707a90625..9d862dddd7 100644 --- a/modules/angular2/src/core/compiler/view_factory.js +++ b/modules/angular2/src/core/compiler/view_factory.js @@ -3,9 +3,9 @@ import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src import * as eli from './element_injector'; 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'; +import {AppViewHydrator} from './view_hydrator'; // TODO(tbosch): Make this an OpaqueToken as soon as our transpiler supports this! export const VIEW_POOL_CAPACITY = 'ViewFactory.viewPoolCapacity'; @@ -15,11 +15,13 @@ export class ViewFactory { _poolCapacityPerProtoView:number; _pooledViewsPerProtoView:Map>; _renderer:Renderer; + _viewHydrator:AppViewHydrator; - constructor(@Inject(VIEW_POOL_CAPACITY) poolCapacityPerProtoView, renderer:Renderer) { + constructor(@Inject(VIEW_POOL_CAPACITY) poolCapacityPerProtoView, renderer:Renderer, viewHydrator:AppViewHydrator) { this._poolCapacityPerProtoView = poolCapacityPerProtoView; this._pooledViewsPerProtoView = MapWrapper.create(); this._renderer = renderer; + this._viewHydrator = viewHydrator; } getView(protoView:viewModule.AppProtoView):viewModule.AppView { @@ -50,7 +52,7 @@ export class ViewFactory { } _createView(protoView:viewModule.AppProtoView): viewModule.AppView { - var view = new viewModule.AppView(this._renderer, protoView, protoView.protoLocals); + var view = new viewModule.AppView(this._renderer, this, this._viewHydrator, protoView, protoView.protoLocals); var changeDetector = protoView.protoChangeDetector.instantiate(view, protoView.bindings, protoView.getVariableBindings(), protoView.getdirectiveRecords()); @@ -58,7 +60,6 @@ export class ViewFactory { var elementInjectors = ListWrapper.createFixedSize(binders.length); var rootElementInjectors = []; var preBuiltObjects = ListWrapper.createFixedSize(binders.length); - var viewContainers = ListWrapper.createFixedSize(binders.length); var componentChildViews = ListWrapper.createFixedSize(binders.length); for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) { @@ -88,22 +89,14 @@ export class ViewFactory { componentChildViews[binderIdx] = childView; } - // viewContainers - var viewContainer = null; - if (isPresent(binder.viewportDirective)) { - viewContainer = new vcModule.ViewContainer(this, this._renderer, view, binder.nestedProtoView, elementInjector); - } - viewContainers[binderIdx] = viewContainer; - // preBuiltObjects if (isPresent(elementInjector)) { - preBuiltObjects[binderIdx] = new eli.PreBuiltObjects(view, new NgElement(view, binderIdx), viewContainer, - childChangeDetector); + preBuiltObjects[binderIdx] = new eli.PreBuiltObjects(view, new NgElement(view, binderIdx), childChangeDetector); } } view.init(changeDetector, elementInjectors, rootElementInjectors, - viewContainers, preBuiltObjects, componentChildViews); + preBuiltObjects, componentChildViews); return view; } diff --git a/modules/angular2/src/core/compiler/view_hydrator.js b/modules/angular2/src/core/compiler/view_hydrator.js index f034ec155e..c0072aeee5 100644 --- a/modules/angular2/src/core/compiler/view_hydrator.js +++ b/modules/angular2/src/core/compiler/view_hydrator.js @@ -44,8 +44,7 @@ export class AppViewHydrator { } 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; + injector = hostElementInjector.getLightDomAppInjector(); } // shadowDomAppInjector @@ -114,19 +113,22 @@ export class AppViewHydrator { this._renderer.destroyInPlaceHostView(parentRenderViewRef, render); } - hydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, atIndex:number, view:viewModule.AppView) { + hydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, atIndex:number, view:viewModule.AppView, injector:Injector = null) { if (!viewContainer.hydrated()) throw new BaseException( 'Cannot create views on a dehydrated ViewContainer'); - var renderViewRefs = this._renderer.createViewInContainer(viewContainer.render, atIndex, view.proto.render); + if (isBlank(injector)) { + injector = viewContainer.elementInjector.getLightDomAppInjector(); + } + var renderViewRefs = this._renderer.createViewInContainer(viewContainer.getRender(), atIndex, view.proto.render); viewContainer.parentView.changeDetector.addChild(view.changeDetector); - this._viewHydrateRecurse(view, renderViewRefs, 0, viewContainer.appInjector, viewContainer.hostElementInjector, + this._viewHydrateRecurse(view, renderViewRefs, 0, injector, viewContainer.elementInjector.getHost(), 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); + this._renderer.destroyViewInContainer(viewContainer.getRender(), atIndex); } _viewHydrateRecurse( @@ -142,14 +144,6 @@ export class AppViewHydrator { 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; @@ -273,16 +267,6 @@ export class AppViewHydrator { 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. */ @@ -296,10 +280,6 @@ export class AppViewHydrator { // as we don't want to change the render side // as the render side does its own recursion. viewContainer.internalClearWithoutRender(); - viewContainer.viewHydrator = null; - viewContainer.appInjector = null; - viewContainer.hostElementInjector = null; - viewContainer.render = null; } } diff --git a/modules/angular2/src/render/dom/direct_dom_renderer.js b/modules/angular2/src/render/dom/direct_dom_renderer.js index aea9fa5a68..76bfd52286 100644 --- a/modules/angular2/src/render/dom/direct_dom_renderer.js +++ b/modules/angular2/src/render/dom/direct_dom_renderer.js @@ -14,7 +14,7 @@ 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]; + return _resolveView(vc.view).getOrCreateViewContainer(vc.elementIndex); } function _resolveView(viewRef:DirectDomViewRef) { diff --git a/modules/angular2/src/render/dom/shadow_dom/light_dom.js b/modules/angular2/src/render/dom/shadow_dom/light_dom.js index d7d436b598..584ef37e42 100644 --- a/modules/angular2/src/render/dom/shadow_dom/light_dom.js +++ b/modules/angular2/src/render/dom/shadow_dom/light_dom.js @@ -9,13 +9,11 @@ export class DestinationLightDom {} class _Root { node; - viewContainer; - content; + boundElementIndex:number; - constructor(node, viewContainer, content) { + constructor(node, boundElementIndex) { this.node = node; - this.viewContainer = viewContainer; - this.content = content; + this.boundElementIndex = boundElementIndex; } } @@ -79,11 +77,16 @@ export class LightDom { for (var i = 0; i < roots.length; ++i) { var root = roots[i]; - - if (isPresent(root.viewContainer)) { - res = ListWrapper.concat(res, root.viewContainer.nodes()); - } else if (isPresent(root.content)) { - res = ListWrapper.concat(res, root.content.nodes()); + if (isPresent(root.boundElementIndex)) { + var vc = this.lightDomView.viewContainers[root.boundElementIndex]; + var content = this.lightDomView.contentTags[root.boundElementIndex]; + if (isPresent(vc)) { + res = ListWrapper.concat(res, vc.nodes()); + } else if (isPresent(content)) { + res = ListWrapper.concat(res, content.nodes()); + } else { + ListWrapper.push(res, root.node); + } } else { ListWrapper.push(res, root.node); } @@ -92,27 +95,22 @@ export class LightDom { } // Returns a list of Roots for all the nodes of the light DOM. - // The Root object contains the DOM node and its corresponding injector (could be null). + // The Root object contains the DOM node and its corresponding boundElementIndex _roots() { if (isPresent(this.roots)) return this.roots; - var viewContainers = this.lightDomView.viewContainers; - var contentTags = this.lightDomView.contentTags; + var boundElements = this.lightDomView.boundElements; this.roots = ListWrapper.map(this.nodes, (n) => { - var foundVc = null; - var foundContentTag = null; - for (var i=0; i; contentTags: List; lightDoms: List; + hostLightDom: LightDom; proto: RenderProtoView; hydrated: boolean; _eventDispatcher: any/*EventDispatcher*/; @@ -33,20 +34,39 @@ export class RenderView { constructor( proto:RenderProtoView, rootNodes:List, - boundTextNodes: List, boundElements:List, viewContainers:List, contentTags:List) { + boundTextNodes: List, boundElements:List, contentTags:List) { this.proto = proto; this.rootNodes = rootNodes; this.boundTextNodes = boundTextNodes; this.boundElements = boundElements; - this.viewContainers = viewContainers; + this.viewContainers = ListWrapper.createFixedSize(boundElements.length); this.contentTags = contentTags; this.lightDoms = ListWrapper.createFixedSize(boundElements.length); ListWrapper.fill(this.lightDoms, null); this.componentChildViews = ListWrapper.createFixedSize(boundElements.length); + this.hostLightDom = null; this.hydrated = false; this.eventHandlerRemovers = null; } + getDirectParentLightDom(boundElementIndex:number) { + var binder = this.proto.elementBinders[boundElementIndex]; + var destLightDom = null; + if (binder.parentIndex !== -1 && binder.distanceToParent === 1) { + destLightDom = this.lightDoms[binder.parentIndex]; + } + return destLightDom; + } + + getOrCreateViewContainer(binderIndex) { + var vc = this.viewContainers[binderIndex]; + if (isBlank(vc)) { + vc = new ViewContainer(this, binderIndex); + this.viewContainers[binderIndex] = vc; + } + return vc; + } + setElementProperty(elementIndex:number, propertyName:string, value:any) { var setter = MapWrapper.get(this.proto.elementBinders[elementIndex].propertySetters, propertyName); setter(this.boundElements[elementIndex], value); diff --git a/modules/angular2/src/render/dom/view/view_container.js b/modules/angular2/src/render/dom/view/view_container.js index d80d587a25..1c1854125a 100644 --- a/modules/angular2/src/render/dom/view/view_container.js +++ b/modules/angular2/src/render/dom/view/view_container.js @@ -3,22 +3,17 @@ import {ListWrapper, MapWrapper, List} from 'angular2/src/facade/collection'; import {DOM} from 'angular2/src/dom/dom_adapter'; import * as viewModule from './view'; -import * as ldModule from '../shadow_dom/light_dom'; export class ViewContainer { - templateElement; + parentView: viewModule.RenderView; + boundElementIndex: number; views: List; - lightDom: ldModule.LightDom; - hostLightDom: ldModule.LightDom; - hydrated: boolean; - - constructor(templateElement) { - this.templateElement = templateElement; + constructor(parentView: viewModule.RenderView, boundElementIndex: number) { + this.parentView = parentView; + this.boundElementIndex = boundElementIndex; // The order in this list matches the DOM order. this.views = []; - this.hostLightDom = null; - this.hydrated = false; } get(index: number): viewModule.RenderView { @@ -30,22 +25,26 @@ export class ViewContainer { } _siblingToInsertAfter(index: number) { - if (index == 0) return this.templateElement; + if (index == 0) return this.parentView.boundElements[this.boundElementIndex]; return ListWrapper.last(this.views[index - 1].rootNodes); } _checkHydrated() { - if (!this.hydrated) throw new BaseException( + if (!this.parentView.hydrated) throw new BaseException( 'Cannot change dehydrated ViewContainer'); } + _getDirectParentLightDom() { + return this.parentView.getDirectParentLightDom(this.boundElementIndex); + } + clear() { this._checkHydrated(); for (var i=this.views.length-1; i>=0; i--) { this.detach(i); } - if (isPresent(this.lightDom)) { - this.lightDom.redistribute(); + if (isPresent(this._getDirectParentLightDom())) { + this._getDirectParentLightDom().redistribute(); } } @@ -54,14 +53,14 @@ export class ViewContainer { if (atIndex == -1) atIndex = this.views.length; ListWrapper.insert(this.views, atIndex, view); - if (isBlank(this.lightDom)) { + if (isBlank(this._getDirectParentLightDom())) { ViewContainer.moveViewNodesAfterSibling(this._siblingToInsertAfter(atIndex), view); } else { - this.lightDom.redistribute(); + this._getDirectParentLightDom().redistribute(); } // new content tags might have appeared, we need to redistribute. - if (isPresent(this.hostLightDom)) { - this.hostLightDom.redistribute(); + if (isPresent(this.parentView.hostLightDom)) { + this.parentView.hostLightDom.redistribute(); } return view; } @@ -74,14 +73,14 @@ export class ViewContainer { this._checkHydrated(); var detachedView = this.get(atIndex); ListWrapper.removeAt(this.views, atIndex); - if (isBlank(this.lightDom)) { + if (isBlank(this._getDirectParentLightDom())) { ViewContainer.removeViewNodes(detachedView); } else { - this.lightDom.redistribute(); + this._getDirectParentLightDom().redistribute(); } // content tags might have disappeared we need to do redistribution. - if (isPresent(this.hostLightDom)) { - this.hostLightDom.redistribute(); + if (isPresent(this.parentView.hostLightDom)) { + this.parentView.hostLightDom.redistribute(); } return detachedView; } diff --git a/modules/angular2/src/render/dom/view/view_factory.js b/modules/angular2/src/render/dom/view/view_factory.js index 9ac2494555..b9bf86739a 100644 --- a/modules/angular2/src/render/dom/view/view_factory.js +++ b/modules/angular2/src/render/dom/view/view_factory.js @@ -8,7 +8,6 @@ import {Content} from '../shadow_dom/content_tag'; import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy'; import {EventManager} from 'angular2/src/render/dom/events/event_manager'; -import * as vcModule from './view_container'; import * as pvModule from './proto_view'; import * as viewModule from './view'; import {NG_BINDING_CLASS_SELECTOR, NG_BINDING_CLASS} from '../util'; @@ -91,7 +90,6 @@ export class ViewFactory { var binders = protoView.elementBinders; var boundTextNodes = []; var boundElements = ListWrapper.createFixedSize(binders.length); - var viewContainers = ListWrapper.createFixedSize(binders.length); var contentTags = ListWrapper.createFixedSize(binders.length); for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) { @@ -111,13 +109,6 @@ export class ViewFactory { ListWrapper.push(boundTextNodes, childNodes[textNodeIndices[i]]); } - // viewContainers - var viewContainer = null; - if (isBlank(binder.componentId) && isPresent(binder.nestedProtoView)) { - viewContainer = new vcModule.ViewContainer(element); - } - viewContainers[binderIdx] = viewContainer; - // contentTags var contentTag = null; if (isPresent(binder.contentTagSelector)) { @@ -128,7 +119,7 @@ export class ViewFactory { var view = new viewModule.RenderView( protoView, viewRootNodes, - boundTextNodes, boundElements, viewContainers, contentTags + boundTextNodes, boundElements, contentTags ); for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) { diff --git a/modules/angular2/src/render/dom/view/view_hydrator.js b/modules/angular2/src/render/dom/view/view_hydrator.js index 2d81a2123e..39aaadf07b 100644 --- a/modules/angular2/src/render/dom/view/view_hydrator.js +++ b/modules/angular2/src/render/dom/view/view_hydrator.js @@ -63,33 +63,21 @@ export class RenderViewHydrator { } hydrateViewInViewContainer(viewContainer:vcModule.ViewContainer, view:viewModule.RenderView) { - this._viewHydrateRecurse(view, viewContainer.hostLightDom); + this._viewHydrateRecurse(view, viewContainer.parentView.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; + view.hostLightDom = hostLightDom; - // 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); - } + // content tags + for (var i = 0; i < view.contentTags.length; i++) { + var destLightDom = view.getDirectParentLightDom(i); var ct = view.contentTags[i]; if (isPresent(ct)) { ct.hydrate(destLightDom); @@ -167,26 +155,17 @@ export class RenderViewHydrator { view.eventHandlerRemovers[i](); } + view.hostLightDom = null; 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 viewContainer; + }); + var inj = injector([], null, null, new PreBuiltObjects(view, null, null)); expect(inj.get(ViewContainer)).toEqual(viewContainer); }); it('should return changeDetectorRef', function () { var cd = new DynamicChangeDetector(null, null, null, [], []); - var inj = injector([], null, null, new PreBuiltObjects(null, null, null, cd)); + var inj = injector([], null, null, new PreBuiltObjects(null, null, cd)); expect(inj.get(ChangeDetectorRef)).toBe(cd.ref); }); @@ -710,12 +714,12 @@ export function main() { beforeEach( () => { renderer = new FakeRenderer(); var protoView = new AppProtoView(null, null); - view = new AppView(renderer, protoView, MapWrapper.create()); + view = new AppView(renderer, null, null, protoView, MapWrapper.create()); view.render = new ViewRef(); }); it('should be injectable and callable', () => { - var preBuildObject = new PreBuiltObjects(view, null, null, null); + var preBuildObject = new PreBuiltObjects(view, null, null); var inj = injector([NeedsPropertySetter], null, null, preBuildObject); var component = inj.get(NeedsPropertySetter); component.setProp('foobar'); @@ -734,7 +738,7 @@ export function main() { }); it('should be injectable and callable without specifying param type annotation', () => { - var preBuildObject = new PreBuiltObjects(view, null, null, null); + var preBuildObject = new PreBuiltObjects(view, null, null); var inj = injector([NeedsPropertySetterNoType], null, null, preBuildObject); var component = inj.get(NeedsPropertySetterNoType); component.setProp('foobar'); @@ -773,6 +777,16 @@ export function main() { var inj = injector([NeedsElementRef]); expect(inj.get(NeedsElementRef).elementRef).toBeAnInstanceOf(ElementRef); }); + + it('should return the viewContainer from the view', () => { + var viewContainer = new ViewContainer(null, null, null); + var view = new DummyView(); + view.spy('getOrCreateViewContainer').andCallFake( (index) => { + return viewContainer; + }); + var inj = injector([NeedsElementRef], null, null, new PreBuiltObjects(view, null, null)); + expect(inj.get(NeedsElementRef).elementRef.viewContainer).toBe(viewContainer); + }); }); describe('directive queries', () => { diff --git a/modules/angular2/test/core/compiler/integration_spec.js b/modules/angular2/test/core/compiler/integration_spec.js index 1e8e177083..d04be4ea3b 100644 --- a/modules/angular2/test/core/compiler/integration_spec.js +++ b/modules/angular2/test/core/compiler/integration_spec.js @@ -34,6 +34,7 @@ import {ElementRef} from 'angular2/src/core/compiler/element_injector'; import {If} from 'angular2/src/directives/if'; import {ViewContainer} from 'angular2/src/core/compiler/view_container'; +import {Compiler} from 'angular2/src/core/compiler/compiler'; export function main() { describe('integration tests', function() { @@ -694,6 +695,27 @@ export function main() { })); }); + describe('dynamic ViewContainers', () => { + + it('should allow to create a ViewContainer at any bound location', + inject([TestBed, AsyncTestCompleter, Compiler], (tb, async, compiler) => { + tb.overrideView(MyComp, new View({ + template: '
', + directives: [DynamicViewport] + })); + + tb.createView(MyComp).then((view) => { + var dynamicVp = view.rawView.elementInjectors[0].get(DynamicViewport); + dynamicVp.done.then( (_) => { + view.detectChanges(); + expect(view.rootNodes).toHaveText('dynamic greet'); + async.done(); + }); + }); + })); + + }); + it('should support static attributes', inject([TestBed, AsyncTestCompleter], (tb, async) => { tb.overrideView(MyComp, new View({ template: '', @@ -774,9 +796,22 @@ export function main() { }); } -class DynamicallyCreatedComponentService { +@Decorator({ + selector: 'dynamic-vp' +}) +class DynamicViewport { + done; + constructor(vc:ViewContainer, inj:Injector, compiler:Compiler) { + var myService = new MyService(); + myService.greeting = 'dynamic greet'; + this.done = compiler.compileInHost(ChildCompUsingService).then( (hostPv) => { + vc.create(0, hostPv, inj.createChildFromResolved(Injector.resolve([bind(MyService).toValue(myService)]))) + }); + } } +class DynamicallyCreatedComponentService {} + @DynamicComponent({ selector: 'dynamic-comp' }) @@ -909,6 +944,19 @@ class ChildComp { } } +@Component({ + selector: 'child-cmp-svc' +}) +@View({ + template: '{{ctxProp}}' +}) +class ChildCompUsingService { + ctxProp:string; + constructor(service: MyService) { + this.ctxProp = service.greeting; + } +} + @Decorator({ selector: 'some-directive' }) diff --git a/modules/angular2/test/core/compiler/view_factory_spec.js b/modules/angular2/test/core/compiler/view_factory_spec.js index 6fa30dc226..3c89b72af2 100644 --- a/modules/angular2/test/core/compiler/view_factory_spec.js +++ b/modules/angular2/test/core/compiler/view_factory_spec.js @@ -35,7 +35,7 @@ export function main() { }); function createViewFactory({capacity}):ViewFactory { - return new ViewFactory(capacity, renderer); + return new ViewFactory(capacity, renderer, null); } function createProtoChangeDetector() { diff --git a/modules/angular2/test/core/compiler/view_hydrator_spec.js b/modules/angular2/test/core/compiler/view_hydrator_spec.js index 223e01505d..9817999825 100644 --- a/modules/angular2/test/core/compiler/view_hydrator_spec.js +++ b/modules/angular2/test/core/compiler/view_hydrator_spec.js @@ -80,19 +80,19 @@ export function main() { } function createEmptyView() { - var view = new AppView(renderer, createProtoView(), MapWrapper.create()); + var view = new AppView(renderer, null, null, createProtoView(), MapWrapper.create()); var changeDetector = new SpyChangeDetector(); - view.init(changeDetector, [], [], [], [], []); + view.init(changeDetector, [], [], [], []); return view; } function createHostView(pv, shadowView, componentInstance) { - var view = new AppView(renderer, pv, MapWrapper.create()); + var view = new AppView(renderer, null, null, pv, MapWrapper.create()); var changeDetector = new SpyChangeDetector(); var eij = createElementInjector(); eij.spy('getComponent').andCallFake( () => componentInstance ); view.init(changeDetector, [eij], [eij], - [null], [null], [shadowView]); + [null], [shadowView]); return view; } diff --git a/modules/angular2/test/core/compiler/view_spec.js b/modules/angular2/test/core/compiler/view_spec.js new file mode 100644 index 0000000000..cca2735367 --- /dev/null +++ b/modules/angular2/test/core/compiler/view_spec.js @@ -0,0 +1,115 @@ +import { + AsyncTestCompleter, + beforeEach, + ddescribe, + xdescribe, + describe, + el, + dispatchEvent, + expect, + iit, + inject, + beforeEachBindings, + it, + xit, + SpyObject, proxy +} from 'angular2/test_lib'; + +import {IMPLEMENTS, isBlank} from 'angular2/src/facade/lang'; +import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection'; + +import {AppProtoView, AppView} from 'angular2/src/core/compiler/view'; +import {ViewContainer} from 'angular2/src/core/compiler/view_container'; +import {Renderer} from 'angular2/src/render/api'; +import {ChangeDetector} from 'angular2/change_detection'; +import {ElementBinder} from 'angular2/src/core/compiler/element_binder'; +import {ElementInjector} from 'angular2/src/core/compiler/element_injector'; + +export function main() { + describe('AppView', () => { + var renderer; + + beforeEach( () => { + renderer = new SpyRenderer(); + }); + + function createElementInjector() { + return new SpyElementInjector(); + } + + function createEmptyElBinder() { + return new ElementBinder(0, null, 0, null, null, null); + } + + function createEmbeddedProtoViewElBinder(nestedProtoView) { + var binder = new ElementBinder(0, null, 0, null, null, null); + binder.nestedProtoView = nestedProtoView; + return binder; + } + + function createProtoView(binders = null) { + if (isBlank(binders)) { + binders = []; + } + var res = new AppProtoView(null, null); + res.elementBinders = binders; + return res; + } + + function createViewWithOneBoundElement(pv) { + var view = new AppView(renderer, null, null, pv, MapWrapper.create()); + var changeDetector = new SpyChangeDetector(); + var eij = createElementInjector(); + view.init(changeDetector, [eij], [eij], + [null], [null]); + return view; + } + + describe('getOrCreateViewContainer()', () => { + + it('should create a new container', () => { + var pv = createProtoView([createEmptyElBinder()]); + var view = createViewWithOneBoundElement(pv); + expect(view.getOrCreateViewContainer(0) instanceof ViewContainer).toBe(true); + }); + + it('should return an existing container', () => { + var pv = createProtoView([createEmptyElBinder()]); + var view = createViewWithOneBoundElement(pv); + var vc = view.getOrCreateViewContainer(0); + expect(view.getOrCreateViewContainer(0)).toBe(vc); + }); + + it('should store an existing nestedProtoView in the container', () => { + var defaultProtoView = createProtoView(); + var pv = createProtoView([createEmbeddedProtoViewElBinder(defaultProtoView)]); + var view = createViewWithOneBoundElement(pv); + var vc = view.getOrCreateViewContainer(0); + expect(vc.defaultProtoView).toBe(defaultProtoView); + }); + + }); + + }); +} + +@proxy +@IMPLEMENTS(Renderer) +class SpyRenderer extends SpyObject { + constructor(){super(Renderer);} + noSuchMethod(m){return super.noSuchMethod(m)} +} + +@proxy +@IMPLEMENTS(ChangeDetector) +class SpyChangeDetector extends SpyObject { + constructor(){super(ChangeDetector);} + noSuchMethod(m){return super.noSuchMethod(m)} +} + +@proxy +@IMPLEMENTS(ElementInjector) +class SpyElementInjector extends SpyObject { + constructor(){super(ElementInjector);} + noSuchMethod(m){return super.noSuchMethod(m)} +} 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 36eeb57e34..298fcf0586 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 @@ -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], [], [], [], []); + 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 776a083a54..e6875dbb56 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 @@ -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], [], [], [], []); + 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/light_dom_spec.js b/modules/angular2/test/render/dom/shadow_dom/light_dom_spec.js index 2fd191a0ce..5af2843cda 100644 --- a/modules/angular2/test/render/dom/shadow_dom/light_dom_spec.js +++ b/modules/angular2/test/render/dom/shadow_dom/light_dom_spec.js @@ -10,24 +10,30 @@ import {ViewContainer} from 'angular2/src/render/dom/view/view_container'; @proxy @IMPLEMENTS(RenderView) class FakeView { + boundElements; contentTags; viewContainers; constructor(containers = null) { + this.boundElements = []; this.contentTags = []; this.viewContainers = []; if (isPresent(containers)) { ListWrapper.forEach(containers, (c) => { + var boundElement = null; + var contentTag = null; + var vc = null; if (c instanceof FakeContentTag) { - ListWrapper.push(this.contentTags, c); - } else { - ListWrapper.push(this.contentTags, null); + contentTag = c; + boundElement = c.contentStartElement; } if (c instanceof FakeViewContainer) { - ListWrapper.push(this.viewContainers, c); - } else { - ListWrapper.push(this.viewContainers, null); + vc = c; + boundElement = c.templateElement; } + ListWrapper.push(this.contentTags, contentTag); + ListWrapper.push(this.viewContainers, vc); + ListWrapper.push(this.boundElements, boundElement); }); } } @@ -40,9 +46,9 @@ class FakeView { @proxy @IMPLEMENTS(ViewContainer) class FakeViewContainer { - templateElement; _nodes; _contentTagContainers; + templateElement; constructor(templateEl, nodes = null, views = null) { this.templateElement = templateEl; 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 21f3f28020..c1b27fb2c5 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], [], [], [], []); + var view = new RenderView(null, [nodes], [], [], []); strategy.attachTemplate(host, view); var shadowRoot = DOM.getShadowRoot(host); diff --git a/modules/angular2/test/render/dom/view/view_hydrator_spec.js b/modules/angular2/test/render/dom/view/view_hydrator_spec.js index fd37ef4630..35b4101b1d 100644 --- a/modules/angular2/test/render/dom/view/view_hydrator_spec.js +++ b/modules/angular2/test/render/dom/view/view_hydrator_spec.js @@ -70,12 +70,12 @@ export function main() { function createEmptyView() { var root = el('
'); return new RenderView(createProtoView(), [DOM.childNodes(root)[0]], - [], [], [], []); + [], [], []); } function createHostView(pv, shadowDomView) { var view = new RenderView(pv, [el('
')], - [], [el('
')], [], []); + [], [el('
')], [null]); viewFactory.setComponentView(view, 0, shadowDomView); return view; } diff --git a/modules/angular2/test/render/dom/view/view_spec.js b/modules/angular2/test/render/dom/view/view_spec.js new file mode 100644 index 0000000000..337b86f464 --- /dev/null +++ b/modules/angular2/test/render/dom/view/view_spec.js @@ -0,0 +1,109 @@ +import { + AsyncTestCompleter, + beforeEach, + ddescribe, + xdescribe, + describe, + el, + dispatchEvent, + expect, + iit, + inject, + beforeEachBindings, + it, + xit, + SpyObject, proxy +} from 'angular2/test_lib'; +import {IMPLEMENTS, isBlank} from 'angular2/src/facade/lang'; +import {ListWrapper} from 'angular2/src/facade/collection'; + +import {RenderProtoView} from 'angular2/src/render/dom/view/proto_view'; +import {ElementBinder} from 'angular2/src/render/dom/view/element_binder'; +import {RenderView} from 'angular2/src/render/dom/view/view'; +import {ViewContainer} from 'angular2/src/render/dom/view/view_container'; +import {LightDom} from 'angular2/src/render/dom/shadow_dom/light_dom'; +import {DOM} from 'angular2/src/dom/dom_adapter'; + +export function main() { + + describe('RenderView', () => { + function createProtoView(binders = null) { + if (isBlank(binders)) { + binders = []; + } + var rootEl = el('
'); + return new RenderProtoView({ + element: rootEl, + elementBinders: binders + }); + } + + function createView(pv=null, boundElementCount=0) { + if (isBlank(pv)) { + pv = createProtoView(); + } + var root = el('
'); + var boundElements = []; + for (var i=0; i { + + it('should return the LightDom of the direct parent', () => { + var pv = createProtoView( + [new ElementBinder(), new ElementBinder({ + parentIndex: 0, + distanceToParent: 1 + })] + ); + var view = createView(pv, 2); + view.lightDoms[0] = new SpyLightDom(); + view.lightDoms[1] = new SpyLightDom(); + expect(view.getDirectParentLightDom(1)).toBe(view.lightDoms[0]); + }); + + it('should return null if the direct parent is not bound', () => { + var pv = createProtoView( + [new ElementBinder(), new ElementBinder(), new ElementBinder({ + parentIndex: 0, + distanceToParent: 2 + })] + ); + var view = createView(pv, 3); + view.lightDoms[0] = new SpyLightDom(); + view.lightDoms[1] = new SpyLightDom(); + view.lightDoms[2] = new SpyLightDom(); + expect(view.getDirectParentLightDom(2)).toBe(null); + }); + + }); + + describe('getOrCreateViewContainer', () => { + it('should create a new container', () => { + var pv = createProtoView([new ElementBinder()]); + var view = createView(pv, 1); + expect(view.getOrCreateViewContainer(0) instanceof ViewContainer).toBe(true); + }); + + it('should return an existing container', () => { + var pv = createProtoView([new ElementBinder()]); + var view = createView(pv, 1); + var vc = view.getOrCreateViewContainer(0); + expect(view.getOrCreateViewContainer(0)).toBe(vc); + }); + }); + + }); +} + +@proxy +@IMPLEMENTS(LightDom) +class SpyLightDom extends SpyObject { + constructor(){super(LightDom);} + noSuchMethod(m){return super.noSuchMethod(m)} +} + diff --git a/modules/examples/src/hello_world/index_static.js b/modules/examples/src/hello_world/index_static.js index 5e4a50483c..04430b5313 100644 --- a/modules/examples/src/hello_world/index_static.js +++ b/modules/examples/src/hello_world/index_static.js @@ -218,8 +218,8 @@ function setup() { reflector.registerType(ViewFactory, { "factory": (capacity, renderer) => - new ViewFactory(capacity, renderer), - "parameters": [[new Inject(VIEW_POOL_CAPACITY)],[Renderer]], + new ViewFactory(capacity, renderer, appViewHydrator), + "parameters": [[new Inject(VIEW_POOL_CAPACITY)],[Renderer],[AppViewHydrator]], "annotations": [] }); @@ -281,13 +281,6 @@ function setup() { "annotations": [] }); - reflector.registerType(ViewFactory, { - "factory": (capacity, renderer) => - new ViewFactory(capacity, renderer), - "parameters": [[new Inject(VIEW_POOL_CAPACITY)],[Renderer]], - "annotations": [] - }); - reflector.registerType(VIEW_POOL_CAPACITY, { "factory": () => 10000, "parameters": [],