refactor(view): introduce AppViewManager to consolidate logic
AppViewManager is the single entry point to changing the view hierarchy. It is split between the manager itself which does coordination and helper methods, so both are easily testable in isolation. Also, ViewContainer is now only a pure reference to a bound element with the previous functionality but does not contain the list of views any more. Part of #1477
This commit is contained in:
16
modules/angular2/src/core/application.js
vendored
16
modules/angular2/src/core/application.js
vendored
@ -28,8 +28,9 @@ import {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_res
|
||||
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 {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||
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';
|
||||
@ -105,12 +106,13 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||
ProtoViewFactory,
|
||||
// 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]
|
||||
bind(AppViewPool).toFactory(
|
||||
(capacity) => new AppViewPool(capacity),
|
||||
[APP_VIEW_POOL_CAPACITY]
|
||||
),
|
||||
bind(VIEW_POOL_CAPACITY).toValue(10000),
|
||||
AppViewHydrator,
|
||||
bind(APP_VIEW_POOL_CAPACITY).toValue(10000),
|
||||
AppViewManager,
|
||||
AppViewManagerUtils,
|
||||
Compiler,
|
||||
CompilerCache,
|
||||
TemplateResolver,
|
||||
|
13
modules/angular2/src/core/compiler/compiler.js
vendored
13
modules/angular2/src/core/compiler/compiler.js
vendored
@ -88,13 +88,17 @@ export class Compiler {
|
||||
// Create a hostView as if the compiler encountered <hostcmp></hostcmp>.
|
||||
// Used for bootstrapping.
|
||||
compileInHost(componentTypeOrBinding:any):Promise<AppProtoView> {
|
||||
var componentBinding = this._bindDirective(componentTypeOrBinding);
|
||||
this._assertTypeIsComponent(componentBinding);
|
||||
return this._renderer.createHostProtoView('host').then( (hostRenderPv) => {
|
||||
return this._compileNestedProtoViews(null, hostRenderPv, [this._bindDirective(componentTypeOrBinding)], true);
|
||||
return this._compileNestedProtoViews(null, hostRenderPv, [componentBinding], true);
|
||||
});
|
||||
}
|
||||
|
||||
compile(component: Type):Promise<AppProtoView> {
|
||||
var protoView = this._compile(this._bindDirective(component));
|
||||
var componentBinding = this._bindDirective(component);
|
||||
this._assertTypeIsComponent(componentBinding);
|
||||
var protoView = this._compile(componentBinding);
|
||||
return PromiseWrapper.isPromise(protoView) ? protoView : PromiseWrapper.resolve(protoView);
|
||||
}
|
||||
|
||||
@ -265,4 +269,9 @@ export class Compiler {
|
||||
}
|
||||
}
|
||||
|
||||
_assertTypeIsComponent(directiveBinding:DirectiveBinding):void {
|
||||
if (!(directiveBinding.annotation instanceof Component)) {
|
||||
throw new BaseException(`Could not load '${stringify(directiveBinding.key.token)}' because it is not a component.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,12 +1,9 @@
|
||||
import {Key, Injector, Injectable, ResolvedBinding} from 'angular2/di'
|
||||
import {Key, Injector, Injectable, ResolvedBinding, Binding, bind} from 'angular2/di'
|
||||
import {Compiler} from './compiler';
|
||||
import {DirectiveMetadataReader} from './directive_metadata_reader';
|
||||
import {Type, BaseException, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||
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 {AppViewHydrator} from 'angular2/src/core/compiler/view_hydrator';
|
||||
import {ElementRef, DirectiveBinding} from './element_injector';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
import {ElementRef} from './element_injector';
|
||||
import {AppView} from './view';
|
||||
|
||||
/**
|
||||
@ -47,31 +44,23 @@ export class ComponentRef {
|
||||
@Injectable()
|
||||
export class DynamicComponentLoader {
|
||||
_compiler:Compiler;
|
||||
_viewFactory:ViewFactory;
|
||||
_viewHydrator:AppViewHydrator;
|
||||
_directiveMetadataReader:DirectiveMetadataReader;
|
||||
_viewManager:AppViewManager;
|
||||
|
||||
constructor(compiler:Compiler, directiveMetadataReader:DirectiveMetadataReader,
|
||||
viewFactory:ViewFactory, viewHydrator:AppViewHydrator) {
|
||||
constructor(compiler:Compiler,
|
||||
viewManager: AppViewManager) {
|
||||
this._compiler = compiler;
|
||||
this._directiveMetadataReader = directiveMetadataReader;
|
||||
this._viewFactory = viewFactory;
|
||||
this._viewHydrator = viewHydrator;
|
||||
this._viewManager = viewManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a component into the location given by the provided ElementRef. The loaded component
|
||||
* receives injection as if it in the place of the provided ElementRef.
|
||||
*/
|
||||
loadIntoExistingLocation(type:Type, location:ElementRef, injector:Injector = null):Promise<ComponentRef> {
|
||||
this._assertTypeIsComponent(type);
|
||||
var annotation = this._directiveMetadataReader.read(type).annotation;
|
||||
var componentBinding = DirectiveBinding.createFromType(type, annotation);
|
||||
|
||||
return this._compiler.compile(type).then(componentProtoView => {
|
||||
var componentView = this._viewFactory.getView(componentProtoView);
|
||||
this._viewHydrator.hydrateDynamicComponentView(
|
||||
location, componentView, componentBinding, injector);
|
||||
loadIntoExistingLocation(typeOrBinding, location:ElementRef, injector:Injector = null):Promise<ComponentRef> {
|
||||
var binding = this._getBinding(typeOrBinding);
|
||||
return this._compiler.compile(binding.token).then(componentProtoView => {
|
||||
var componentView = this._viewManager.createDynamicComponentView(
|
||||
location, componentProtoView, binding, injector);
|
||||
|
||||
var dispose = () => {throw new BaseException("Not implemented");};
|
||||
return new ComponentRef(location, location.elementInjector.getDynamicallyLoadedComponent(), componentView, dispose);
|
||||
@ -82,21 +71,16 @@ export class DynamicComponentLoader {
|
||||
* Loads a component in the element specified by elementOrSelector. The loaded component receives
|
||||
* injection normally as a hosted view.
|
||||
*/
|
||||
loadIntoNewLocation(type:Type, parentComponentLocation:ElementRef, elementOrSelector:any,
|
||||
loadIntoNewLocation(typeOrBinding, parentComponentLocation:ElementRef, elementOrSelector:any,
|
||||
injector:Injector = null):Promise<ComponentRef> {
|
||||
this._assertTypeIsComponent(type);
|
||||
|
||||
return this._compiler.compileInHost(type).then(hostProtoView => {
|
||||
var hostView = this._viewFactory.getView(hostProtoView);
|
||||
this._viewHydrator.hydrateInPlaceHostView(
|
||||
parentComponentLocation, elementOrSelector, hostView, injector
|
||||
);
|
||||
return this._compiler.compileInHost(this._getBinding(typeOrBinding)).then(hostProtoView => {
|
||||
var hostView = this._viewManager.createInPlaceHostView(
|
||||
parentComponentLocation, elementOrSelector, hostProtoView, injector);
|
||||
|
||||
var newLocation = hostView.elementInjectors[0].getElementRef();
|
||||
var component = hostView.elementInjectors[0].getComponent();
|
||||
var dispose = () => {
|
||||
this._viewHydrator.dehydrateInPlaceHostView(parentComponentLocation, hostView);
|
||||
this._viewFactory.returnView(hostView);
|
||||
this._viewManager.destroyInPlaceHostView(parentComponentLocation, hostView);
|
||||
};
|
||||
return new ComponentRef(newLocation, component, hostView.componentChildViews[0], dispose);
|
||||
});
|
||||
@ -106,10 +90,9 @@ export class DynamicComponentLoader {
|
||||
* Loads a component next to the provided ElementRef. The loaded component receives
|
||||
* injection normally as a hosted view.
|
||||
*/
|
||||
loadNextToExistingLocation(type:Type, location:ElementRef, injector:Injector = null):Promise<ComponentRef> {
|
||||
this._assertTypeIsComponent(type);
|
||||
|
||||
return this._compiler.compileInHost(type).then(hostProtoView => {
|
||||
loadNextToExistingLocation(typeOrBinding, location:ElementRef, injector:Injector = null):Promise<ComponentRef> {
|
||||
var binding = this._getBinding(typeOrBinding);
|
||||
return this._compiler.compileInHost(binding).then(hostProtoView => {
|
||||
var hostView = location.viewContainer.create(-1, hostProtoView, injector);
|
||||
|
||||
var newLocation = hostView.elementInjectors[0].getElementRef();
|
||||
@ -122,11 +105,14 @@ export class DynamicComponentLoader {
|
||||
});
|
||||
}
|
||||
|
||||
/** Asserts that the type being dynamically instantiated is a Component. */
|
||||
_assertTypeIsComponent(type:Type):void {
|
||||
var annotation = this._directiveMetadataReader.read(type).annotation;
|
||||
if (!(annotation instanceof Component)) {
|
||||
throw new BaseException(`Could not load '${stringify(type)}' because it is not a component.`);
|
||||
_getBinding(typeOrBinding) {
|
||||
var binding;
|
||||
if (typeOrBinding instanceof Binding) {
|
||||
binding = typeOrBinding;
|
||||
} else {
|
||||
binding = bind(typeOrBinding).toClass(typeOrBinding);
|
||||
}
|
||||
return binding;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import {Injector, Key, Dependency, bind, Binding, ResolvedBinding, NoBindingErro
|
||||
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
|
||||
import {Attribute, Query} from 'angular2/src/core/annotations/di';
|
||||
import * as viewModule from 'angular2/src/core/compiler/view';
|
||||
import * as avmModule from './view_manager';
|
||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||
import {NgElement} from 'angular2/src/core/compiler/ng_element';
|
||||
import {Directive, Component, onChange, onDestroy, onAllChangesDone} from 'angular2/src/core/annotations/annotations';
|
||||
@ -30,28 +31,30 @@ export class ElementRef {
|
||||
boundElementIndex:number;
|
||||
injector:Injector;
|
||||
elementInjector:ElementInjector;
|
||||
viewContainer:ViewContainer;
|
||||
|
||||
constructor(elementInjector, hostView, boundElementIndex, injector){
|
||||
constructor(elementInjector, hostView, boundElementIndex, injector, viewManager, defaultProtoView){
|
||||
this.elementInjector = elementInjector;
|
||||
this.hostView = hostView;
|
||||
this.boundElementIndex = boundElementIndex;
|
||||
this.injector = injector;
|
||||
}
|
||||
|
||||
get viewContainer() {
|
||||
return this.hostView.getOrCreateViewContainer(this.boundElementIndex);
|
||||
this.viewContainer = new ViewContainer(viewManager, this, defaultProtoView);
|
||||
}
|
||||
}
|
||||
|
||||
class StaticKeys {
|
||||
viewManagerId:number;
|
||||
viewId:number;
|
||||
ngElementId:number;
|
||||
defaultProtoViewId:number;
|
||||
viewContainerId:number;
|
||||
changeDetectorRefId:number;
|
||||
elementRefId:number;
|
||||
|
||||
constructor() {
|
||||
//TODO: vsavkin Key.annotate(Key.get(AppView), 'static')
|
||||
this.viewManagerId = Key.get(avmModule.AppViewManager).id;
|
||||
this.defaultProtoViewId = Key.get(viewModule.AppProtoView).id;
|
||||
this.viewId = Key.get(viewModule.AppView).id;
|
||||
this.ngElementId = Key.get(NgElement).id;
|
||||
this.viewContainerId = Key.get(ViewContainer).id;
|
||||
@ -290,13 +293,15 @@ export class DirectiveBinding extends ResolvedBinding {
|
||||
|
||||
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
|
||||
export class PreBuiltObjects {
|
||||
viewManager:avmModule.AppViewManager;
|
||||
defaultProtoView:viewModule.AppProtoView;
|
||||
view:viewModule.AppView;
|
||||
element:NgElement;
|
||||
changeDetector:ChangeDetector;
|
||||
constructor(view, element:NgElement, changeDetector:ChangeDetector) {
|
||||
constructor(viewManager:avmModule.AppViewManager, view:viewModule.AppView, element:NgElement, defaultProtoView:viewModule.AppProtoView) {
|
||||
this.viewManager = viewManager;
|
||||
this.view = view;
|
||||
this.defaultProtoView = defaultProtoView;
|
||||
this.element = element;
|
||||
this.changeDetector = changeDetector;
|
||||
}
|
||||
}
|
||||
|
||||
@ -649,10 +654,6 @@ export class ElementInjector extends TreeNode {
|
||||
return this._preBuiltObjects.element;
|
||||
}
|
||||
|
||||
getChangeDetector() {
|
||||
return this._preBuiltObjects.changeDetector;
|
||||
}
|
||||
|
||||
getComponent() {
|
||||
if (this._proto._binding0IsComponent) {
|
||||
return this._obj0;
|
||||
@ -662,7 +663,8 @@ export class ElementInjector extends TreeNode {
|
||||
}
|
||||
|
||||
getElementRef() {
|
||||
return new ElementRef(this, this._preBuiltObjects.view, this._proto.index, this._lightDomAppInjector);
|
||||
return new ElementRef(this, this._preBuiltObjects.view, this._proto.index, this._lightDomAppInjector,
|
||||
this._preBuiltObjects.viewManager, this._preBuiltObjects.defaultProtoView);
|
||||
}
|
||||
|
||||
getDynamicallyLoadedComponent() {
|
||||
@ -732,9 +734,16 @@ export class ElementInjector extends TreeNode {
|
||||
_getByDependency(dep:DirectiveDependency, requestor:Key) {
|
||||
if (isPresent(dep.attributeName)) return this._buildAttribute(dep);
|
||||
if (isPresent(dep.queryDirective)) return this._findQuery(dep.queryDirective).list;
|
||||
if (dep.key.id === StaticKeys.instance().changeDetectorRefId) {
|
||||
var componentView = this._preBuiltObjects.view.componentChildViews[this._proto.index];
|
||||
return componentView.changeDetector.ref;
|
||||
}
|
||||
if (dep.key.id === StaticKeys.instance().elementRefId) {
|
||||
return this.getElementRef();
|
||||
}
|
||||
if (dep.key.id === StaticKeys.instance().viewContainerId) {
|
||||
return this.getElementRef().viewContainer;
|
||||
}
|
||||
return this._getByKey(dep.key, dep.depth, dep.optional, requestor);
|
||||
}
|
||||
|
||||
@ -906,10 +915,10 @@ export class ElementInjector extends TreeNode {
|
||||
_getPreBuiltObjectByKeyId(keyId:int) {
|
||||
var staticKeys = StaticKeys.instance();
|
||||
// TODO: AppView should not be injectable. Remove it.
|
||||
if (keyId === staticKeys.viewManagerId) return this._preBuiltObjects.viewManagerId;
|
||||
if (keyId === staticKeys.viewId) return this._preBuiltObjects.view;
|
||||
if (keyId === staticKeys.ngElementId) return this._preBuiltObjects.element;
|
||||
if (keyId === staticKeys.viewContainerId) return this._preBuiltObjects.view.getOrCreateViewContainer(this._proto.index);
|
||||
if (keyId === staticKeys.changeDetectorRefId) return this._preBuiltObjects.changeDetector.ref;
|
||||
if (keyId === staticKeys.defaultProtoViewId) return this._preBuiltObjects.defaultProtoView;
|
||||
|
||||
//TODO add other objects as needed
|
||||
return _undefined;
|
||||
@ -968,6 +977,10 @@ export class ElementInjector extends TreeNode {
|
||||
return this._lightDomAppInjector;
|
||||
}
|
||||
|
||||
getShadowDomAppInjector() {
|
||||
return this._shadowDomAppInjector;
|
||||
}
|
||||
|
||||
getHost() {
|
||||
return this._host;
|
||||
}
|
||||
|
35
modules/angular2/src/core/compiler/view.js
vendored
35
modules/angular2/src/core/compiler/view.js
vendored
@ -6,10 +6,18 @@ import {ProtoElementInjector, ElementInjector, PreBuiltObjects, DirectiveBinding
|
||||
import {ElementBinder} from './element_binder';
|
||||
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';
|
||||
|
||||
// TODO(tbosch): rename ViewContainer -> ViewContainerRef
|
||||
// and InternalAppViewContainer -> ViewContainer!
|
||||
export class InternalAppViewContainer {
|
||||
views: List<AppView>;
|
||||
|
||||
constructor() {
|
||||
// The order in this list matches the DOM order.
|
||||
this.views = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Const of making objects: http://jsperf.com/instantiate-size-of-object
|
||||
@ -28,12 +36,10 @@ export class AppView {
|
||||
/// Host views that were added by an imperative view.
|
||||
/// This is a dynamically growing / shrinking array.
|
||||
imperativeHostViews: List<AppView>;
|
||||
viewContainers: List<ViewContainer>;
|
||||
viewContainers: List<InternalAppViewContainer>;
|
||||
preBuiltObjects: List<PreBuiltObjects>;
|
||||
proto: AppProtoView;
|
||||
renderer: renderApi.Renderer;
|
||||
viewFactory: vfModule.ViewFactory;
|
||||
viewHydrator: vhModule.AppViewHydrator;
|
||||
|
||||
/**
|
||||
* The context against which data-binding expressions in this view are evaluated against.
|
||||
@ -49,7 +55,7 @@ export class AppView {
|
||||
*/
|
||||
locals:Locals;
|
||||
|
||||
constructor(renderer:renderApi.Renderer, viewFactory:vfModule.ViewFactory, proto:AppProtoView, protoLocals:Map) {
|
||||
constructor(renderer:renderApi.Renderer, proto:AppProtoView, protoLocals:Map) {
|
||||
this.render = null;
|
||||
this.proto = proto;
|
||||
this.changeDetector = null;
|
||||
@ -61,8 +67,6 @@ export class AppView {
|
||||
this.context = null;
|
||||
this.locals = new Locals(null, MapWrapper.clone(protoLocals)); //TODO optimize this
|
||||
this.renderer = renderer;
|
||||
this.viewFactory = viewFactory;
|
||||
this.viewHydrator = null;
|
||||
this.imperativeHostViews = [];
|
||||
}
|
||||
|
||||
@ -75,15 +79,6 @@ export class AppView {
|
||||
this.componentChildViews = componentChildViews;
|
||||
}
|
||||
|
||||
getOrCreateViewContainer(boundElementIndex:number):ViewContainer {
|
||||
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):void {
|
||||
if (!this.hydrated()) throw new BaseException('Cannot set locals on dehydrated view.');
|
||||
if (!MapWrapper.contains(this.proto.variableBindings, contextName)) {
|
||||
@ -130,8 +125,8 @@ export class AppView {
|
||||
}
|
||||
|
||||
getDetectorFor(directive:DirectiveIndex) {
|
||||
var elementInjector = this.elementInjectors[directive.elementIndex];
|
||||
return elementInjector.getChangeDetector();
|
||||
var childView = this.componentChildViews[directive.elementIndex];
|
||||
return isPresent(childView) ? childView.changeDetector : null;
|
||||
}
|
||||
|
||||
// implementation of EventDispatcher#dispatchEvent
|
||||
|
114
modules/angular2/src/core/compiler/view_container.js
vendored
114
modules/angular2/src/core/compiler/view_container.js
vendored
@ -1,108 +1,68 @@
|
||||
import {ListWrapper, MapWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {BaseException} from 'angular2/src/facade/lang';
|
||||
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 viewModule from './view';
|
||||
import {ViewContainerRef} from 'angular2/src/render/api';
|
||||
import * as avmModule from './view_manager';
|
||||
|
||||
/**
|
||||
* @exportedAs angular2/view
|
||||
*/
|
||||
export class ViewContainer {
|
||||
parentView: viewModule.AppView;
|
||||
defaultProtoView: viewModule.AppProtoView;
|
||||
_views: List<viewModule.AppView>;
|
||||
elementInjector: eiModule.ElementInjector;
|
||||
_viewManager: avmModule.AppViewManager;
|
||||
_location: eiModule.ElementRef;
|
||||
_defaultProtoView: viewModule.AppProtoView;
|
||||
|
||||
constructor(parentView: viewModule.AppView,
|
||||
defaultProtoView: viewModule.AppProtoView,
|
||||
elementInjector: eiModule.ElementInjector) {
|
||||
this.parentView = parentView;
|
||||
this.defaultProtoView = defaultProtoView;
|
||||
this.elementInjector = elementInjector;
|
||||
|
||||
// The order in this list matches the DOM order.
|
||||
this._views = [];
|
||||
constructor(viewManager: avmModule.AppViewManager,
|
||||
location: eiModule.ElementRef,
|
||||
defaultProtoView: viewModule.AppProtoView) {
|
||||
this._viewManager = viewManager;
|
||||
this._location = location;
|
||||
this._defaultProtoView = defaultProtoView;
|
||||
}
|
||||
|
||||
getRender():ViewContainerRef {
|
||||
return new ViewContainerRef(this.parentView.render, this.elementInjector.getBoundElementIndex());
|
||||
}
|
||||
|
||||
internalClearWithoutRender():void {
|
||||
for (var i = this._views.length - 1; i >= 0; i--) {
|
||||
this._detachInjectors(i);
|
||||
}
|
||||
_getViews() {
|
||||
var vc = this._location.hostView.viewContainers[this._location.boundElementIndex];
|
||||
return isPresent(vc) ? vc.views : [];
|
||||
}
|
||||
|
||||
clear():void {
|
||||
for (var i = this._views.length - 1; i >= 0; i--) {
|
||||
for (var i = this.length - 1; i >= 0; i--) {
|
||||
this.remove(i);
|
||||
}
|
||||
}
|
||||
|
||||
get(index: number): viewModule.AppView {
|
||||
return this._views[index];
|
||||
return this._getViews()[index];
|
||||
}
|
||||
|
||||
get length() /* :int */ {
|
||||
return this._views.length;
|
||||
}
|
||||
|
||||
_siblingInjectorToLinkAfter(index: number):eiModule.ElementInjector {
|
||||
if (index == 0) return null;
|
||||
return ListWrapper.last(this._views[index - 1].rootElementInjectors)
|
||||
}
|
||||
|
||||
hydrated():boolean {
|
||||
return this.parentView.hydrated();
|
||||
return this._getViews().length;
|
||||
}
|
||||
|
||||
// TODO(rado): profile and decide whether bounds checks should be added
|
||||
// to the methods below.
|
||||
create(atIndex:number=-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 (atIndex == -1) atIndex = this.length;
|
||||
if (isBlank(protoView)) {
|
||||
protoView = this.defaultProtoView;
|
||||
protoView = this._defaultProtoView;
|
||||
}
|
||||
var newView = this.parentView.viewFactory.getView(protoView);
|
||||
// insertion must come before hydration so that element injector trees are attached.
|
||||
this._insertInjectors(newView, atIndex);
|
||||
this.parentView.viewHydrator.hydrateViewInViewContainer(this, atIndex, newView, injector);
|
||||
|
||||
return newView;
|
||||
return this._viewManager.createViewInContainer(this._location, atIndex, protoView, injector);
|
||||
}
|
||||
|
||||
insert(view:viewModule.AppView, atIndex:number=-1): viewModule.AppView {
|
||||
if (atIndex == -1) atIndex = this._views.length;
|
||||
this._insertInjectors(view, atIndex);
|
||||
this.parentView.changeDetector.addChild(view.changeDetector);
|
||||
this.parentView.renderer.insertViewIntoContainer(this.getRender(), atIndex, view.render);
|
||||
return view;
|
||||
}
|
||||
|
||||
_insertInjectors(view:viewModule.AppView, atIndex:number): viewModule.AppView {
|
||||
ListWrapper.insert(this._views, atIndex, view);
|
||||
this._linkElementInjectors(this._siblingInjectorToLinkAfter(atIndex), view);
|
||||
|
||||
return view;
|
||||
if (atIndex == -1) atIndex = this.length;
|
||||
return this._viewManager.attachViewInContainer(this._location, atIndex, view);
|
||||
}
|
||||
|
||||
indexOf(view:viewModule.AppView) {
|
||||
return ListWrapper.indexOf(this._views, view);
|
||||
return ListWrapper.indexOf(this._getViews(), view);
|
||||
}
|
||||
|
||||
remove(atIndex:number=-1):void {
|
||||
if (atIndex == -1) atIndex = this._views.length - 1;
|
||||
var view = this._views[atIndex];
|
||||
// opposite order as in create
|
||||
this.parentView.viewHydrator.dehydrateViewInViewContainer(this, atIndex, view);
|
||||
this._detachInjectors(atIndex);
|
||||
this.parentView.viewFactory.returnView(view);
|
||||
if (atIndex == -1) atIndex = this.length - 1;
|
||||
this._viewManager.destroyViewInContainer(this._location, atIndex);
|
||||
// view is intentionally not returned to the client.
|
||||
}
|
||||
|
||||
@ -111,29 +71,7 @@ export class ViewContainer {
|
||||
* moving the dom nodes while the directives in the view stay intact.
|
||||
*/
|
||||
detach(atIndex:number=-1): viewModule.AppView {
|
||||
if (atIndex == -1) atIndex = this._views.length - 1;
|
||||
var detachedView = this._detachInjectors(atIndex);
|
||||
detachedView.changeDetector.remove();
|
||||
this.parentView.renderer.detachViewFromContainer(this.getRender(), atIndex);
|
||||
return detachedView;
|
||||
}
|
||||
|
||||
_detachInjectors(atIndex:number): viewModule.AppView {
|
||||
var detachedView = this.get(atIndex);
|
||||
ListWrapper.removeAt(this._views, atIndex);
|
||||
this._unlinkElementInjectors(detachedView);
|
||||
return detachedView;
|
||||
}
|
||||
|
||||
_linkElementInjectors(sibling, view:viewModule.AppView):void {
|
||||
for (var i = view.rootElementInjectors.length - 1; i >= 0; i--) {
|
||||
view.rootElementInjectors[i].linkAfter(this.elementInjector, sibling);
|
||||
}
|
||||
}
|
||||
|
||||
_unlinkElementInjectors(view:viewModule.AppView):void {
|
||||
for (var i = 0; i < view.rootElementInjectors.length; ++i) {
|
||||
view.rootElementInjectors[i].unlink();
|
||||
}
|
||||
if (atIndex == -1) atIndex = this.length - 1;
|
||||
return this._viewManager.detachViewInContainer(this._location, atIndex);
|
||||
}
|
||||
}
|
||||
|
@ -1,97 +0,0 @@
|
||||
import {Injectable, Inject, OpaqueToken} 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 {NgElement} from 'angular2/src/core/compiler/ng_element';
|
||||
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';
|
||||
|
||||
@Injectable()
|
||||
export class ViewFactory {
|
||||
_poolCapacityPerProtoView:number;
|
||||
_pooledViewsPerProtoView:Map<viewModule.AppProtoView, List<viewModule.AppView>>;
|
||||
_renderer:Renderer;
|
||||
|
||||
constructor(@Inject(VIEW_POOL_CAPACITY) poolCapacityPerProtoView, renderer:Renderer) {
|
||||
this._poolCapacityPerProtoView = poolCapacityPerProtoView;
|
||||
this._pooledViewsPerProtoView = MapWrapper.create();
|
||||
this._renderer = renderer;
|
||||
}
|
||||
|
||||
getView(protoView:viewModule.AppProtoView):viewModule.AppView {
|
||||
var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView);
|
||||
if (isPresent(pooledViews) && pooledViews.length > 0) {
|
||||
return ListWrapper.removeLast(pooledViews);
|
||||
}
|
||||
return this._createView(protoView);
|
||||
}
|
||||
|
||||
returnView(view:viewModule.AppView) {
|
||||
if (view.hydrated()) {
|
||||
throw new BaseException('Only dehydrated Views can be put back into the pool!');
|
||||
}
|
||||
var protoView = view.proto;
|
||||
var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView);
|
||||
if (isBlank(pooledViews)) {
|
||||
pooledViews = [];
|
||||
MapWrapper.set(this._pooledViewsPerProtoView, protoView, pooledViews);
|
||||
}
|
||||
if (pooledViews.length < this._poolCapacityPerProtoView) {
|
||||
ListWrapper.push(pooledViews, view);
|
||||
}
|
||||
}
|
||||
|
||||
_createView(protoView:viewModule.AppProtoView): viewModule.AppView {
|
||||
var view = new viewModule.AppView(this._renderer, this, protoView, protoView.protoLocals);
|
||||
var changeDetector = protoView.protoChangeDetector.instantiate(view, protoView.bindings,
|
||||
protoView.getVariableBindings(), protoView.getdirectiveRecords());
|
||||
|
||||
var binders = protoView.elementBinders;
|
||||
var elementInjectors = ListWrapper.createFixedSize(binders.length);
|
||||
var rootElementInjectors = [];
|
||||
var preBuiltObjects = ListWrapper.createFixedSize(binders.length);
|
||||
var componentChildViews = ListWrapper.createFixedSize(binders.length);
|
||||
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
var elementInjector = null;
|
||||
|
||||
// elementInjectors and rootElementInjectors
|
||||
var protoElementInjector = binder.protoElementInjector;
|
||||
if (isPresent(protoElementInjector)) {
|
||||
if (isPresent(protoElementInjector.parent)) {
|
||||
var parentElementInjector = elementInjectors[protoElementInjector.parent.index];
|
||||
elementInjector = protoElementInjector.instantiate(parentElementInjector);
|
||||
} else {
|
||||
elementInjector = protoElementInjector.instantiate(null);
|
||||
ListWrapper.push(rootElementInjectors, elementInjector);
|
||||
}
|
||||
}
|
||||
elementInjectors[binderIdx] = elementInjector;
|
||||
|
||||
// componentChildViews
|
||||
var childChangeDetector = null;
|
||||
if (binder.hasStaticComponent()) {
|
||||
var childView = this._createView(binder.nestedProtoView);
|
||||
childChangeDetector = childView.changeDetector;
|
||||
changeDetector.addShadowDomChild(childChangeDetector);
|
||||
|
||||
componentChildViews[binderIdx] = childView;
|
||||
}
|
||||
|
||||
// preBuiltObjects
|
||||
if (isPresent(elementInjector)) {
|
||||
preBuiltObjects[binderIdx] = new eli.PreBuiltObjects(view, new NgElement(view, binderIdx), childChangeDetector);
|
||||
}
|
||||
}
|
||||
|
||||
view.init(changeDetector, elementInjectors, rootElementInjectors,
|
||||
preBuiltObjects, componentChildViews);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
}
|
302
modules/angular2/src/core/compiler/view_hydrator.js
vendored
302
modules/angular2/src/core/compiler/view_hydrator.js
vendored
@ -1,302 +0,0 @@
|
||||
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';
|
||||
import {ViewFactory} from 'angular2/src/core/compiler/view_factory';
|
||||
|
||||
/**
|
||||
* 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;
|
||||
_viewFactory:ViewFactory;
|
||||
|
||||
constructor(renderer:renderApi.Renderer, viewFactory:ViewFactory) {
|
||||
this._renderer = renderer;
|
||||
this._viewFactory = viewFactory;
|
||||
}
|
||||
|
||||
hydrateDynamicComponentView(location:eli.ElementRef,
|
||||
componentView:viewModule.AppView, componentDirective:eli.DirectiveBinding, injector:Injector) {
|
||||
var hostView = location.hostView;
|
||||
var boundElementIndex = location.boundElementIndex;
|
||||
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)) {
|
||||
injector = hostElementInjector.getLightDomAppInjector();
|
||||
}
|
||||
|
||||
// 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(parentComponentLocation:eli.ElementRef,
|
||||
hostElementSelector, hostView:viewModule.AppView, injector:Injector) {
|
||||
var parentRenderViewRef = null;
|
||||
if (isPresent(parentComponentLocation)) {
|
||||
var parentView = parentComponentLocation.hostView.componentChildViews[parentComponentLocation.boundElementIndex];
|
||||
parentRenderViewRef = parentView.render;
|
||||
parentView.changeDetector.addChild(hostView.changeDetector);
|
||||
ListWrapper.push(parentView.imperativeHostViews, hostView);
|
||||
|
||||
if (isBlank(injector)) {
|
||||
injector = parentComponentLocation.injector;
|
||||
}
|
||||
}
|
||||
|
||||
var binder = hostView.proto.elementBinders[0];
|
||||
var shadowDomAppInjector = this._createShadowDomAppInjector(binder.componentDirective, injector);
|
||||
|
||||
var renderViewRefs = this._renderer.createInPlaceHostView(parentRenderViewRef, hostElementSelector, hostView.proto.render);
|
||||
|
||||
this._viewHydrateRecurse(
|
||||
hostView, renderViewRefs, 0, shadowDomAppInjector, null, new Object(), null
|
||||
);
|
||||
}
|
||||
|
||||
dehydrateInPlaceHostView(parentComponentLocation:eli.ElementRef, hostView:viewModule.AppView) {
|
||||
var parentRenderViewRef = null;
|
||||
if (isPresent(parentComponentLocation)) {
|
||||
var parentView = parentComponentLocation.hostView.componentChildViews[parentComponentLocation.boundElementIndex];
|
||||
parentRenderViewRef = parentView.render;
|
||||
ListWrapper.remove(parentView.imperativeHostViews, hostView);
|
||||
parentView.changeDetector.removeChild(hostView.changeDetector);
|
||||
}
|
||||
var render = hostView.render;
|
||||
this._viewDehydrateRecurse(hostView);
|
||||
this._renderer.destroyInPlaceHostView(parentRenderViewRef, render);
|
||||
}
|
||||
|
||||
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');
|
||||
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, 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.getRender(), atIndex);
|
||||
}
|
||||
|
||||
_viewHydrateRecurse(
|
||||
view:viewModule.AppView,
|
||||
renderComponentViewRefs:List<renderApi.ViewRef>,
|
||||
renderComponentIndex:number,
|
||||
appInjector: Injector, hostElementInjector: eli.ElementInjector,
|
||||
context: Object, locals:Locals):number {
|
||||
if (view.hydrated()) throw new BaseException('The view is already hydrated.');
|
||||
view.viewHydrator = this;
|
||||
view.render = renderComponentViewRefs[renderComponentIndex++];
|
||||
|
||||
view.context = context;
|
||||
view.locals.parent = locals;
|
||||
|
||||
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]);
|
||||
this._setUpEventEmitters(view, elementInjector, 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().domElement);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
_setUpEventEmitters(view:viewModule.AppView, elementInjector:eli.ElementInjector, boundElementIndex:number) {
|
||||
var emitters = elementInjector.getEventEmitterAccessors();
|
||||
for(var directiveIndex = 0; directiveIndex < emitters.length; ++directiveIndex) {
|
||||
var directiveEmitters = emitters[directiveIndex];
|
||||
var directive = elementInjector.getDirectiveAtIndex(directiveIndex);
|
||||
|
||||
for (var eventIndex = 0; eventIndex < directiveEmitters.length; ++eventIndex) {
|
||||
var eventEmitterAccessor = directiveEmitters[eventIndex];
|
||||
eventEmitterAccessor.subscribe(view, boundElementIndex, directive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.changeDetector.removeShadowDomChild(componentView.changeDetector);
|
||||
view.componentChildViews[i] = null;
|
||||
this._viewFactory.returnView(componentView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// imperativeHostViews
|
||||
for (var i = 0; i < view.imperativeHostViews.length; i++) {
|
||||
var hostView = view.imperativeHostViews[i];
|
||||
this._viewDehydrateRecurse(hostView);
|
||||
view.changeDetector.removeChild(hostView.changeDetector);
|
||||
this._viewFactory.returnView(hostView);
|
||||
}
|
||||
view.imperativeHostViews = [];
|
||||
|
||||
// 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.
|
||||
*/
|
||||
_viewContainerDehydrateRecurse(viewContainer:vcModule.ViewContainer) {
|
||||
for (var i=0; i<viewContainer.length; i++) {
|
||||
var view = viewContainer.get(i);
|
||||
view.changeDetector.remove();
|
||||
this._viewDehydrateRecurse(view);
|
||||
}
|
||||
// Note: We don't call clear here,
|
||||
// as we don't want to change the render side
|
||||
// as the render side does its own recursion.
|
||||
viewContainer.internalClearWithoutRender();
|
||||
}
|
||||
|
||||
}
|
201
modules/angular2/src/core/compiler/view_manager.js
vendored
Normal file
201
modules/angular2/src/core/compiler/view_manager.js
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
import {Injector, Injectable, Binding} from 'angular2/di';
|
||||
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import * as eli from './element_injector';
|
||||
import * as viewModule from './view';
|
||||
import {Renderer, ViewRef, ViewContainerRef} from 'angular2/src/render/api';
|
||||
import {AppViewManagerUtils} from './view_manager_utils';
|
||||
import {AppViewPool} from './view_pool';
|
||||
|
||||
/**
|
||||
* Entry point for creating, moving views in the view hierarchy and destroying views.
|
||||
* This manager contains all recursion and delegates to helper methods
|
||||
* in AppViewManagerUtils and the Renderer, so unit tests get simpler.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AppViewManager {
|
||||
_viewPool:AppViewPool;
|
||||
_utils:AppViewManagerUtils;
|
||||
_renderer:Renderer;
|
||||
|
||||
constructor(viewPool:AppViewPool, utils:AppViewManagerUtils, renderer:Renderer) {
|
||||
this._renderer = renderer;
|
||||
this._viewPool = viewPool;
|
||||
this._utils = utils;
|
||||
}
|
||||
|
||||
createDynamicComponentView(hostLocation:eli.ElementRef,
|
||||
componentProtoView:viewModule.AppProtoView, componentBinding:Binding, injector:Injector):viewModule.AppView {
|
||||
var hostView = hostLocation.hostView;
|
||||
var boundElementIndex = hostLocation.boundElementIndex;
|
||||
var binder = hostView.proto.elementBinders[boundElementIndex];
|
||||
if (!binder.hasDynamicComponent()) {
|
||||
throw new BaseException(`There is no dynamic component directive at element ${boundElementIndex}`)
|
||||
}
|
||||
|
||||
var componentView = this._createViewRecurse(componentProtoView);
|
||||
var renderViewRefs = this._renderer.createDynamicComponentView(hostView.render, boundElementIndex, componentProtoView.render);
|
||||
componentView.render = renderViewRefs[0];
|
||||
this._utils.attachComponentView(hostView, boundElementIndex, componentView);
|
||||
this._utils.hydrateDynamicComponentInElementInjector(hostView, boundElementIndex, componentBinding, injector);
|
||||
this._utils.hydrateComponentView(hostView, boundElementIndex);
|
||||
|
||||
this._viewHydrateRecurse(componentView, renderViewRefs, 1);
|
||||
return componentView;
|
||||
}
|
||||
|
||||
createInPlaceHostView(parentComponentLocation:eli.ElementRef,
|
||||
hostElementSelector, hostProtoView:viewModule.AppProtoView, injector:Injector):viewModule.AppView {
|
||||
var parentComponentHostView = null;
|
||||
var parentComponentBoundElementIndex = null;
|
||||
var parentRenderViewRef = null;
|
||||
if (isPresent(parentComponentLocation)) {
|
||||
parentComponentHostView = parentComponentLocation.hostView;
|
||||
parentComponentBoundElementIndex = parentComponentLocation.boundElementIndex;
|
||||
parentRenderViewRef = parentComponentHostView.componentChildViews[parentComponentBoundElementIndex].render;
|
||||
}
|
||||
var hostView = this._createViewRecurse(hostProtoView);
|
||||
var renderViewRefs = this._renderer.createInPlaceHostView(parentRenderViewRef, hostElementSelector, hostView.proto.render);
|
||||
hostView.render = renderViewRefs[0];
|
||||
this._utils.attachAndHydrateInPlaceHostView(parentComponentHostView, parentComponentBoundElementIndex, hostView, injector);
|
||||
this._viewHydrateRecurse(hostView, renderViewRefs, 1);
|
||||
return hostView;
|
||||
}
|
||||
|
||||
destroyInPlaceHostView(parentComponentLocation:eli.ElementRef, hostView:viewModule.AppView) {
|
||||
var parentView = null;
|
||||
var parentRenderViewRef = null;
|
||||
if (isPresent(parentComponentLocation)) {
|
||||
parentView = parentComponentLocation.hostView.componentChildViews[parentComponentLocation.boundElementIndex];
|
||||
parentRenderViewRef = parentView.render;
|
||||
}
|
||||
var hostViewRenderRef = hostView.render;
|
||||
this._utils.dehydrateAndDetachInPlaceHostView(parentView, hostView);
|
||||
this._viewDehydrateRecurse(hostView);
|
||||
this._renderer.destroyInPlaceHostView(parentRenderViewRef, hostViewRenderRef);
|
||||
this._destroyView(hostView);
|
||||
}
|
||||
|
||||
createViewInContainer(viewContainerLocation:eli.ElementRef,
|
||||
atIndex:number, protoView:viewModule.AppProtoView, injector:Injector = null):viewModule.AppView {
|
||||
var parentView = viewContainerLocation.hostView;
|
||||
var boundElementIndex:number = viewContainerLocation.boundElementIndex;
|
||||
var view = this._createViewRecurse(protoView);
|
||||
var renderViewRefs = this._renderer.createViewInContainer(this._getRenderViewContainerRef(parentView, boundElementIndex), atIndex, view.proto.render);
|
||||
view.render = renderViewRefs[0];
|
||||
|
||||
this._utils.attachViewInContainer(parentView, boundElementIndex, atIndex, view);
|
||||
this._utils.hydrateViewInContainer(parentView, boundElementIndex, atIndex, injector);
|
||||
this._viewHydrateRecurse(view, renderViewRefs, 1);
|
||||
return view;
|
||||
}
|
||||
|
||||
destroyViewInContainer(viewContainerLocation:eli.ElementRef, atIndex:number) {
|
||||
var parentView = viewContainerLocation.hostView;
|
||||
var boundElementIndex:number = viewContainerLocation.boundElementIndex;
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[atIndex];
|
||||
this._utils.dehydrateView(view);
|
||||
this._viewDehydrateRecurse(view);
|
||||
this._utils.detachViewInContainer(parentView, boundElementIndex, atIndex);
|
||||
this._renderer.destroyViewInContainer(this._getRenderViewContainerRef(parentView, boundElementIndex), atIndex);
|
||||
this._destroyView(view);
|
||||
}
|
||||
|
||||
attachViewInContainer(viewContainerLocation:eli.ElementRef, atIndex:number, view:viewModule.AppView):viewModule.AppView {
|
||||
var parentView = viewContainerLocation.hostView;
|
||||
var boundElementIndex:number = viewContainerLocation.boundElementIndex;
|
||||
this._utils.attachViewInContainer(parentView, boundElementIndex, atIndex, view);
|
||||
this._renderer.insertViewIntoContainer(this._getRenderViewContainerRef(parentView, boundElementIndex), atIndex, view.render);
|
||||
return view;
|
||||
}
|
||||
|
||||
detachViewInContainer(viewContainerLocation:eli.ElementRef, atIndex:number):viewModule.AppView {
|
||||
var parentView = viewContainerLocation.hostView;
|
||||
var boundElementIndex:number = viewContainerLocation.boundElementIndex;
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[atIndex];
|
||||
this._utils.detachViewInContainer(parentView, boundElementIndex, atIndex);
|
||||
this._renderer.detachViewFromContainer(this._getRenderViewContainerRef(parentView, boundElementIndex), atIndex);
|
||||
return view;
|
||||
}
|
||||
|
||||
_getRenderViewContainerRef(parentView:viewModule.AppView, boundElementIndex:number) {
|
||||
return new ViewContainerRef(parentView.render, boundElementIndex);
|
||||
}
|
||||
|
||||
_createViewRecurse(protoView:viewModule.AppProtoView) {
|
||||
var view = this._viewPool.getView(protoView);
|
||||
if (isBlank(view)) {
|
||||
view = this._utils.createView(protoView, this, this._renderer);
|
||||
var binders = protoView.elementBinders;
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
if (binder.hasStaticComponent()) {
|
||||
var childView = this._createViewRecurse(binder.nestedProtoView);
|
||||
this._utils.attachComponentView(view, binderIdx, childView);
|
||||
}
|
||||
}
|
||||
}
|
||||
return view;
|
||||
}
|
||||
|
||||
_destroyView(view:viewModule.AppView) {
|
||||
this._viewPool.returnView(view);
|
||||
}
|
||||
|
||||
_viewHydrateRecurse(
|
||||
view:viewModule.AppView,
|
||||
renderComponentViewRefs:List<ViewRef>,
|
||||
renderComponentIndex:number):number {
|
||||
this._renderer.setEventDispatcher(view.render, view);
|
||||
|
||||
var binders = view.proto.elementBinders;
|
||||
for (var i = 0; i < binders.length; ++i) {
|
||||
if (binders[i].hasStaticComponent()) {
|
||||
var childView = view.componentChildViews[i];
|
||||
childView.render = renderComponentViewRefs[renderComponentIndex++];
|
||||
this._utils.hydrateComponentView(view, i);
|
||||
renderComponentIndex = this._viewHydrateRecurse(
|
||||
view.componentChildViews[i],
|
||||
renderComponentViewRefs,
|
||||
renderComponentIndex
|
||||
);
|
||||
}
|
||||
}
|
||||
return renderComponentIndex;
|
||||
}
|
||||
|
||||
_viewDehydrateRecurse(view:viewModule.AppView) {
|
||||
var binders = view.proto.elementBinders;
|
||||
for (var i = 0; i < binders.length; i++) {
|
||||
var componentView = view.componentChildViews[i];
|
||||
if (isPresent(componentView)) {
|
||||
this._utils.dehydrateView(componentView);
|
||||
this._viewDehydrateRecurse(componentView);
|
||||
if (binders[i].hasDynamicComponent()) {
|
||||
this._utils.detachComponentView(view, i);
|
||||
this._destroyView(componentView);
|
||||
}
|
||||
}
|
||||
var vc = view.viewContainers[i];
|
||||
if (isPresent(vc)) {
|
||||
for (var j = vc.views.length - 1; j >= 0; j--) {
|
||||
var childView = vc.views[j];
|
||||
this._utils.dehydrateView(childView);
|
||||
this._utils.detachViewInContainer(view, i, j);
|
||||
this._destroyView(childView);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// imperativeHostViews
|
||||
for (var i = 0; i < view.imperativeHostViews.length; i++) {
|
||||
var hostView = view.imperativeHostViews[i];
|
||||
this._viewDehydrateRecurse(hostView);
|
||||
this._utils.dehydrateAndDetachInPlaceHostView(view, hostView);
|
||||
this._destroyView(hostView);
|
||||
}
|
||||
view.render = null;
|
||||
}
|
||||
}
|
243
modules/angular2/src/core/compiler/view_manager_utils.js
vendored
Normal file
243
modules/angular2/src/core/compiler/view_manager_utils.js
vendored
Normal file
@ -0,0 +1,243 @@
|
||||
import {Injectable, Injector, Binding} 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 {NgElement} from 'angular2/src/core/compiler/ng_element';
|
||||
import * as viewModule from './view';
|
||||
import * as avmModule from './view_manager';
|
||||
import {Renderer} from 'angular2/src/render/api';
|
||||
import {BindingPropagationConfig, Locals} from 'angular2/change_detection';
|
||||
import {DirectiveMetadataReader} from './directive_metadata_reader';
|
||||
|
||||
@Injectable()
|
||||
export class AppViewManagerUtils {
|
||||
_metadataReader:DirectiveMetadataReader;
|
||||
|
||||
constructor(metadataReader:DirectiveMetadataReader) {
|
||||
this._metadataReader = metadataReader;
|
||||
}
|
||||
|
||||
createView(protoView:viewModule.AppProtoView, viewManager:avmModule.AppViewManager, renderer:Renderer): viewModule.AppView {
|
||||
var view = new viewModule.AppView(renderer, protoView, protoView.protoLocals);
|
||||
var changeDetector = protoView.protoChangeDetector.instantiate(view, protoView.bindings,
|
||||
protoView.getVariableBindings(), protoView.getdirectiveRecords());
|
||||
|
||||
var binders = protoView.elementBinders;
|
||||
var elementInjectors = ListWrapper.createFixedSize(binders.length);
|
||||
var rootElementInjectors = [];
|
||||
var preBuiltObjects = ListWrapper.createFixedSize(binders.length);
|
||||
var componentChildViews = ListWrapper.createFixedSize(binders.length);
|
||||
|
||||
for (var binderIdx = 0; binderIdx < binders.length; binderIdx++) {
|
||||
var binder = binders[binderIdx];
|
||||
var elementInjector = null;
|
||||
|
||||
// elementInjectors and rootElementInjectors
|
||||
var protoElementInjector = binder.protoElementInjector;
|
||||
if (isPresent(protoElementInjector)) {
|
||||
if (isPresent(protoElementInjector.parent)) {
|
||||
var parentElementInjector = elementInjectors[protoElementInjector.parent.index];
|
||||
elementInjector = protoElementInjector.instantiate(parentElementInjector);
|
||||
} else {
|
||||
elementInjector = protoElementInjector.instantiate(null);
|
||||
ListWrapper.push(rootElementInjectors, elementInjector);
|
||||
}
|
||||
}
|
||||
elementInjectors[binderIdx] = elementInjector;
|
||||
|
||||
// preBuiltObjects
|
||||
if (isPresent(elementInjector)) {
|
||||
var defaultProtoView = isPresent(binder.viewportDirective) ? binder.nestedProtoView : null;
|
||||
preBuiltObjects[binderIdx] = new eli.PreBuiltObjects(viewManager, view, new NgElement(view, binderIdx), defaultProtoView);
|
||||
}
|
||||
}
|
||||
|
||||
view.init(changeDetector, elementInjectors, rootElementInjectors,
|
||||
preBuiltObjects, componentChildViews);
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
attachComponentView(hostView:viewModule.AppView, boundElementIndex:number,
|
||||
componentView:viewModule.AppView) {
|
||||
var childChangeDetector = componentView.changeDetector;
|
||||
hostView.changeDetector.addShadowDomChild(childChangeDetector);
|
||||
hostView.componentChildViews[boundElementIndex] = componentView;
|
||||
}
|
||||
|
||||
detachComponentView(hostView:viewModule.AppView, boundElementIndex:number) {
|
||||
var componentView = hostView.componentChildViews[boundElementIndex];
|
||||
hostView.changeDetector.removeShadowDomChild(componentView.changeDetector);
|
||||
hostView.componentChildViews[boundElementIndex] = null;
|
||||
}
|
||||
|
||||
hydrateComponentView(hostView:viewModule.AppView, boundElementIndex:number, injector:Injector = null) {
|
||||
var elementInjector = hostView.elementInjectors[boundElementIndex];
|
||||
var componentView = hostView.componentChildViews[boundElementIndex];
|
||||
var binder = hostView.proto.elementBinders[boundElementIndex];
|
||||
var component;
|
||||
if (binder.hasDynamicComponent()) {
|
||||
component = elementInjector.getDynamicallyLoadedComponent();
|
||||
} else {
|
||||
component = elementInjector.getComponent();
|
||||
}
|
||||
this._hydrateView(
|
||||
componentView, injector, elementInjector, component, null
|
||||
);
|
||||
}
|
||||
|
||||
attachAndHydrateInPlaceHostView(parentComponentHostView:viewModule.AppView, parentComponentBoundElementIndex:number,
|
||||
hostView:viewModule.AppView, injector:Injector = null) {
|
||||
var hostElementInjector = null;
|
||||
if (isPresent(parentComponentHostView)) {
|
||||
hostElementInjector = parentComponentHostView.elementInjectors[parentComponentBoundElementIndex];
|
||||
var parentView = parentComponentHostView.componentChildViews[parentComponentBoundElementIndex];
|
||||
parentView.changeDetector.addChild(hostView.changeDetector);
|
||||
ListWrapper.push(parentView.imperativeHostViews, hostView);
|
||||
}
|
||||
this._hydrateView(hostView, injector, hostElementInjector, new Object(), null);
|
||||
}
|
||||
|
||||
dehydrateAndDetachInPlaceHostView(parentView:viewModule.AppView,
|
||||
hostView:viewModule.AppView) {
|
||||
this.dehydrateView(hostView);
|
||||
|
||||
if (isPresent(parentView)) {
|
||||
parentView.changeDetector.removeChild(hostView.changeDetector);
|
||||
ListWrapper.remove(parentView.imperativeHostViews, hostView);
|
||||
}
|
||||
}
|
||||
|
||||
attachViewInContainer(parentView:viewModule.AppView, boundElementIndex:number, atIndex:number, view:viewModule.AppView) {
|
||||
parentView.changeDetector.addChild(view.changeDetector);
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
if (isBlank(viewContainer)) {
|
||||
viewContainer = new viewModule.InternalAppViewContainer();
|
||||
parentView.viewContainers[boundElementIndex] = viewContainer;
|
||||
}
|
||||
ListWrapper.insert(viewContainer.views, atIndex, view);
|
||||
var sibling;
|
||||
if (atIndex == 0) {
|
||||
sibling = null;
|
||||
} else {
|
||||
sibling = ListWrapper.last(viewContainer.views[atIndex - 1].rootElementInjectors)
|
||||
}
|
||||
var elementInjector = parentView.elementInjectors[boundElementIndex];
|
||||
for (var i = view.rootElementInjectors.length - 1; i >= 0; i--) {
|
||||
view.rootElementInjectors[i].linkAfter(elementInjector, sibling);
|
||||
}
|
||||
}
|
||||
|
||||
detachViewInContainer(parentView:viewModule.AppView, boundElementIndex:number, atIndex:number) {
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[atIndex];
|
||||
view.changeDetector.remove();
|
||||
ListWrapper.removeAt(viewContainer.views, atIndex);
|
||||
for (var i = 0; i < view.rootElementInjectors.length; ++i) {
|
||||
view.rootElementInjectors[i].unlink();
|
||||
}
|
||||
}
|
||||
|
||||
hydrateViewInContainer(parentView:viewModule.AppView, boundElementIndex:number, atIndex:number, injector:Injector) {
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[atIndex];
|
||||
var elementInjector = parentView.elementInjectors[boundElementIndex];
|
||||
this._hydrateView(view, injector, elementInjector, parentView.context, parentView.locals);
|
||||
}
|
||||
|
||||
hydrateDynamicComponentInElementInjector(hostView:viewModule.AppView, boundElementIndex:number,
|
||||
componentBinding:Binding, injector:Injector = null) {
|
||||
var elementInjector = hostView.elementInjectors[boundElementIndex];
|
||||
if (isPresent(elementInjector.getDynamicallyLoadedComponent())) {
|
||||
throw new BaseException(`There already is a dynamic component loaded at element ${boundElementIndex}`);
|
||||
}
|
||||
if (isBlank(injector)) {
|
||||
injector = elementInjector.getLightDomAppInjector();
|
||||
}
|
||||
var annotation = this._metadataReader.read(componentBinding.token).annotation;
|
||||
var componentDirective = eli.DirectiveBinding.createFromBinding(componentBinding, annotation);
|
||||
var shadowDomAppInjector = this._createShadowDomAppInjector(componentDirective, injector);
|
||||
elementInjector.dynamicallyCreateComponent(componentDirective, shadowDomAppInjector);
|
||||
}
|
||||
|
||||
_createShadowDomAppInjector(componentDirective, appInjector) {
|
||||
var shadowDomAppInjector = null;
|
||||
|
||||
// shadowDomAppInjector
|
||||
var injectables = componentDirective.resolvedInjectables;
|
||||
if (isPresent(injectables)) {
|
||||
shadowDomAppInjector = appInjector.createChildFromResolved(injectables);
|
||||
} else {
|
||||
shadowDomAppInjector = appInjector;
|
||||
}
|
||||
return shadowDomAppInjector;
|
||||
}
|
||||
|
||||
_hydrateView(view:viewModule.AppView, appInjector:Injector, hostElementInjector:eli.ElementInjector, context: Object, parentLocals:Locals) {
|
||||
if (isBlank(appInjector)) {
|
||||
appInjector = hostElementInjector.getShadowDomAppInjector();
|
||||
}
|
||||
if (isBlank(appInjector)) {
|
||||
appInjector = hostElementInjector.getLightDomAppInjector();
|
||||
}
|
||||
view.context = context;
|
||||
view.locals.parent = parentLocals;
|
||||
view.changeDetector.hydrate(view.context, view.locals, view);
|
||||
|
||||
var binders = view.proto.elementBinders;
|
||||
for (var i = 0; i < binders.length; ++i) {
|
||||
var elementInjector = view.elementInjectors[i];
|
||||
if (isPresent(elementInjector)) {
|
||||
var componentDirective = view.proto.elementBinders[i].componentDirective;
|
||||
var shadowDomAppInjector = null;
|
||||
if (isPresent(componentDirective)) {
|
||||
shadowDomAppInjector = this._createShadowDomAppInjector(componentDirective, appInjector);
|
||||
} else {
|
||||
shadowDomAppInjector = null;
|
||||
}
|
||||
elementInjector.instantiateDirectives(appInjector, hostElementInjector, shadowDomAppInjector, view.preBuiltObjects[i]);
|
||||
this._setUpEventEmitters(view, elementInjector, 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().domElement);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_setUpEventEmitters(view:viewModule.AppView, elementInjector:eli.ElementInjector, boundElementIndex:number) {
|
||||
var emitters = elementInjector.getEventEmitterAccessors();
|
||||
for (var directiveIndex = 0; directiveIndex < emitters.length; ++directiveIndex) {
|
||||
var directiveEmitters = emitters[directiveIndex];
|
||||
var directive = elementInjector.getDirectiveAtIndex(directiveIndex);
|
||||
|
||||
for (var eventIndex = 0; eventIndex < directiveEmitters.length; ++eventIndex) {
|
||||
var eventEmitterAccessor = directiveEmitters[eventIndex];
|
||||
eventEmitterAccessor.subscribe(view, boundElementIndex, directive);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dehydrateView(view:viewModule.AppView) {
|
||||
var binders = view.proto.elementBinders;
|
||||
for (var i = 0; i < binders.length; ++i) {
|
||||
var elementInjector = view.elementInjectors[i];
|
||||
if (isPresent(elementInjector)) {
|
||||
elementInjector.clearDirectives();
|
||||
}
|
||||
}
|
||||
if (isPresent(view.locals)) {
|
||||
view.locals.clearValues();
|
||||
}
|
||||
view.context = null;
|
||||
view.changeDetector.dehydrate();
|
||||
}
|
||||
|
||||
}
|
40
modules/angular2/src/core/compiler/view_pool.js
vendored
Normal file
40
modules/angular2/src/core/compiler/view_pool.js
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
import {Inject, OpaqueToken} from 'angular2/di';
|
||||
import {ListWrapper, MapWrapper, Map, List} from 'angular2/src/facade/collection';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
|
||||
import * as viewModule from './view';
|
||||
|
||||
|
||||
// TODO(tbosch): Make this an OpaqueToken as soon as our transpiler supports this!
|
||||
export const APP_VIEW_POOL_CAPACITY = 'AppViewPool.viewPoolCapacity';
|
||||
|
||||
export class AppViewPool {
|
||||
_poolCapacityPerProtoView:number;
|
||||
_pooledViewsPerProtoView:Map<viewModule.AppProtoView, List<viewModule.AppView>>;
|
||||
|
||||
constructor(@Inject(APP_VIEW_POOL_CAPACITY) poolCapacityPerProtoView) {
|
||||
this._poolCapacityPerProtoView = poolCapacityPerProtoView;
|
||||
this._pooledViewsPerProtoView = MapWrapper.create();
|
||||
}
|
||||
|
||||
getView(protoView:viewModule.AppProtoView):viewModule.AppView {
|
||||
var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView);
|
||||
if (isPresent(pooledViews) && pooledViews.length > 0) {
|
||||
return ListWrapper.removeLast(pooledViews);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
returnView(view:viewModule.AppView) {
|
||||
var protoView = view.proto;
|
||||
var pooledViews = MapWrapper.get(this._pooledViewsPerProtoView, protoView);
|
||||
if (isBlank(pooledViews)) {
|
||||
pooledViews = [];
|
||||
MapWrapper.set(this._pooledViewsPerProtoView, protoView, pooledViews);
|
||||
}
|
||||
if (pooledViews.length < this._poolCapacityPerProtoView) {
|
||||
ListWrapper.push(pooledViews, view);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
33
modules/angular2/src/test_lib/test_bed.js
vendored
33
modules/angular2/src/test_lib/test_bed.js
vendored
@ -8,13 +8,8 @@ import {List} from 'angular2/src/facade/collection';
|
||||
import {View} from 'angular2/src/core/annotations/view';
|
||||
|
||||
import {TemplateResolver} from 'angular2/src/core/compiler/template_resolver';
|
||||
import {Compiler} from 'angular2/src/core/compiler/compiler';
|
||||
import {AppView} from 'angular2/src/core/compiler/view';
|
||||
import {ViewFactory} from 'angular2/src/core/compiler/view_factory';
|
||||
import {AppViewHydrator} from 'angular2/src/core/compiler/view_hydrator';
|
||||
|
||||
import {DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
import {DynamicComponentLoader, ComponentRef} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||
|
||||
import {queryView, viewRootNodes, el} from './utils';
|
||||
import {instantiateType, getTypeOf} from './lang_utils';
|
||||
@ -92,18 +87,9 @@ export class TestBed {
|
||||
}
|
||||
|
||||
var rootEl = el('<div></div>');
|
||||
var metadataReader = this._injector.get(DirectiveMetadataReader);
|
||||
var componentBinding = DirectiveBinding.createFromBinding(
|
||||
bind(component).toValue(context),
|
||||
metadataReader.read(component).annotation
|
||||
);
|
||||
return this._injector.get(Compiler).compileInHost(componentBinding).then((pv) => {
|
||||
var viewFactory = this._injector.get(ViewFactory);
|
||||
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]);
|
||||
var componentBinding = bind(component).toValue(context);
|
||||
return this._injector.get(DynamicComponentLoader).loadIntoNewLocation(componentBinding, null, rootEl, this._injector).then((hostComponentRef) => {
|
||||
return new ViewProxy(hostComponentRef);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -112,12 +98,12 @@ export class TestBed {
|
||||
* Proxy to `AppView` return by `createView` in {@link TestBed} which offers a high level API for tests.
|
||||
*/
|
||||
export class ViewProxy {
|
||||
_componentRef: ComponentRef;
|
||||
_view: AppView;
|
||||
_injector: Injector;
|
||||
|
||||
constructor(injector: Injector, view: AppView) {
|
||||
this._view = view;
|
||||
this._injector = injector;
|
||||
constructor(componentRef: ComponentRef) {
|
||||
this._componentRef = componentRef;
|
||||
this._view = componentRef.hostView.componentChildViews[0];
|
||||
}
|
||||
|
||||
get context(): any {
|
||||
@ -137,8 +123,7 @@ export class ViewProxy {
|
||||
}
|
||||
|
||||
destroy() {
|
||||
var viewHydrator = this._injector.get(AppViewHydrator);
|
||||
viewHydrator.dehydrateInPlaceHostView(null, this._view);
|
||||
this._componentRef.dispose();
|
||||
}
|
||||
|
||||
/**
|
||||
|
12
modules/angular2/src/test_lib/test_injector.js
vendored
12
modules/angular2/src/test_lib/test_injector.js
vendored
@ -35,8 +35,9 @@ import {Injector} from 'angular2/di';
|
||||
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 {AppViewPool, APP_VIEW_POOL_CAPACITY} from 'angular2/src/core/compiler/view_pool';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
import {AppViewManagerUtils} from 'angular2/src/core/compiler/view_manager_utils';
|
||||
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';
|
||||
@ -86,9 +87,10 @@ function _getAppBindings() {
|
||||
rvh.RenderViewHydrator,
|
||||
bind(rvf.VIEW_POOL_CAPACITY).toValue(500),
|
||||
ProtoViewFactory,
|
||||
ViewFactory,
|
||||
AppViewHydrator,
|
||||
bind(VIEW_POOL_CAPACITY).toValue(500),
|
||||
AppViewPool,
|
||||
AppViewManager,
|
||||
AppViewManagerUtils,
|
||||
bind(APP_VIEW_POOL_CAPACITY).toValue(500),
|
||||
Compiler,
|
||||
CompilerCache,
|
||||
bind(TemplateResolver).toClass(MockTemplateResolver),
|
||||
|
@ -18,8 +18,6 @@ import 'package:angular2/src/facade/collection.dart' show StringMapWrapper;
|
||||
import './test_injector.dart';
|
||||
export './test_injector.dart' show inject;
|
||||
|
||||
import 'package:collection/equality.dart';
|
||||
|
||||
bool IS_DARTIUM = true;
|
||||
|
||||
List _testBindings = [];
|
||||
|
Reference in New Issue
Block a user