feat(view): introduce free embedded views

Free embedded views are view instances that are created
logically in the same was as views of a ViewContainer,
but their dom nodes are not attached.

BREAKING CHANGE:

- `Renderer.detachFreeHostView` was renamed to
  `Renderer.detachFreeView`
- `DomRenderer.getHostElement()` was generalized into
  `DomRenderer.getRootNodes()`
This commit is contained in:
Tobias Bosch
2015-06-03 11:02:51 -07:00
parent 9ce0870f6c
commit 5030ffb01c
10 changed files with 230 additions and 32 deletions

View File

@ -23,12 +23,9 @@ import * as renderApi from 'angular2/src/render/api';
import {EventDispatcher} from 'angular2/src/render/api';
export class AppViewContainer {
views: List<AppView>;
constructor() {
// The order in this list matches the DOM order.
this.views = [];
}
// The order in this list matches the DOM order.
views: List<AppView> = [];
freeViews: List<AppView> = [];
}
/**

View File

@ -115,6 +115,24 @@ export class AppViewManager {
this._destroyFreeHostView(parentView, hostView);
}
createFreeEmbeddedView(location: ElementRef, protoViewRef: ProtoViewRef,
injector: Injector = null): ViewRef {
var protoView = internalProtoView(protoViewRef);
var parentView = internalView(location.parentView);
var boundElementIndex = location.boundElementIndex;
var view = this._createPooledView(protoView);
this._utils.attachAndHydrateFreeEmbeddedView(parentView, boundElementIndex, view, injector);
this._viewHydrateRecurse(view);
return new ViewRef(view);
}
destroyFreeEmbeddedView(location: ElementRef, viewRef: ViewRef) {
var parentView = internalView(location.parentView);
var boundElementIndex = location.boundElementIndex;
this._destroyFreeEmbeddedView(parentView, boundElementIndex, internalView(viewRef));
}
createViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
protoViewRef: ProtoViewRef, context: ElementRef = null,
injector: Injector = null): ViewRef {
@ -225,11 +243,18 @@ export class AppViewManager {
_destroyFreeHostView(parentView, hostView) {
this._viewDehydrateRecurse(hostView, true);
this._renderer.detachFreeHostView(parentView.render, hostView.render);
this._renderer.detachFreeView(hostView.render);
this._utils.detachFreeHostView(parentView, hostView);
this._destroyPooledView(hostView);
}
_destroyFreeEmbeddedView(parentView, boundElementIndex, view) {
this._viewDehydrateRecurse(view, false);
this._renderer.detachFreeView(view.render);
this._utils.detachFreeEmbeddedView(parentView, boundElementIndex, view);
this._destroyPooledView(view);
}
_viewHydrateRecurse(view: viewModule.AppView) {
this._renderer.hydrateView(view.render);
@ -260,6 +285,9 @@ export class AppViewManager {
for (var j = vc.views.length - 1; j >= 0; j--) {
this._destroyViewInContainer(view, i, j);
}
for (var j = vc.freeViews.length - 1; j >= 0; j--) {
this._destroyFreeEmbeddedView(view, i, j);
}
}
}

View File

@ -110,6 +110,28 @@ export class AppViewManagerUtils {
ListWrapper.remove(parentView.freeHostViews, hostView);
}
attachAndHydrateFreeEmbeddedView(parentView: viewModule.AppView, boundElementIndex: number,
view: viewModule.AppView, injector: Injector = null) {
parentView.changeDetector.addChild(view.changeDetector);
var viewContainer = this._getOrCreateViewContainer(parentView, boundElementIndex);
ListWrapper.push(viewContainer.freeViews, view);
var elementInjector = parentView.elementInjectors[boundElementIndex];
for (var i = view.rootElementInjectors.length - 1; i >= 0; i--) {
view.rootElementInjectors[i].link(elementInjector);
}
this._hydrateView(view, injector, elementInjector, parentView.context, parentView.locals);
}
detachFreeEmbeddedView(parentView: viewModule.AppView, boundElementIndex: number,
view: viewModule.AppView) {
var viewContainer = parentView.viewContainers[boundElementIndex];
view.changeDetector.remove();
ListWrapper.remove(viewContainer.freeViews, view);
for (var i = 0; i < view.rootElementInjectors.length; ++i) {
view.rootElementInjectors[i].unlink();
}
}
attachViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
contextView: viewModule.AppView, contextBoundElementIndex: number,
atIndex: number, view: viewModule.AppView) {
@ -118,11 +140,7 @@ export class AppViewManagerUtils {
contextBoundElementIndex = boundElementIndex;
}
parentView.changeDetector.addChild(view.changeDetector);
var viewContainer = parentView.viewContainers[boundElementIndex];
if (isBlank(viewContainer)) {
viewContainer = new viewModule.AppViewContainer();
parentView.viewContainers[boundElementIndex] = viewContainer;
}
var viewContainer = this._getOrCreateViewContainer(parentView, boundElementIndex);
ListWrapper.insert(viewContainer.views, atIndex, view);
var sibling;
if (atIndex == 0) {
@ -208,6 +226,15 @@ export class AppViewManagerUtils {
view.changeDetector.hydrate(view.context, view.locals, view);
}
_getOrCreateViewContainer(parentView: viewModule.AppView, boundElementIndex: number) {
var viewContainer = parentView.viewContainers[boundElementIndex];
if (isBlank(viewContainer)) {
viewContainer = new viewModule.AppViewContainer();
parentView.viewContainers[boundElementIndex] = viewContainer;
}
return viewContainer;
}
_setUpEventEmitters(view: viewModule.AppView, elementInjector: eli.ElementInjector,
boundElementIndex: number) {
var emitters = elementInjector.getEventEmitterAccessors();

View File

@ -237,9 +237,9 @@ export class Renderer {
}
/**
* Detaches a free host view's element from the DOM.
* Detaches a free view's element from the DOM.
*/
detachFreeHostView(parentHostViewRef: RenderViewRef, hostViewRef: RenderViewRef) {}
detachFreeView(view: RenderViewRef) {}
/**
* Creates a regular view out of the given ProtoView

View File

@ -47,9 +47,9 @@ export class DomRenderer extends Renderer {
return new DomViewRef(this._createView(hostProtoView, element));
}
detachFreeHostView(parentHostViewRef: RenderViewRef, hostViewRef: RenderViewRef) {
var hostView = resolveInternalDomView(hostViewRef);
this._removeViewNodes(hostView);
detachFreeView(viewRef: RenderViewRef) {
var view = resolveInternalDomView(viewRef);
this._removeViewNodes(view);
}
createView(protoViewRef: RenderProtoViewRef): RenderViewRef {
@ -83,9 +83,8 @@ export class DomRenderer extends Renderer {
this._moveViewNodesIntoParent(componentView.shadowRoot, componentView);
}
getHostElement(hostViewRef: RenderViewRef) {
var hostView = resolveInternalDomView(hostViewRef);
return hostView.boundElements[0];
getRootNodes(viewRef: RenderViewRef): List</*node*/ any> {
return resolveInternalDomView(viewRef).rootNodes;
}
detachComponentView(hostViewRef: RenderViewRef, boundElementIndex: number,