feat(view): add imperative views
This commit is contained in:
parent
817c79ca77
commit
ada1e642c5
|
@ -4,3 +4,5 @@ export * from './annotations';
|
||||||
export * from './directives';
|
export * from './directives';
|
||||||
export * from './forms';
|
export * from './forms';
|
||||||
export {Observable, EventEmitter} from 'angular2/src/facade/async';
|
export {Observable, EventEmitter} from 'angular2/src/facade/async';
|
||||||
|
export * from 'angular2/src/render/api';
|
||||||
|
export {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer';
|
||||||
|
|
|
@ -71,19 +71,28 @@ export class View {
|
||||||
*/
|
*/
|
||||||
directives:any; //List<Type>;
|
directives:any; //List<Type>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specify a custom renderer for this View.
|
||||||
|
* If this is set, neither `template`, `templateURL` nor `directives` are used.
|
||||||
|
*/
|
||||||
|
renderer:any; // string;
|
||||||
|
|
||||||
@CONST()
|
@CONST()
|
||||||
constructor({
|
constructor({
|
||||||
templateUrl,
|
templateUrl,
|
||||||
template,
|
template,
|
||||||
directives
|
directives,
|
||||||
|
renderer
|
||||||
}: {
|
}: {
|
||||||
templateUrl: string,
|
templateUrl: string,
|
||||||
template: string,
|
template: string,
|
||||||
directives: List<Type>
|
directives: List<Type>,
|
||||||
|
renderer: string
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
this.templateUrl = templateUrl;
|
this.templateUrl = templateUrl;
|
||||||
this.template = template;
|
this.template = template;
|
||||||
this.directives = directives;
|
this.directives = directives;
|
||||||
|
this.renderer = renderer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||||
// We need to do this here to ensure that we create Testability and
|
// We need to do this here to ensure that we create Testability and
|
||||||
// it's ready on the window for users.
|
// it's ready on the window for users.
|
||||||
registry.registerApplication(appElement, testability);
|
registry.registerApplication(appElement, testability);
|
||||||
return dynamicComponentLoader.loadIntoNewLocation(appElement, appComponentAnnotatedType.type, injector);
|
return dynamicComponentLoader.loadIntoNewLocation(appComponentAnnotatedType.type, null, appElement, injector);
|
||||||
}, [DynamicComponentLoader, Injector, appElementToken, appComponentAnnotatedTypeToken,
|
}, [DynamicComponentLoader, Injector, appElementToken, appComponentAnnotatedTypeToken,
|
||||||
Testability, TestabilityRegistry]),
|
Testability, TestabilityRegistry]),
|
||||||
|
|
||||||
|
@ -91,6 +91,7 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||||
bind(ShadowDomStrategy).toFactory(
|
bind(ShadowDomStrategy).toFactory(
|
||||||
(styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head),
|
(styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head),
|
||||||
[StyleUrlResolver, appDocumentToken]),
|
[StyleUrlResolver, appDocumentToken]),
|
||||||
|
DirectDomRenderer,
|
||||||
bind(Renderer).toClass(DirectDomRenderer),
|
bind(Renderer).toClass(DirectDomRenderer),
|
||||||
bind(rc.Compiler).toClass(rc.DefaultCompiler),
|
bind(rc.Compiler).toClass(rc.DefaultCompiler),
|
||||||
// TODO(tbosch): We need an explicit factory here, as
|
// TODO(tbosch): We need an explicit factory here, as
|
||||||
|
@ -105,8 +106,8 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||||
// 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, renderer, appViewHydrator) => new ViewFactory(capacity, renderer, appViewHydrator),
|
(capacity, renderer) => new ViewFactory(capacity, renderer),
|
||||||
[VIEW_POOL_CAPACITY, Renderer, AppViewHydrator]
|
[VIEW_POOL_CAPACITY, Renderer]
|
||||||
),
|
),
|
||||||
bind(VIEW_POOL_CAPACITY).toValue(10000),
|
bind(VIEW_POOL_CAPACITY).toValue(10000),
|
||||||
AppViewHydrator,
|
AppViewHydrator,
|
||||||
|
|
|
@ -114,14 +114,21 @@ export class Compiler {
|
||||||
}
|
}
|
||||||
|
|
||||||
var template = this._templateResolver.resolve(component);
|
var template = this._templateResolver.resolve(component);
|
||||||
var directives = ListWrapper.map(
|
if (isPresent(template.renderer)) {
|
||||||
this._flattenDirectives(template),
|
var directives = [];
|
||||||
(directive) => this._bindDirective(directive)
|
pvPromise = this._renderer.createImperativeComponentProtoView(template.renderer).then( (renderPv) => {
|
||||||
);
|
return this._compileNestedProtoViews(componentBinding, renderPv, directives, true);
|
||||||
var renderTemplate = this._buildRenderTemplate(component, template, directives);
|
});
|
||||||
pvPromise = this._renderer.compile(renderTemplate).then( (renderPv) => {
|
} else {
|
||||||
return this._compileNestedProtoViews(componentBinding, renderPv, directives, true);
|
var directives = ListWrapper.map(
|
||||||
});
|
this._flattenDirectives(template),
|
||||||
|
(directive) => this._bindDirective(directive)
|
||||||
|
);
|
||||||
|
var renderTemplate = this._buildRenderTemplate(component, template, directives);
|
||||||
|
pvPromise = this._renderer.compile(renderTemplate).then( (renderPv) => {
|
||||||
|
return this._compileNestedProtoViews(componentBinding, renderPv, directives, true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
MapWrapper.set(this._compiling, component, pvPromise);
|
MapWrapper.set(this._compiling, component, pvPromise);
|
||||||
return pvPromise;
|
return pvPromise;
|
||||||
|
|
|
@ -70,15 +70,10 @@ export class DynamicComponentLoader {
|
||||||
|
|
||||||
return this._compiler.compile(type).then(componentProtoView => {
|
return this._compiler.compile(type).then(componentProtoView => {
|
||||||
var componentView = this._viewFactory.getView(componentProtoView);
|
var componentView = this._viewFactory.getView(componentProtoView);
|
||||||
var hostView = location.hostView;
|
|
||||||
this._viewHydrator.hydrateDynamicComponentView(
|
this._viewHydrator.hydrateDynamicComponentView(
|
||||||
hostView, location.boundElementIndex, componentView, componentBinding, injector);
|
location, componentView, componentBinding, injector);
|
||||||
|
|
||||||
// TODO(vsavkin): return a component ref that dehydrates the component view and removes it
|
var dispose = () => {throw new BaseException("Not implemented");};
|
||||||
// from the component child views
|
|
||||||
// See ViewFactory.returnView
|
|
||||||
// See AppViewHydrator.dehydrateDynamicComponentView
|
|
||||||
var dispose = () => {throw "Not implemented";};
|
|
||||||
return new ComponentRef(location, location.elementInjector.getDynamicallyLoadedComponent(), componentView, dispose);
|
return new ComponentRef(location, location.elementInjector.getDynamicallyLoadedComponent(), componentView, dispose);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -87,19 +82,22 @@ export class DynamicComponentLoader {
|
||||||
* Loads a component in the element specified by elementOrSelector. The loaded component receives
|
* Loads a component in the element specified by elementOrSelector. The loaded component receives
|
||||||
* injection normally as a hosted view.
|
* injection normally as a hosted view.
|
||||||
*/
|
*/
|
||||||
loadIntoNewLocation(elementOrSelector:any, type:Type, injector:Injector = null):Promise<ComponentRef> {
|
loadIntoNewLocation(type:Type, parentComponentLocation:ElementRef, elementOrSelector:any,
|
||||||
|
injector:Injector = null):Promise<ComponentRef> {
|
||||||
this._assertTypeIsComponent(type);
|
this._assertTypeIsComponent(type);
|
||||||
|
|
||||||
return this._compiler.compileInHost(type).then(hostProtoView => {
|
return this._compiler.compileInHost(type).then(hostProtoView => {
|
||||||
var hostView = this._viewFactory.getView(hostProtoView);
|
var hostView = this._viewFactory.getView(hostProtoView);
|
||||||
this._viewHydrator.hydrateInPlaceHostView(null, elementOrSelector, hostView, injector);
|
this._viewHydrator.hydrateInPlaceHostView(
|
||||||
|
parentComponentLocation, elementOrSelector, hostView, injector
|
||||||
|
);
|
||||||
|
|
||||||
// TODO(vsavkin): return a component ref that dehydrates the host view
|
|
||||||
// See ViewFactory.returnView
|
|
||||||
// See AppViewHydrator.dehydrateInPlaceHostView
|
|
||||||
var newLocation = hostView.elementInjectors[0].getElementRef();
|
var newLocation = hostView.elementInjectors[0].getElementRef();
|
||||||
var component = hostView.elementInjectors[0].getComponent();
|
var component = hostView.elementInjectors[0].getComponent();
|
||||||
var dispose = () => {throw "Not implemented";};
|
var dispose = () => {
|
||||||
|
this._viewHydrator.dehydrateInPlaceHostView(parentComponentLocation, hostView);
|
||||||
|
this._viewFactory.returnView(hostView);
|
||||||
|
};
|
||||||
return new ComponentRef(newLocation, component, hostView.componentChildViews[0], dispose);
|
return new ComponentRef(newLocation, component, hostView.componentChildViews[0], dispose);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,27 +26,21 @@ var _staticKeys;
|
||||||
* @exportedAs angular2/view
|
* @exportedAs angular2/view
|
||||||
*/
|
*/
|
||||||
export class ElementRef {
|
export class ElementRef {
|
||||||
|
hostView:viewModule.AppView;
|
||||||
|
boundElementIndex:number;
|
||||||
|
injector:Injector;
|
||||||
elementInjector:ElementInjector;
|
elementInjector:ElementInjector;
|
||||||
|
|
||||||
constructor(elementInjector:ElementInjector){
|
constructor(elementInjector, hostView, boundElementIndex, injector){
|
||||||
this.elementInjector = elementInjector;
|
this.elementInjector = elementInjector;
|
||||||
}
|
this.hostView = hostView;
|
||||||
|
this.boundElementIndex = boundElementIndex;
|
||||||
get hostView() {
|
this.injector = injector;
|
||||||
return this.elementInjector._preBuiltObjects.view;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get viewContainer() {
|
get viewContainer() {
|
||||||
return this.hostView.getOrCreateViewContainer(this.boundElementIndex);
|
return this.hostView.getOrCreateViewContainer(this.boundElementIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
get injector() {
|
|
||||||
return this.elementInjector._lightDomAppInjector;
|
|
||||||
}
|
|
||||||
|
|
||||||
get boundElementIndex() {
|
|
||||||
return this.elementInjector._proto.index;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class StaticKeys {
|
class StaticKeys {
|
||||||
|
@ -673,7 +667,7 @@ export class ElementInjector extends TreeNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
getElementRef() {
|
getElementRef() {
|
||||||
return new ElementRef(this);
|
return new ElementRef(this, this._preBuiltObjects.view, this._proto.index, this._lightDomAppInjector);
|
||||||
}
|
}
|
||||||
|
|
||||||
getDynamicallyLoadedComponent() {
|
getDynamicallyLoadedComponent() {
|
||||||
|
|
|
@ -25,6 +25,9 @@ export class AppView {
|
||||||
elementInjectors:List<ElementInjector>;
|
elementInjectors:List<ElementInjector>;
|
||||||
changeDetector:ChangeDetector;
|
changeDetector:ChangeDetector;
|
||||||
componentChildViews: List<AppView>;
|
componentChildViews: List<AppView>;
|
||||||
|
/// Host views that were added by an imperative view.
|
||||||
|
/// This is a dynamically growing / shrinking array.
|
||||||
|
imperativeHostViews: List<AppView>;
|
||||||
viewContainers: List<ViewContainer>;
|
viewContainers: List<ViewContainer>;
|
||||||
preBuiltObjects: List<PreBuiltObjects>;
|
preBuiltObjects: List<PreBuiltObjects>;
|
||||||
proto: AppProtoView;
|
proto: AppProtoView;
|
||||||
|
@ -46,7 +49,7 @@ export class AppView {
|
||||||
*/
|
*/
|
||||||
locals:Locals;
|
locals:Locals;
|
||||||
|
|
||||||
constructor(renderer:renderApi.Renderer, viewFactory:vfModule.ViewFactory, viewHydrator:vhModule.AppViewHydrator, proto:AppProtoView, protoLocals:Map) {
|
constructor(renderer:renderApi.Renderer, viewFactory:vfModule.ViewFactory, proto:AppProtoView, protoLocals:Map) {
|
||||||
this.render = null;
|
this.render = null;
|
||||||
this.proto = proto;
|
this.proto = proto;
|
||||||
this.changeDetector = null;
|
this.changeDetector = null;
|
||||||
|
@ -59,7 +62,8 @@ export class AppView {
|
||||||
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;
|
this.renderer = renderer;
|
||||||
this.viewFactory = viewFactory;
|
this.viewFactory = viewFactory;
|
||||||
this.viewHydrator = viewHydrator;
|
this.viewHydrator = null;
|
||||||
|
this.imperativeHostViews = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List,
|
init(changeDetector:ChangeDetector, elementInjectors:List, rootElementInjectors:List,
|
||||||
|
@ -151,7 +155,7 @@ export class AppView {
|
||||||
}
|
}
|
||||||
var result = expr.eval(context, new Locals(this.locals, locals));
|
var result = expr.eval(context, new Locals(this.locals, locals));
|
||||||
if (isPresent(result)) {
|
if (isPresent(result)) {
|
||||||
allowDefaultBehavior = allowDefaultBehavior && result;
|
allowDefaultBehavior = allowDefaultBehavior && result;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,6 @@ 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 viewModule from './view';
|
import * as viewModule from './view';
|
||||||
import {Renderer} from 'angular2/src/render/api';
|
import {Renderer} from 'angular2/src/render/api';
|
||||||
import {AppViewHydrator} from './view_hydrator';
|
|
||||||
|
|
||||||
// TODO(tbosch): Make this an OpaqueToken as soon as our transpiler supports this!
|
// 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';
|
||||||
|
@ -15,13 +14,11 @@ export class ViewFactory {
|
||||||
_poolCapacityPerProtoView:number;
|
_poolCapacityPerProtoView:number;
|
||||||
_pooledViewsPerProtoView:Map<viewModule.AppProtoView, List<viewModule.AppView>>;
|
_pooledViewsPerProtoView:Map<viewModule.AppProtoView, List<viewModule.AppView>>;
|
||||||
_renderer:Renderer;
|
_renderer:Renderer;
|
||||||
_viewHydrator:AppViewHydrator;
|
|
||||||
|
|
||||||
constructor(@Inject(VIEW_POOL_CAPACITY) poolCapacityPerProtoView, renderer:Renderer, viewHydrator:AppViewHydrator) {
|
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;
|
this._renderer = renderer;
|
||||||
this._viewHydrator = viewHydrator;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getView(protoView:viewModule.AppProtoView):viewModule.AppView {
|
getView(protoView:viewModule.AppProtoView):viewModule.AppView {
|
||||||
|
@ -48,7 +45,7 @@ export class ViewFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
_createView(protoView:viewModule.AppProtoView): viewModule.AppView {
|
_createView(protoView:viewModule.AppProtoView): viewModule.AppView {
|
||||||
var view = new viewModule.AppView(this._renderer, this, this._viewHydrator, protoView, protoView.protoLocals);
|
var view = new viewModule.AppView(this._renderer, this, 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());
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,7 @@ import * as viewModule from './view';
|
||||||
import {BindingPropagationConfig, Locals} from 'angular2/change_detection';
|
import {BindingPropagationConfig, Locals} from 'angular2/change_detection';
|
||||||
|
|
||||||
import * as renderApi from 'angular2/src/render/api';
|
import * as renderApi from 'angular2/src/render/api';
|
||||||
|
import {ViewFactory} from 'angular2/src/core/compiler/view_factory';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dehydrated view is a state of the view that allows it to be moved around
|
* A dehydrated view is a state of the view that allows it to be moved around
|
||||||
|
@ -27,13 +28,17 @@ import * as renderApi from 'angular2/src/render/api';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppViewHydrator {
|
export class AppViewHydrator {
|
||||||
_renderer:renderApi.Renderer;
|
_renderer:renderApi.Renderer;
|
||||||
|
_viewFactory:ViewFactory;
|
||||||
|
|
||||||
constructor(renderer:renderApi.Renderer) {
|
constructor(renderer:renderApi.Renderer, viewFactory:ViewFactory) {
|
||||||
this._renderer = renderer;
|
this._renderer = renderer;
|
||||||
|
this._viewFactory = viewFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrateDynamicComponentView(hostView:viewModule.AppView, boundElementIndex:number,
|
hydrateDynamicComponentView(location:eli.ElementRef,
|
||||||
componentView:viewModule.AppView, componentDirective:eli.DirectiveBinding, injector:Injector) {
|
componentView:viewModule.AppView, componentDirective:eli.DirectiveBinding, injector:Injector) {
|
||||||
|
var hostView = location.hostView;
|
||||||
|
var boundElementIndex = location.boundElementIndex;
|
||||||
var binder = hostView.proto.elementBinders[boundElementIndex];
|
var binder = hostView.proto.elementBinders[boundElementIndex];
|
||||||
if (!binder.hasDynamicComponent()) {
|
if (!binder.hasDynamicComponent()) {
|
||||||
throw new BaseException(`There is no dynamic component directive at element ${boundElementIndex}`);
|
throw new BaseException(`There is no dynamic component directive at element ${boundElementIndex}`);
|
||||||
|
@ -84,16 +89,23 @@ export class AppViewHydrator {
|
||||||
// parentView.componentChildViews[boundElementIndex] = null;
|
// parentView.componentChildViews[boundElementIndex] = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrateInPlaceHostView(parentView:viewModule.AppView, hostElementSelector, hostView:viewModule.AppView, injector:Injector) {
|
hydrateInPlaceHostView(parentComponentLocation:eli.ElementRef,
|
||||||
|
hostElementSelector, hostView:viewModule.AppView, injector:Injector) {
|
||||||
var parentRenderViewRef = null;
|
var parentRenderViewRef = null;
|
||||||
if (isPresent(parentView)) {
|
if (isPresent(parentComponentLocation)) {
|
||||||
// Needed for user views
|
var parentView = parentComponentLocation.hostView.componentChildViews[parentComponentLocation.boundElementIndex];
|
||||||
throw new BaseException('Not yet supported');
|
parentRenderViewRef = parentView.render;
|
||||||
|
parentView.changeDetector.addChild(hostView.changeDetector);
|
||||||
|
ListWrapper.push(parentView.imperativeHostViews, hostView);
|
||||||
|
|
||||||
|
if (isBlank(injector)) {
|
||||||
|
injector = parentComponentLocation.injector;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var binder = hostView.proto.elementBinders[0];
|
var binder = hostView.proto.elementBinders[0];
|
||||||
var shadowDomAppInjector = this._createShadowDomAppInjector(binder.componentDirective, injector);
|
var shadowDomAppInjector = this._createShadowDomAppInjector(binder.componentDirective, injector);
|
||||||
|
|
||||||
// render views
|
|
||||||
var renderViewRefs = this._renderer.createInPlaceHostView(parentRenderViewRef, hostElementSelector, hostView.proto.render);
|
var renderViewRefs = this._renderer.createInPlaceHostView(parentRenderViewRef, hostElementSelector, hostView.proto.render);
|
||||||
|
|
||||||
this._viewHydrateRecurse(
|
this._viewHydrateRecurse(
|
||||||
|
@ -101,11 +113,13 @@ export class AppViewHydrator {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
dehydrateInPlaceHostView(parentView:viewModule.AppView, hostView:viewModule.AppView) {
|
dehydrateInPlaceHostView(parentComponentLocation:eli.ElementRef, hostView:viewModule.AppView) {
|
||||||
var parentRenderViewRef = null;
|
var parentRenderViewRef = null;
|
||||||
if (isPresent(parentView)) {
|
if (isPresent(parentComponentLocation)) {
|
||||||
// Needed for user views
|
var parentView = parentComponentLocation.hostView.componentChildViews[parentComponentLocation.boundElementIndex];
|
||||||
throw new BaseException('Not yet supported');
|
parentRenderViewRef = parentView.render;
|
||||||
|
ListWrapper.remove(parentView.imperativeHostViews, hostView);
|
||||||
|
parentView.changeDetector.removeChild(hostView.changeDetector);
|
||||||
}
|
}
|
||||||
var render = hostView.render;
|
var render = hostView.render;
|
||||||
this._viewDehydrateRecurse(hostView);
|
this._viewDehydrateRecurse(hostView);
|
||||||
|
@ -137,7 +151,7 @@ export class AppViewHydrator {
|
||||||
appInjector: Injector, hostElementInjector: eli.ElementInjector,
|
appInjector: Injector, hostElementInjector: eli.ElementInjector,
|
||||||
context: Object, locals:Locals):number {
|
context: Object, locals:Locals):number {
|
||||||
if (view.hydrated()) throw new BaseException('The view is already hydrated.');
|
if (view.hydrated()) throw new BaseException('The view is already hydrated.');
|
||||||
|
view.viewHydrator = this;
|
||||||
view.render = renderComponentViewRefs[renderComponentIndex++];
|
view.render = renderComponentViewRefs[renderComponentIndex++];
|
||||||
|
|
||||||
view.context = context;
|
view.context = context;
|
||||||
|
@ -215,12 +229,22 @@ export class AppViewHydrator {
|
||||||
this._viewDehydrateRecurse(componentView);
|
this._viewDehydrateRecurse(componentView);
|
||||||
var binder = view.proto.elementBinders[i];
|
var binder = view.proto.elementBinders[i];
|
||||||
if (binder.hasDynamicComponent()) {
|
if (binder.hasDynamicComponent()) {
|
||||||
view.componentChildViews[i] = null;
|
|
||||||
view.changeDetector.removeShadowDomChild(componentView.changeDetector);
|
view.changeDetector.removeShadowDomChild(componentView.changeDetector);
|
||||||
|
view.componentChildViews[i] = null;
|
||||||
|
this._viewFactory.returnView(componentView);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// imperativeHostViews
|
||||||
|
for (var i = 0; i < view.imperativeHostViews.length; i++) {
|
||||||
|
var hostView = view.imperativeHostViews[i];
|
||||||
|
this._viewDehydrateRecurse(hostView);
|
||||||
|
view.changeDetector.removeChild(hostView.changeDetector);
|
||||||
|
this._viewFactory.returnView(hostView);
|
||||||
|
}
|
||||||
|
view.imperativeHostViews = [];
|
||||||
|
|
||||||
// elementInjectors
|
// elementInjectors
|
||||||
for (var i = 0; i < view.elementInjectors.length; i++) {
|
for (var i = 0; i < view.elementInjectors.length; i++) {
|
||||||
if (isPresent(view.elementInjectors[i])) {
|
if (isPresent(view.elementInjectors[i])) {
|
||||||
|
|
|
@ -151,7 +151,7 @@ class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
||||||
void appendChild(Node el, Node node) {
|
void appendChild(Node el, Node node) {
|
||||||
el.append(node);
|
el.append(node);
|
||||||
}
|
}
|
||||||
void removeChild(Element el, Node node) {
|
void removeChild(el, Node node) {
|
||||||
node.remove();
|
node.remove();
|
||||||
}
|
}
|
||||||
void replaceChild(Node el, Node newNode, Node oldNode) {
|
void replaceChild(Node el, Node newNode, Node oldNode) {
|
||||||
|
|
|
@ -165,6 +165,14 @@ export class Renderer {
|
||||||
*/
|
*/
|
||||||
createHostProtoView(componentId):Promise<ProtoViewDto> { return null; }
|
createHostProtoView(componentId):Promise<ProtoViewDto> { return null; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creats a ProtoViewDto for a component that will use an imperative View using the given
|
||||||
|
* renderer.
|
||||||
|
* Note: Rigth now, the renderer argument is ignored, but will be used in the future to define
|
||||||
|
* a custom handler.
|
||||||
|
*/
|
||||||
|
createImperativeComponentProtoView(rendererId):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,
|
||||||
|
|
|
@ -12,6 +12,7 @@ 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';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
|
import {ViewContainer} from './view/view_container';
|
||||||
|
|
||||||
function _resolveViewContainer(vc:api.ViewContainerRef) {
|
function _resolveViewContainer(vc:api.ViewContainerRef) {
|
||||||
return _resolveView(vc.view).getOrCreateViewContainer(vc.elementIndex);
|
return _resolveView(vc.view).getOrCreateViewContainer(vc.elementIndex);
|
||||||
|
@ -90,6 +91,12 @@ export class DirectDomRenderer extends api.Renderer {
|
||||||
return PromiseWrapper.resolve(hostProtoViewBuilder.build());
|
return PromiseWrapper.resolve(hostProtoViewBuilder.build());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
createImperativeComponentProtoView(rendererId):Promise<api.ProtoViewDto> {
|
||||||
|
var protoViewBuilder = new ProtoViewBuilder(null);
|
||||||
|
protoViewBuilder.setImperativeRendererId(rendererId);
|
||||||
|
return PromiseWrapper.resolve(protoViewBuilder.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
|
||||||
|
@ -156,6 +163,21 @@ export class DirectDomRenderer extends api.Renderer {
|
||||||
this._viewHydrator.dehydrateInPlaceHostView(parentView, hostView);
|
this._viewHydrator.dehydrateInPlaceHostView(parentView, hostView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setImperativeComponentRootNodes(parentViewRef:api.ViewRef, elementIndex:number, nodes:List):void {
|
||||||
|
var parentView = _resolveView(parentViewRef);
|
||||||
|
var hostElement = parentView.boundElements[elementIndex];
|
||||||
|
var componentView = parentView.componentChildViews[elementIndex];
|
||||||
|
if (isBlank(componentView)) {
|
||||||
|
throw new BaseException(`There is no componentChildView at index ${elementIndex}`);
|
||||||
|
}
|
||||||
|
if (isBlank(componentView.proto.imperativeRendererId)) {
|
||||||
|
throw new BaseException(`This component view has no imperative renderer`);
|
||||||
|
}
|
||||||
|
ViewContainer.removeViewNodes(componentView);
|
||||||
|
componentView.rootNodes = nodes;
|
||||||
|
this._shadowDomStrategy.attachTemplate(hostElement, componentView);
|
||||||
|
}
|
||||||
|
|
||||||
setElementProperty(viewRef:api.ViewRef, elementIndex:number, propertyName:string, propertyValue:any):void {
|
setElementProperty(viewRef:api.ViewRef, elementIndex:number, propertyName:string, propertyValue:any):void {
|
||||||
_resolveView(viewRef).setElementProperty(elementIndex, propertyName, propertyValue);
|
_resolveView(viewRef).setElementProperty(elementIndex, propertyName, propertyValue);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,15 +11,23 @@ export class RenderProtoView {
|
||||||
elementBinders:List<ElementBinder>;
|
elementBinders:List<ElementBinder>;
|
||||||
isTemplateElement:boolean;
|
isTemplateElement:boolean;
|
||||||
rootBindingOffset:int;
|
rootBindingOffset:int;
|
||||||
|
imperativeRendererId:string;
|
||||||
|
|
||||||
constructor({
|
constructor({
|
||||||
elementBinders,
|
elementBinders,
|
||||||
element
|
element,
|
||||||
|
imperativeRendererId
|
||||||
}) {
|
}) {
|
||||||
this.element = element;
|
this.element = element;
|
||||||
this.elementBinders = elementBinders;
|
this.elementBinders = elementBinders;
|
||||||
this.isTemplateElement = DOM.isTemplateElement(this.element);
|
this.imperativeRendererId = imperativeRendererId;
|
||||||
this.rootBindingOffset = (isPresent(this.element) && DOM.hasClass(this.element, NG_BINDING_CLASS)) ? 1 : 0;
|
if (isPresent(imperativeRendererId)) {
|
||||||
|
this.rootBindingOffset = 0;
|
||||||
|
this.isTemplateElement = false;
|
||||||
|
} else {
|
||||||
|
this.isTemplateElement = DOM.isTemplateElement(this.element);
|
||||||
|
this.rootBindingOffset = (isPresent(this.element) && DOM.hasClass(this.element, NG_BINDING_CLASS)) ? 1 : 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mergeChildComponentProtoViews(componentProtoViews:List<RenderProtoView>) {
|
mergeChildComponentProtoViews(componentProtoViews:List<RenderProtoView>) {
|
||||||
|
|
|
@ -20,12 +20,18 @@ export class ProtoViewBuilder {
|
||||||
rootElement;
|
rootElement;
|
||||||
variableBindings: Map<string, string>;
|
variableBindings: Map<string, string>;
|
||||||
elements:List<ElementBinderBuilder>;
|
elements:List<ElementBinderBuilder>;
|
||||||
isRootView:boolean;
|
imperativeRendererId:string;
|
||||||
|
|
||||||
constructor(rootElement) {
|
constructor(rootElement) {
|
||||||
this.rootElement = rootElement;
|
this.rootElement = rootElement;
|
||||||
this.elements = [];
|
this.elements = [];
|
||||||
this.variableBindings = MapWrapper.create();
|
this.variableBindings = MapWrapper.create();
|
||||||
|
this.imperativeRendererId = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setImperativeRendererId(id:string):ProtoViewBuilder {
|
||||||
|
this.imperativeRendererId = id;
|
||||||
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
bindElement(element, description = null):ElementBinderBuilder {
|
bindElement(element, description = null):ElementBinderBuilder {
|
||||||
|
@ -90,7 +96,8 @@ 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,
|
||||||
|
imperativeRendererId: this.imperativeRendererId
|
||||||
})),
|
})),
|
||||||
elementBinders: apiElementBinders,
|
elementBinders: apiElementBinders,
|
||||||
variableBindings: this.variableBindings
|
variableBindings: this.variableBindings
|
||||||
|
|
|
@ -31,6 +31,9 @@ export class RenderView {
|
||||||
hydrated: boolean;
|
hydrated: boolean;
|
||||||
_eventDispatcher: any/*EventDispatcher*/;
|
_eventDispatcher: any/*EventDispatcher*/;
|
||||||
eventHandlerRemovers: List<Function>;
|
eventHandlerRemovers: List<Function>;
|
||||||
|
/// Host views that were added by an imperative view.
|
||||||
|
/// This is a dynamically growing / shrinking array.
|
||||||
|
imperativeHostViews: List<RenderView>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
proto:RenderProtoView, rootNodes:List,
|
proto:RenderProtoView, rootNodes:List,
|
||||||
|
@ -46,7 +49,8 @@ export class RenderView {
|
||||||
this.componentChildViews = ListWrapper.createFixedSize(boundElements.length);
|
this.componentChildViews = ListWrapper.createFixedSize(boundElements.length);
|
||||||
this.hostLightDom = null;
|
this.hostLightDom = null;
|
||||||
this.hydrated = false;
|
this.hydrated = false;
|
||||||
this.eventHandlerRemovers = null;
|
this.eventHandlerRemovers = [];
|
||||||
|
this.imperativeHostViews = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirectParentLightDom(boundElementIndex:number) {
|
getDirectParentLightDom(boundElementIndex:number) {
|
||||||
|
|
|
@ -58,6 +58,12 @@ export class ViewFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
_createView(protoView:pvModule.RenderProtoView, inplaceElement): viewModule.RenderView {
|
_createView(protoView:pvModule.RenderProtoView, inplaceElement): viewModule.RenderView {
|
||||||
|
if (isPresent(protoView.imperativeRendererId)) {
|
||||||
|
return new viewModule.RenderView(
|
||||||
|
protoView, [], [], [], []
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
var rootElementClone = isPresent(inplaceElement) ? inplaceElement : DOM.importIntoDoc(protoView.element);
|
var rootElementClone = isPresent(inplaceElement) ? inplaceElement : DOM.importIntoDoc(protoView.element);
|
||||||
var elementsWithBindingsDynamic;
|
var elementsWithBindingsDynamic;
|
||||||
if (protoView.isTemplateElement) {
|
if (protoView.isTemplateElement) {
|
||||||
|
@ -125,7 +131,7 @@ export class ViewFactory {
|
||||||
// static child components
|
// static child components
|
||||||
if (binder.hasStaticComponent()) {
|
if (binder.hasStaticComponent()) {
|
||||||
var childView = this._createView(binder.nestedProtoView, null);
|
var childView = this._createView(binder.nestedProtoView, null);
|
||||||
this.setComponentView(view, binderIdx, childView);
|
ViewFactory.setComponentView(this._shadowDomStrategy, view, binderIdx, childView);
|
||||||
}
|
}
|
||||||
|
|
||||||
// events
|
// events
|
||||||
|
@ -148,10 +154,10 @@ export class ViewFactory {
|
||||||
// This method is used by the ViewFactory and the ViewHydrator
|
// This method is used by the ViewFactory and the ViewHydrator
|
||||||
// TODO(tbosch): change shadow dom emulation so that LightDom
|
// TODO(tbosch): change shadow dom emulation so that LightDom
|
||||||
// instances don't need to be recreated by instead hydrated/dehydrated
|
// instances don't need to be recreated by instead hydrated/dehydrated
|
||||||
setComponentView(hostView:viewModule.RenderView, elementIndex:number, componentView:viewModule.RenderView) {
|
static setComponentView(shadowDomStrategy:ShadowDomStrategy, hostView:viewModule.RenderView, elementIndex:number, componentView:viewModule.RenderView) {
|
||||||
var element = hostView.boundElements[elementIndex];
|
var element = hostView.boundElements[elementIndex];
|
||||||
var lightDom = this._shadowDomStrategy.constructLightDom(hostView, componentView, element);
|
var lightDom = shadowDomStrategy.constructLightDom(hostView, componentView, element);
|
||||||
this._shadowDomStrategy.attachTemplate(element, componentView);
|
shadowDomStrategy.attachTemplate(element, componentView);
|
||||||
hostView.lightDoms[elementIndex] = lightDom;
|
hostView.lightDoms[elementIndex] = lightDom;
|
||||||
hostView.componentChildViews[elementIndex] = componentView;
|
hostView.componentChildViews[elementIndex] = componentView;
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@ import {EventManager} from '../events/event_manager';
|
||||||
import {ViewFactory} from './view_factory';
|
import {ViewFactory} from './view_factory';
|
||||||
import * as vcModule from './view_container';
|
import * as vcModule from './view_container';
|
||||||
import * as viewModule from './view';
|
import * as viewModule from './view';
|
||||||
|
import {ShadowDomStrategy} from '../shadow_dom/shadow_dom_strategy';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A dehydrated view is a state of the view that allows it to be moved around
|
* A dehydrated view is a state of the view that allows it to be moved around
|
||||||
|
@ -23,14 +24,16 @@ import * as viewModule from './view';
|
||||||
export class RenderViewHydrator {
|
export class RenderViewHydrator {
|
||||||
_eventManager:EventManager;
|
_eventManager:EventManager;
|
||||||
_viewFactory:ViewFactory;
|
_viewFactory:ViewFactory;
|
||||||
|
_shadowDomStrategy:ShadowDomStrategy;
|
||||||
|
|
||||||
constructor(eventManager:EventManager, viewFactory:ViewFactory) {
|
constructor(eventManager:EventManager, viewFactory:ViewFactory, shadowDomStrategy:ShadowDomStrategy) {
|
||||||
this._eventManager = eventManager;
|
this._eventManager = eventManager;
|
||||||
this._viewFactory = viewFactory;
|
this._viewFactory = viewFactory;
|
||||||
|
this._shadowDomStrategy = shadowDomStrategy;
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrateDynamicComponentView(hostView:viewModule.RenderView, boundElementIndex:number, componentView:viewModule.RenderView) {
|
hydrateDynamicComponentView(hostView:viewModule.RenderView, boundElementIndex:number, componentView:viewModule.RenderView) {
|
||||||
this._viewFactory.setComponentView(hostView, boundElementIndex, componentView);
|
ViewFactory.setComponentView(this._shadowDomStrategy, hostView, boundElementIndex, componentView);
|
||||||
var lightDom = hostView.lightDoms[boundElementIndex];
|
var lightDom = hostView.lightDoms[boundElementIndex];
|
||||||
this._viewHydrateRecurse(componentView, lightDom);
|
this._viewHydrateRecurse(componentView, lightDom);
|
||||||
if (isPresent(lightDom)) {
|
if (isPresent(lightDom)) {
|
||||||
|
@ -50,15 +53,17 @@ export class RenderViewHydrator {
|
||||||
|
|
||||||
hydrateInPlaceHostView(parentView:viewModule.RenderView, hostView:viewModule.RenderView) {
|
hydrateInPlaceHostView(parentView:viewModule.RenderView, hostView:viewModule.RenderView) {
|
||||||
if (isPresent(parentView)) {
|
if (isPresent(parentView)) {
|
||||||
throw new BaseException('Not supported yet');
|
ListWrapper.push(parentView.imperativeHostViews, hostView);
|
||||||
}
|
}
|
||||||
this._viewHydrateRecurse(hostView, null);
|
this._viewHydrateRecurse(hostView, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
dehydrateInPlaceHostView(parentView:viewModule.RenderView, hostView:viewModule.RenderView) {
|
dehydrateInPlaceHostView(parentView:viewModule.RenderView, hostView:viewModule.RenderView) {
|
||||||
if (isPresent(parentView)) {
|
if (isPresent(parentView)) {
|
||||||
throw new BaseException('Not supported yet');
|
ListWrapper.remove(parentView.imperativeHostViews, hostView);
|
||||||
}
|
}
|
||||||
|
vcModule.ViewContainer.removeViewNodes(hostView);
|
||||||
|
hostView.rootNodes = [];
|
||||||
this._viewDehydrateRecurse(hostView);
|
this._viewDehydrateRecurse(hostView);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -130,12 +135,24 @@ export class RenderViewHydrator {
|
||||||
this._viewDehydrateRecurse(cv);
|
this._viewDehydrateRecurse(cv);
|
||||||
if (view.proto.elementBinders[i].hasDynamicComponent()) {
|
if (view.proto.elementBinders[i].hasDynamicComponent()) {
|
||||||
vcModule.ViewContainer.removeViewNodes(cv);
|
vcModule.ViewContainer.removeViewNodes(cv);
|
||||||
|
this._viewFactory.returnView(cv);
|
||||||
view.lightDoms[i] = null;
|
view.lightDoms[i] = null;
|
||||||
view.componentChildViews[i] = null;
|
view.componentChildViews[i] = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// imperativeHostViews
|
||||||
|
for (var i = 0; i < view.imperativeHostViews.length; i++) {
|
||||||
|
var hostView = view.imperativeHostViews[i];
|
||||||
|
this._viewDehydrateRecurse(hostView);
|
||||||
|
vcModule.ViewContainer.removeViewNodes(hostView);
|
||||||
|
hostView.rootNodes = [];
|
||||||
|
this._viewFactory.returnView(hostView);
|
||||||
|
}
|
||||||
|
view.imperativeHostViews = [];
|
||||||
|
|
||||||
|
|
||||||
// viewContainers and content tags
|
// viewContainers and content tags
|
||||||
if (isPresent(view.viewContainers)) {
|
if (isPresent(view.viewContainers)) {
|
||||||
for (var i = 0; i < view.viewContainers.length; i++) {
|
for (var i = 0; i < view.viewContainers.length; i++) {
|
||||||
|
|
|
@ -79,6 +79,7 @@ function _getAppBindings() {
|
||||||
bind(ShadowDomStrategy).toFactory(
|
bind(ShadowDomStrategy).toFactory(
|
||||||
(styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head),
|
(styleUrlResolver, doc) => new EmulatedUnscopedShadowDomStrategy(styleUrlResolver, doc.head),
|
||||||
[StyleUrlResolver, appDocumentToken]),
|
[StyleUrlResolver, appDocumentToken]),
|
||||||
|
bind(DirectDomRenderer).toClass(DirectDomRenderer),
|
||||||
bind(Renderer).toClass(DirectDomRenderer),
|
bind(Renderer).toClass(DirectDomRenderer),
|
||||||
bind(rc.Compiler).toClass(rc.DefaultCompiler),
|
bind(rc.Compiler).toClass(rc.DefaultCompiler),
|
||||||
rvf.ViewFactory,
|
rvf.ViewFactory,
|
||||||
|
|
|
@ -10,10 +10,11 @@ import {
|
||||||
inject,
|
inject,
|
||||||
IS_DARTIUM,
|
IS_DARTIUM,
|
||||||
it,
|
it,
|
||||||
|
SpyObject, proxy
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {List, ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper, Map, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {Type, isBlank, stringify, isPresent} from 'angular2/src/facade/lang';
|
import {IMPLEMENTS, Type, isBlank, stringify, isPresent} from 'angular2/src/facade/lang';
|
||||||
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
import {PromiseWrapper, Promise} from 'angular2/src/facade/async';
|
||||||
|
|
||||||
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
||||||
|
@ -30,20 +31,28 @@ import {ProtoViewFactory} from 'angular2/src/core/compiler/proto_view_factory';
|
||||||
|
|
||||||
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
import {UrlResolver} from 'angular2/src/services/url_resolver';
|
||||||
import * as renderApi from 'angular2/src/render/api';
|
import * as renderApi from 'angular2/src/render/api';
|
||||||
|
// TODO(tbosch): Spys don't support named modules...
|
||||||
|
import {Renderer} from 'angular2/src/render/api';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('compiler', function() {
|
describe('compiler', function() {
|
||||||
var reader, tplResolver, renderer, protoViewFactory, cmpUrlMapper;
|
var reader, tplResolver, renderer, protoViewFactory, cmpUrlMapper, renderCompileRequests;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
reader = new DirectiveMetadataReader();
|
reader = new DirectiveMetadataReader();
|
||||||
tplResolver = new FakeTemplateResolver();
|
tplResolver = new FakeTemplateResolver();
|
||||||
cmpUrlMapper = new RuntimeComponentUrlMapper();
|
cmpUrlMapper = new RuntimeComponentUrlMapper();
|
||||||
|
renderer = new SpyRenderer();
|
||||||
});
|
});
|
||||||
|
|
||||||
function createCompiler(renderCompileResults:List, protoViewFactoryResults:List<AppProtoView>) {
|
function createCompiler(renderCompileResults:List, protoViewFactoryResults:List<AppProtoView>) {
|
||||||
var urlResolver = new FakeUrlResolver();
|
var urlResolver = new FakeUrlResolver();
|
||||||
renderer = new FakeRenderer(renderCompileResults);
|
renderCompileRequests = [];
|
||||||
|
renderer.spy('compile').andCallFake( (template) => {
|
||||||
|
ListWrapper.push(renderCompileRequests, template);
|
||||||
|
return PromiseWrapper.resolve(ListWrapper.removeAt(renderCompileResults, 0));
|
||||||
|
});
|
||||||
|
|
||||||
protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults)
|
protoViewFactory = new FakeProtoViewFactory(protoViewFactoryResults)
|
||||||
return new Compiler(
|
return new Compiler(
|
||||||
reader,
|
reader,
|
||||||
|
@ -62,8 +71,8 @@ export function main() {
|
||||||
tplResolver.setView(MainComponent, template);
|
tplResolver.setView(MainComponent, template);
|
||||||
var compiler = createCompiler([createRenderProtoView()], [createProtoView()]);
|
var compiler = createCompiler([createRenderProtoView()], [createProtoView()]);
|
||||||
return compiler.compile(MainComponent).then( (protoView) => {
|
return compiler.compile(MainComponent).then( (protoView) => {
|
||||||
expect(renderer.requests.length).toBe(1);
|
expect(renderCompileRequests.length).toBe(1);
|
||||||
return renderer.requests[0];
|
return renderCompileRequests[0];
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -362,6 +371,11 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should create host proto views', inject([AsyncTestCompleter], (async) => {
|
it('should create host proto views', inject([AsyncTestCompleter], (async) => {
|
||||||
|
renderer.spy('createHostProtoView').andCallFake( (componentId) => {
|
||||||
|
return PromiseWrapper.resolve(
|
||||||
|
createRenderProtoView([createRenderComponentElementBinder(0)])
|
||||||
|
);
|
||||||
|
});
|
||||||
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)
|
||||||
|
@ -379,6 +393,25 @@ export function main() {
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should create imperative proto views', inject([AsyncTestCompleter], (async) => {
|
||||||
|
renderer.spy('createImperativeComponentProtoView').andCallFake( (rendererId) => {
|
||||||
|
return PromiseWrapper.resolve(
|
||||||
|
createRenderProtoView([])
|
||||||
|
);
|
||||||
|
});
|
||||||
|
tplResolver.setView(MainComponent, new View({renderer: 'some-renderer'}));
|
||||||
|
var mainProtoView = createProtoView();
|
||||||
|
var compiler = createCompiler(
|
||||||
|
[],
|
||||||
|
[mainProtoView]
|
||||||
|
);
|
||||||
|
compiler.compile(MainComponent).then( (protoView) => {
|
||||||
|
expect(protoView).toBe(mainProtoView);
|
||||||
|
expect(renderer.spy('createImperativeComponentProtoView')).toHaveBeenCalledWith('some-renderer');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -482,26 +515,11 @@ class DirectiveWithAttributes {
|
||||||
constructor(@Attribute('someAttr') someAttr:string) {}
|
constructor(@Attribute('someAttr') someAttr:string) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
class FakeRenderer extends renderApi.Renderer {
|
@proxy
|
||||||
requests:List<renderApi.ViewDefinition>;
|
@IMPLEMENTS(Renderer)
|
||||||
_results:List;
|
class SpyRenderer extends SpyObject {
|
||||||
|
constructor(){super(Renderer);}
|
||||||
constructor(results) {
|
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||||
super();
|
|
||||||
this._results = results;
|
|
||||||
this.requests = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
compile(template:renderApi.ViewDefinition):Promise<renderApi.ProtoViewDto> {
|
|
||||||
ListWrapper.push(this.requests, template);
|
|
||||||
return PromiseWrapper.resolve(ListWrapper.removeAt(this._results, 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
createHostProtoView(componentId):Promise<renderApi.ProtoViewDto> {
|
|
||||||
return PromiseWrapper.resolve(
|
|
||||||
createRenderProtoView([createRenderComponentElementBinder(0)])
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class FakeUrlResolver extends UrlResolver {
|
class FakeUrlResolver extends UrlResolver {
|
||||||
|
|
|
@ -21,6 +21,7 @@ import {View} from 'angular2/src/core/annotations/view';
|
||||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||||
import {ElementRef} from 'angular2/src/core/compiler/element_injector';
|
import {ElementRef} from 'angular2/src/core/compiler/element_injector';
|
||||||
import {If} from 'angular2/src/directives/if';
|
import {If} from 'angular2/src/directives/if';
|
||||||
|
import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('DynamicComponentLoader', function () {
|
describe('DynamicComponentLoader', function () {
|
||||||
|
@ -134,9 +135,71 @@ export function main() {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('loading into a new location', () => {
|
||||||
|
it('should allow to create, update and destroy components',
|
||||||
|
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
tb.overrideView(MyComp, new View({
|
||||||
|
template: '<imp-ng-cmp #impview></imp-ng-cmp>',
|
||||||
|
directives: [ImperativeViewComponentUsingNgComponent]
|
||||||
|
}));
|
||||||
|
tb.createView(MyComp).then((view) => {
|
||||||
|
var userViewComponent = view.rawView.locals.get("impview");
|
||||||
|
|
||||||
|
userViewComponent.done.then((childComponentRef) => {
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
expect(view.rootNodes).toHaveText('hello');
|
||||||
|
|
||||||
|
childComponentRef.instance.ctxProp = 'new';
|
||||||
|
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
expect(view.rootNodes).toHaveText('new');
|
||||||
|
|
||||||
|
childComponentRef.dispose();
|
||||||
|
|
||||||
|
expect(view.rootNodes).toHaveText('');
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'imp-ng-cmp'
|
||||||
|
})
|
||||||
|
@View({
|
||||||
|
renderer: 'imp-ng-cmp-renderer'
|
||||||
|
})
|
||||||
|
class ImperativeViewComponentUsingNgComponent {
|
||||||
|
done;
|
||||||
|
|
||||||
|
constructor(self:ElementRef, dynamicComponentLoader:DynamicComponentLoader, renderer:DirectDomRenderer) {
|
||||||
|
var div = el('<div></div>');
|
||||||
|
renderer.setImperativeComponentRootNodes(self.hostView.render, self.boundElementIndex, [div]);
|
||||||
|
this.done = dynamicComponentLoader.loadIntoNewLocation(ChildComp, self, div, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'child-cmp',
|
||||||
|
})
|
||||||
|
@View({
|
||||||
|
template: '{{ctxProp}}'
|
||||||
|
})
|
||||||
|
class ChildComp {
|
||||||
|
ctxProp:string;
|
||||||
|
constructor() {
|
||||||
|
this.ctxProp = 'hello';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class DynamicallyCreatedComponentService {
|
class DynamicallyCreatedComponentService {
|
||||||
}
|
}
|
||||||
|
|
|
@ -714,7 +714,7 @@ export function main() {
|
||||||
beforeEach( () => {
|
beforeEach( () => {
|
||||||
renderer = new FakeRenderer();
|
renderer = new FakeRenderer();
|
||||||
var protoView = new AppProtoView(null, null);
|
var protoView = new AppProtoView(null, null);
|
||||||
view = new AppView(renderer, null, null, protoView, MapWrapper.create());
|
view = new AppView(renderer, null, protoView, MapWrapper.create());
|
||||||
view.render = new ViewRef();
|
view.render = new ViewRef();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -33,6 +33,9 @@ import {If} from 'angular2/src/directives/if';
|
||||||
|
|
||||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||||
import {Compiler} from 'angular2/src/core/compiler/compiler';
|
import {Compiler} from 'angular2/src/core/compiler/compiler';
|
||||||
|
import {ElementRef} from 'angular2/src/core/compiler/element_injector';
|
||||||
|
|
||||||
|
import {DirectDomRenderer} from 'angular2/src/render/dom/direct_dom_renderer';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('integration tests', function() {
|
describe('integration tests', function() {
|
||||||
|
@ -602,7 +605,7 @@ export function main() {
|
||||||
DOM.dispatchEvent(view.rootNodes[1], DOM.createMouseEvent('click'));
|
DOM.dispatchEvent(view.rootNodes[1], DOM.createMouseEvent('click'));
|
||||||
expect(DOM.getChecked(view.rootNodes[0])).toBeFalsy();
|
expect(DOM.getChecked(view.rootNodes[0])).toBeFalsy();
|
||||||
expect(DOM.getChecked(view.rootNodes[1])).toBeTruthy();
|
expect(DOM.getChecked(view.rootNodes[1])).toBeTruthy();
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -722,6 +725,18 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should support imperative views',
|
||||||
|
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
tb.overrideView(MyComp, new View({
|
||||||
|
template: '<simple-imp-cmp></simple-imp-cmp>',
|
||||||
|
directives: [SimpleImperativeViewComponent]
|
||||||
|
}));
|
||||||
|
tb.createView(MyComp).then((view) => {
|
||||||
|
expect(view.rootNodes).toHaveText('hello imp view');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
// Disabled until a solution is found, refs:
|
// Disabled until a solution is found, refs:
|
||||||
// - https://github.com/angular/angular/issues/776
|
// - https://github.com/angular/angular/issues/776
|
||||||
// - https://github.com/angular/angular/commit/81f3f32
|
// - https://github.com/angular/angular/commit/81f3f32
|
||||||
|
@ -783,6 +798,21 @@ export function main() {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'simple-imp-cmp'
|
||||||
|
})
|
||||||
|
@View({
|
||||||
|
renderer: 'simple-imp-cmp-renderer'
|
||||||
|
})
|
||||||
|
class SimpleImperativeViewComponent {
|
||||||
|
done;
|
||||||
|
|
||||||
|
constructor(self:ElementRef, renderer:DirectDomRenderer) {
|
||||||
|
renderer.setImperativeComponentRootNodes(self.hostView.render, self.boundElementIndex, [el('hello imp view')]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Decorator({
|
@Decorator({
|
||||||
selector: 'dynamic-vp'
|
selector: 'dynamic-vp'
|
||||||
})
|
})
|
||||||
|
@ -888,7 +918,7 @@ class ComponentWithPipes {
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'child-cmp',
|
selector: 'child-cmp',
|
||||||
injectables: [MyService]
|
injectables: [MyService],
|
||||||
})
|
})
|
||||||
@View({
|
@View({
|
||||||
directives: [MyDir],
|
directives: [MyDir],
|
||||||
|
|
|
@ -35,7 +35,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
function createViewFactory({capacity}):ViewFactory {
|
function createViewFactory({capacity}):ViewFactory {
|
||||||
return new ViewFactory(capacity, renderer, null);
|
return new ViewFactory(capacity, renderer);
|
||||||
}
|
}
|
||||||
|
|
||||||
function createProtoChangeDetector() {
|
function createProtoChangeDetector() {
|
||||||
|
|
|
@ -21,21 +21,24 @@ import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
|
||||||
import {Renderer, ViewRef} from 'angular2/src/render/api';
|
import {Renderer, ViewRef} from 'angular2/src/render/api';
|
||||||
import {ChangeDetector} from 'angular2/change_detection';
|
import {ChangeDetector} from 'angular2/change_detection';
|
||||||
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
import {ElementBinder} from 'angular2/src/core/compiler/element_binder';
|
||||||
import {DirectiveBinding, ElementInjector} from 'angular2/src/core/compiler/element_injector';
|
import {DirectiveBinding, ElementInjector, ElementRef} 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';
|
import {AppViewHydrator} from 'angular2/src/core/compiler/view_hydrator';
|
||||||
|
import {ViewFactory} from 'angular2/src/core/compiler/view_factory';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('AppViewHydrator', () => {
|
describe('AppViewHydrator', () => {
|
||||||
var renderer;
|
var renderer;
|
||||||
var reader;
|
var reader;
|
||||||
var hydrator;
|
var hydrator;
|
||||||
|
var viewFactory;
|
||||||
|
|
||||||
beforeEach( () => {
|
beforeEach( () => {
|
||||||
renderer = new SpyRenderer();
|
renderer = new SpyRenderer();
|
||||||
reader = new DirectiveMetadataReader();
|
reader = new DirectiveMetadataReader();
|
||||||
hydrator = new AppViewHydrator(renderer);
|
viewFactory = new SpyViewFactory();
|
||||||
|
hydrator = new AppViewHydrator(renderer, viewFactory);
|
||||||
});
|
});
|
||||||
|
|
||||||
function createDirectiveBinding(type) {
|
function createDirectiveBinding(type) {
|
||||||
|
@ -81,14 +84,14 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createEmptyView() {
|
function createEmptyView() {
|
||||||
var view = new AppView(renderer, null, null, createProtoView(), MapWrapper.create());
|
var view = new AppView(renderer, null, createProtoView(), MapWrapper.create());
|
||||||
var changeDetector = new SpyChangeDetector();
|
var changeDetector = new SpyChangeDetector();
|
||||||
view.init(changeDetector, [], [], [], []);
|
view.init(changeDetector, [], [], [], []);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createHostView(pv, shadowView, componentInstance, elementInjectors = null) {
|
function createHostView(pv, shadowView, componentInstance, elementInjectors = null) {
|
||||||
var view = new AppView(renderer, null, null, pv, MapWrapper.create());
|
var view = new AppView(renderer, null, pv, MapWrapper.create());
|
||||||
var changeDetector = new SpyChangeDetector();
|
var changeDetector = new SpyChangeDetector();
|
||||||
|
|
||||||
var eis;
|
var eis;
|
||||||
|
@ -117,7 +120,7 @@ export function main() {
|
||||||
var view = createHostView(pv, null, null);
|
var view = createHostView(pv, null, null);
|
||||||
var shadowView = createEmptyView();
|
var shadowView = createEmptyView();
|
||||||
expect(
|
expect(
|
||||||
() => hydrator.hydrateDynamicComponentView(view, 0, shadowView, null, null)
|
() => hydrator.hydrateDynamicComponentView(new ElementRef(null, view, 0, null), shadowView, null, null)
|
||||||
).toThrowError('There is no dynamic component directive at element 0');
|
).toThrowError('There is no dynamic component directive at element 0');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -126,7 +129,7 @@ export function main() {
|
||||||
var view = createHostView(pv, null, null);
|
var view = createHostView(pv, null, null);
|
||||||
var shadowView = createEmptyView();
|
var shadowView = createEmptyView();
|
||||||
expect(
|
expect(
|
||||||
() => hydrator.hydrateDynamicComponentView(view, 0, shadowView, null, null)
|
() => hydrator.hydrateDynamicComponentView(new ElementRef(null, view, 0, null), shadowView, null, null)
|
||||||
).toThrowError('There is no dynamic component directive at element 0');
|
).toThrowError('There is no dynamic component directive at element 0');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -135,9 +138,10 @@ export function main() {
|
||||||
var shadowView = createEmptyView();
|
var shadowView = createEmptyView();
|
||||||
var view = createHostView(pv, null, null);
|
var view = createHostView(pv, null, null);
|
||||||
renderer.spy('createDynamicComponentView').andReturn([new ViewRef(), new ViewRef()]);
|
renderer.spy('createDynamicComponentView').andReturn([new ViewRef(), new ViewRef()]);
|
||||||
hydrator.hydrateDynamicComponentView(view, 0, shadowView, createDirectiveBinding(SomeComponent), null);
|
var elRef = new ElementRef(null, view, 0, null);
|
||||||
|
hydrator.hydrateDynamicComponentView(elRef, shadowView, createDirectiveBinding(SomeComponent), null);
|
||||||
expect(
|
expect(
|
||||||
() => hydrator.hydrateDynamicComponentView(view, 0, shadowView, null, null)
|
() => hydrator.hydrateDynamicComponentView(elRef, shadowView, null, null)
|
||||||
).toThrowError('There already is a bound component at element 0');
|
).toThrowError('There already is a bound component at element 0');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -217,6 +221,7 @@ export function main() {
|
||||||
|
|
||||||
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();
|
||||||
|
expect(viewFactory.spy('returnView')).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear dynamic child components', () => {
|
it('should clear dynamic child components', () => {
|
||||||
|
@ -225,6 +230,19 @@ export function main() {
|
||||||
|
|
||||||
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);
|
||||||
|
expect(viewFactory.spy('returnView')).toHaveBeenCalledWith(shadowView);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear imperatively added child components', () => {
|
||||||
|
createAndHydrate(createProtoView());
|
||||||
|
var impHostView = createHostView(createHostProtoView(createProtoView()), createEmptyView(), null);
|
||||||
|
shadowView.imperativeHostViews = [impHostView];
|
||||||
|
|
||||||
|
dehydrate(hostView);
|
||||||
|
|
||||||
|
expect(shadowView.imperativeHostViews).toEqual([]);
|
||||||
|
expect(viewFactory.spy('returnView')).toHaveBeenCalledWith(impHostView);
|
||||||
|
expect(shadowView.changeDetector.spy('removeChild')).toHaveBeenCalledWith(impHostView.changeDetector);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -255,3 +273,10 @@ class SpyElementInjector extends SpyObject {
|
||||||
constructor(){super(ElementInjector);}
|
constructor(){super(ElementInjector);}
|
||||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(ViewFactory)
|
||||||
|
class SpyViewFactory extends SpyObject {
|
||||||
|
constructor(){super(ViewFactory);}
|
||||||
|
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||||
|
}
|
|
@ -57,7 +57,7 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createViewWithOneBoundElement(pv) {
|
function createViewWithOneBoundElement(pv) {
|
||||||
var view = new AppView(renderer, null, null, pv, MapWrapper.create());
|
var view = new AppView(renderer, null, pv, MapWrapper.create());
|
||||||
var changeDetector = new SpyChangeDetector();
|
var changeDetector = new SpyChangeDetector();
|
||||||
var eij = createElementInjector();
|
var eij = createElementInjector();
|
||||||
view.init(changeDetector, [eij], [eij],
|
view.init(changeDetector, [eij], [eij],
|
||||||
|
|
|
@ -50,6 +50,16 @@ export function main() {
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should create imperative proto views', inject([AsyncTestCompleter], (async) => {
|
||||||
|
createRenderer();
|
||||||
|
renderer.createImperativeComponentProtoView('someRenderId').then( (rootProtoView) => {
|
||||||
|
expect(rootProtoView.elementBinders).toEqual([]);
|
||||||
|
|
||||||
|
expect(rootProtoView.render.delegate.imperativeRendererId).toBe('someRenderId');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should add a static component', inject([AsyncTestCompleter], (async) => {
|
it('should add a static component', inject([AsyncTestCompleter], (async) => {
|
||||||
createRenderer();
|
createRenderer();
|
||||||
renderer.createHostProtoView('someComponentId').then( (rootProtoView) => {
|
renderer.createHostProtoView('someComponentId').then( (rootProtoView) => {
|
||||||
|
|
|
@ -46,7 +46,7 @@ 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);
|
||||||
var viewHydrator = new RenderViewHydrator(eventManager, viewFactory);
|
var viewHydrator = new RenderViewHydrator(eventManager, viewFactory, shadowDomStrategy);
|
||||||
this.renderer = new DirectDomRenderer(compiler, viewFactory, viewHydrator, shadowDomStrategy);
|
this.renderer = new DirectDomRenderer(compiler, viewFactory, viewHydrator, shadowDomStrategy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import {
|
||||||
xit,
|
xit,
|
||||||
SpyObject, proxy
|
SpyObject, proxy
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
import {IMPLEMENTS, isBlank} from 'angular2/src/facade/lang';
|
import {IMPLEMENTS, isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
import {RenderProtoView} from 'angular2/src/render/dom/view/proto_view';
|
import {RenderProtoView} from 'angular2/src/render/dom/view/proto_view';
|
||||||
import {ElementBinder} from 'angular2/src/render/dom/view/element_binder';
|
import {ElementBinder} from 'angular2/src/render/dom/view/element_binder';
|
||||||
|
@ -76,7 +76,7 @@ export function main() {
|
||||||
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>')], [null]);
|
[], [el('<div></div>')], [null]);
|
||||||
viewFactory.setComponentView(view, 0, shadowDomView);
|
ViewFactory.setComponentView(shadowDomStrategy, view, 0, shadowDomView);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,8 +94,8 @@ export function main() {
|
||||||
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);
|
viewFactory = new SpyViewFactory();
|
||||||
viewHydrator = new RenderViewHydrator(eventManager, viewFactory);
|
viewHydrator = new RenderViewHydrator(eventManager, viewFactory, shadowDomStrategy);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('hydrateDynamicComponentView', () => {
|
describe('hydrateDynamicComponentView', () => {
|
||||||
|
@ -111,6 +111,59 @@ export function main() {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('hydrateInPlaceHostView', () => {
|
||||||
|
|
||||||
|
function createInPlaceHostView() {
|
||||||
|
var hostPv = createHostProtoView(createProtoView());
|
||||||
|
var shadowView = createEmptyView();
|
||||||
|
return createHostView(hostPv, shadowView);
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should hydrate the view', () => {
|
||||||
|
var hostView = createInPlaceHostView();
|
||||||
|
viewHydrator.hydrateInPlaceHostView(null, hostView);
|
||||||
|
|
||||||
|
expect(hostView.hydrated).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should store the view in the parent view', () => {
|
||||||
|
var parentView = createEmptyView();
|
||||||
|
var hostView = createInPlaceHostView();
|
||||||
|
|
||||||
|
viewHydrator.hydrateInPlaceHostView(parentView, hostView);
|
||||||
|
|
||||||
|
expect(parentView.imperativeHostViews).toEqual([hostView]);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('dehydrateInPlaceHostView', () => {
|
||||||
|
|
||||||
|
function createAndHydrateInPlaceHostView(parentView) {
|
||||||
|
var hostPv = createHostProtoView(createProtoView());
|
||||||
|
var shadowView = createEmptyView();
|
||||||
|
var hostView = createHostView(hostPv, shadowView);
|
||||||
|
viewHydrator.hydrateInPlaceHostView(parentView, hostView);
|
||||||
|
return hostView;
|
||||||
|
}
|
||||||
|
|
||||||
|
it('should clear the host view', () => {
|
||||||
|
var parentView = createEmptyView();
|
||||||
|
var hostView = createAndHydrateInPlaceHostView(parentView);
|
||||||
|
|
||||||
|
var rootNodes = hostView.rootNodes;
|
||||||
|
expect(rootNodes[0].parentNode).toBeTruthy();
|
||||||
|
|
||||||
|
viewHydrator.dehydrateInPlaceHostView(parentView, hostView);
|
||||||
|
|
||||||
|
expect(parentView.imperativeHostViews).toEqual([]);
|
||||||
|
expect(rootNodes[0].parentNode).toBeFalsy();
|
||||||
|
expect(hostView.rootNodes).toEqual([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
describe('hydrate... shared functionality', () => {
|
describe('hydrate... shared functionality', () => {
|
||||||
|
|
||||||
it('should hydrate existing child components', () => {
|
it('should hydrate existing child components', () => {
|
||||||
|
@ -128,9 +181,12 @@ export function main() {
|
||||||
describe('dehydrate... shared functionality', () => {
|
describe('dehydrate... shared functionality', () => {
|
||||||
var hostView;
|
var hostView;
|
||||||
|
|
||||||
function createAndHydrate(nestedProtoView, shadowView) {
|
function createAndHydrate(nestedProtoView, shadowView, imperativeHostView = null) {
|
||||||
var hostPv = createHostProtoView(nestedProtoView);
|
var hostPv = createHostProtoView(nestedProtoView);
|
||||||
hostView = createHostView(hostPv, shadowView);
|
hostView = createHostView(hostPv, shadowView);
|
||||||
|
if (isPresent(imperativeHostView)) {
|
||||||
|
viewHydrator.hydrateInPlaceHostView(hostView, imperativeHostView);
|
||||||
|
}
|
||||||
|
|
||||||
hydrate(hostView);
|
hydrate(hostView);
|
||||||
}
|
}
|
||||||
|
@ -152,15 +208,36 @@ export function main() {
|
||||||
|
|
||||||
expect(hostView.componentChildViews[0]).toBe(shadowView);
|
expect(hostView.componentChildViews[0]).toBe(shadowView);
|
||||||
expect(shadowView.rootNodes[0].parentNode).toBeTruthy();
|
expect(shadowView.rootNodes[0].parentNode).toBeTruthy();
|
||||||
|
expect(viewFactory.spy('returnView')).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should clear dynamic child components', () => {
|
it('should clear dynamic child components', () => {
|
||||||
var shadowView = createEmptyView();
|
var shadowView = createEmptyView();
|
||||||
createAndHydrate(null, shadowView);
|
createAndHydrate(null, shadowView);
|
||||||
|
expect(shadowView.rootNodes[0].parentNode).toBeTruthy();
|
||||||
|
|
||||||
dehydrate(hostView);
|
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);
|
||||||
|
expect(viewFactory.spy('returnView')).toHaveBeenCalledWith(shadowView);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should clear imperatively added child components', () => {
|
||||||
|
var shadowView = createEmptyView();
|
||||||
|
createAndHydrate(createProtoView(), shadowView);
|
||||||
|
var impHostView = createHostView(createHostProtoView(createProtoView()), createEmptyView());
|
||||||
|
shadowView.imperativeHostViews = [impHostView];
|
||||||
|
|
||||||
|
var rootNodes = impHostView.rootNodes;
|
||||||
|
expect(rootNodes[0].parentNode).toBeTruthy();
|
||||||
|
|
||||||
|
dehydrate(hostView);
|
||||||
|
|
||||||
|
expect(shadowView.imperativeHostViews).toEqual([]);
|
||||||
|
expect(impHostView.rootNodes).toEqual([]);
|
||||||
|
expect(rootNodes[0].parentNode).toBeFalsy();
|
||||||
|
expect(viewFactory.spy('returnView')).toHaveBeenCalledWith(impHostView);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -189,3 +266,9 @@ class SpyLightDom extends SpyObject {
|
||||||
noSuchMethod(m){return super.noSuchMethod(m)}
|
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@proxy
|
||||||
|
@IMPLEMENTS(ViewFactory)
|
||||||
|
class SpyViewFactory extends SpyObject {
|
||||||
|
constructor(){super(ViewFactory);}
|
||||||
|
noSuchMethod(m){return super.noSuchMethod(m)}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue