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