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:
Tobias Bosch
2015-04-15 21:51:30 -07:00
parent 97fc248e00
commit 923d90bce8
35 changed files with 1013 additions and 763 deletions
+5 -5
View File
@@ -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>'}));
var rootProtoView = createProtoView([
createComponentElementBinder(reader, MainComponent)
@@ -373,7 +373,7 @@ export function main() {
],
[rootProtoView, mainProtoView]
);
compiler.compileRoot(null, MainComponent).then( (protoView) => {
compiler.compileInHost(MainComponent).then( (protoView) => {
expect(protoView).toBe(rootProtoView);
expect(rootProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView);
async.done();
@@ -388,7 +388,7 @@ function createDirectiveBinding(reader, type) {
}
function createProtoView(elementBinders = null) {
var pv = new AppProtoView(null, null, null);
var pv = new AppProtoView(null, null);
if (isBlank(elementBinders)) {
elementBinders = [];
}
@@ -497,7 +497,7 @@ class FakeRenderer extends renderApi.Renderer {
return PromiseWrapper.resolve(ListWrapper.removeAt(this._results, 0));
}
createRootProtoView(elementOrSelector, componentId):Promise<renderApi.ProtoViewDto> {
createHostProtoView(componentId):Promise<renderApi.ProtoViewDto> {
return PromiseWrapper.resolve(
createRenderProtoView([createRenderComponentElementBinder(0)])
);
@@ -545,7 +545,7 @@ class FakeProtoViewFactory extends ProtoViewFactory {
_results:List;
constructor(results) {
super(null, null);
super(null);
this.requests = [];
this._results = results;
}
@@ -24,26 +24,32 @@ import {ElementRef, ElementInjector, ProtoElementInjector, PreBuiltObjects} from
import {Compiler} from 'angular2/src/core/compiler/compiler';
import {AppProtoView, AppView} from 'angular2/src/core/compiler/view';
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() {
describe("DynamicComponentLoader", () => {
var compiler;
var viewFactory;
var directiveMetadataReader;
var renderer;
var viewHydrator;
var loader;
beforeEach( () => {
compiler = new SpyCompiler();
viewFactory = new SpyViewFactory();
renderer = new SpyRenderer();
viewHydrator = new SpyAppViewHydrator();
directiveMetadataReader = new DirectiveMetadataReader();
loader = new DynamicComponentLoader(compiler, directiveMetadataReader, renderer, viewFactory);;
loader = new DynamicComponentLoader(compiler, directiveMetadataReader, viewFactory, viewHydrator);
});
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) {
@@ -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 hostView = new SpyAppView();
var childView = new SpyAppView();
hostView.spy('setDynamicComponentChildView').andCallFake( (boundElementIndex, childView) => {
ListWrapper.push(log, ['setDynamicComponentChildView', boundElementIndex, childView]);
var protoView = createProtoView();
var hostView = createEmptyView();
var childView = createEmptyView();
viewHydrator.spy('hydrateDynamicComponentView').andCallFake( (hostView, boundElementIndex,
componentView, componentDirective, injector) => {
ListWrapper.push(log, ['hydrateDynamicComponentView', hostView, boundElementIndex, componentView]);
});
childView.spy('hydrate').andCallFake( (appInjector, hostElementInjector, context, locals) => {
ListWrapper.push(log, 'hydrate');
viewFactory.spy('getView').andCallFake( (protoView) => {
ListWrapper.push(log, ['getView', protoView]);
return childView;
});
compiler.spy('compile').andCallFake( (_) => PromiseWrapper.resolve(createProtoView()));
viewFactory.spy('getView').andCallFake( (_) => childView);
compiler.spy('compile').andCallFake( (_) => PromiseWrapper.resolve(protoView));
var elementRef = createElementRef(hostView, 23);
loader.loadIntoExistingLocation(SomeComponent, elementRef).then( (componentRef) => {
expect(log[0]).toEqual('hydrate');
expect(log[1]).toEqual(['setDynamicComponentChildView', 23, childView]);
expect(log[0]).toEqual(['getView', protoView]);
expect(log[1]).toEqual(['hydrateDynamicComponentView', hostView, 23, childView]);
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)}}
@proxy
@IMPLEMENTS(Renderer)
class SpyRenderer extends SpyObject {noSuchMethod(m){return super.noSuchMethod(m)}}
@IMPLEMENTS(AppViewHydrator)
class SpyAppViewHydrator extends SpyObject {noSuchMethod(m){return super.noSuchMethod(m)}}
@proxy
@IMPLEMENTS(AppView)
+15 -13
View File
@@ -614,7 +614,7 @@ export function main() {
});
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));
expect(inj.get(ViewContainer)).toEqual(viewContainer);
@@ -631,21 +631,21 @@ export function main() {
describe("dynamicallyCreateComponent", () => {
it("should create a component dynamically", () => {
var inj = injector([]);
inj.dynamicallyCreateComponent(SimpleDirective, null, null);
inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null), null);
expect(inj.getDynamicallyLoadedComponent()).toBeAnInstanceOf(SimpleDirective);
expect(inj.get(SimpleDirective)).toBeAnInstanceOf(SimpleDirective);
});
it("should inject parent dependencies into the dynamically-loaded component", () => {
var inj = parentChildInjectors([SimpleDirective], []);
inj.dynamicallyCreateComponent(NeedDirectiveFromAncestor, null, null);
inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(NeedDirectiveFromAncestor, null), null);
expect(inj.getDynamicallyLoadedComponent()).toBeAnInstanceOf(NeedDirectiveFromAncestor);
expect(inj.getDynamicallyLoadedComponent().dependency).toBeAnInstanceOf(SimpleDirective);
});
it("should not inject the proxy component into the children of the dynamically-loaded component", () => {
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 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", () => {
var proto = new ProtoElementInjector(null, 0, [NeedsDirective], false);
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();
});
it("should inject the dynamically-loaded component into the children of the dynamically-loaded component", () => {
var injWithDynamicallyLoadedComponent = injector([]);
injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(SimpleDirective, null, null);
injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null), null);
var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false);
var shadowDomInjector = shadowDomProtoInjector.instantiate(null);
@@ -678,8 +678,10 @@ export function main() {
it("should remove the dynamically-loaded component when dehydrating", () => {
var inj = injector([]);
inj.dynamicallyCreateComponent(
DirectiveWithDestroy,
new DummyDirective({lifecycle: [onDestroy]}),
DirectiveBinding.createFromType(
DirectiveWithDestroy,
new DummyDirective({lifecycle: [onDestroy]})
),
null);
var dir = inj.getDynamicallyLoadedComponent();
@@ -696,7 +698,7 @@ export function main() {
it("should inject services of the dynamically-loaded component", () => {
var inj = injector([]);
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");
});
});
@@ -706,13 +708,13 @@ export function main() {
function createpreBuildObject(eventName, eventHandler) {
var handlers = StringMapWrapper.create();
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);
var eventBindings = ListWrapper.create();
ListWrapper.push(eventBindings, new EventBinding(eventName, new Parser(new Lexer()).parseAction('handler()', '')));
pv.bindEvent(eventBindings);
var view = new AppView(pv, MapWrapper.create());
var view = new AppView(null, pv, MapWrapper.create());
view.context = new ContextWithHandler(eventHandler);
return new PreBuiltObjects(view, null, null, null);
}
@@ -751,8 +753,8 @@ export function main() {
beforeEach( () => {
renderer = new FakeRenderer();
var protoView = new AppProtoView(renderer, null, null);
view = new AppView(protoView, MapWrapper.create());
var protoView = new AppProtoView(null, null);
view = new AppView(renderer, protoView, MapWrapper.create());
view.render = new ViewRef();
});
+1 -2
View File
@@ -300,7 +300,6 @@ export function main() {
}));
tb.createView(MyComp, {context: ctx}).then((view) => {
view.detectChanges();
var childNodesOfWrapper = view.rootNodes[0].childNodes;
@@ -582,7 +581,7 @@ export function main() {
dispatchEvent(DOM.getGlobalEventTarget("document"), 'domEvent');
expect(listener.eventType).toEqual('document_domEvent');
view.rawView.dehydrate();
view.destroy();
listener = injector.get(DecoratorListeningDomEvent);
dispatchEvent(DOM.getGlobalEventTarget("body"), 'domEvent');
expect(listener.eventType).toEqual('');
+12 -2
View File
@@ -16,6 +16,7 @@ import {
} from 'angular2/test_lib';
import {IMPLEMENTS, isBlank} from 'angular2/src/facade/lang';
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 {DirectiveBinding, ElementInjector} from 'angular2/src/core/compiler/element_injector';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
@@ -26,13 +27,15 @@ import {ChangeDetector, ProtoChangeDetector} from 'angular2/change_detection';
export function main() {
describe('AppViewFactory', () => {
var reader;
var renderer;
beforeEach( () => {
renderer = new SpyRenderer();
reader = new DirectiveMetadataReader();
});
function createViewFactory({capacity}):ViewFactory {
return new ViewFactory(capacity);
return new ViewFactory(capacity, renderer);
}
function createProtoChangeDetector() {
@@ -47,7 +50,7 @@ export function main() {
if (isBlank(binders)) {
binders = [];
}
var pv = new AppProtoView(null, null, createProtoChangeDetector());
var pv = new AppProtoView(null, createProtoChangeDetector());
pv.elementBinders = binders;
return pv;
}
@@ -154,6 +157,13 @@ export function main() {
@Component({ selector: 'someComponent' })
class SomeComponent {}
@proxy
@IMPLEMENTS(Renderer)
class SpyRenderer extends SpyObject {
constructor(){super(Renderer);}
noSuchMethod(m){return super.noSuchMethod(m)}
}
@proxy
@IMPLEMENTS(ChangeDetector)
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 {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
import {Component} from 'angular2/src/core/annotations/annotations';
import {AppViewHydrator} from 'angular2/src/core/compiler/view_hydrator';
export function main() {
describe('AppView', () => {
describe('AppViewHydrator', () => {
var renderer;
var reader;
var hydrator;
beforeEach( () => {
renderer = new SpyRenderer();
reader = new DirectiveMetadataReader();
hydrator = new AppViewHydrator(renderer);
});
function createDirectiveBinding(type) {
@@ -61,7 +64,7 @@ export function main() {
if (isBlank(binders)) {
binders = [];
}
var res = new AppProtoView(renderer, null, null);
var res = new AppProtoView(null, null);
res.elementBinders = binders;
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) {
var view = new AppView(pv, MapWrapper.create());
var view = new AppView(renderer, pv, MapWrapper.create());
var changeDetector = new SpyChangeDetector();
var eij = createElementInjector();
eij.spy('getComponent').andCallFake( () => componentInstance );
@@ -85,84 +95,93 @@ export function main() {
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', () => {
var pv = createProtoView([createEmptyElBinder()]);
var view = createHostView(pv, null, null);
var shadowView = new FakeAppView();
var shadowView = createEmptyView();
expect(
() => view.setDynamicComponentChildView(0, shadowView)
() => hydrator.hydrateDynamicComponentView(view, 0, shadowView, null, null)
).toThrowError('There is no dynamic component directive at element 0');
});
it('should not allow to use static component indices', () => {
var pv = createHostProtoView(createProtoView());
var view = createHostView(pv, null, null);
var shadowView = new FakeAppView();
var shadowView = createEmptyView();
expect(
() => view.setDynamicComponentChildView(0, shadowView)
() => hydrator.hydrateDynamicComponentView(view, 0, shadowView, null, null)
).toThrowError('There is no dynamic component directive at element 0');
});
it('should not allow to overwrite an existing component', () => {
var pv = createHostProtoView(null);
var shadowView = new FakeAppView();
var shadowView = createEmptyView();
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(
() => view.setDynamicComponentChildView(0, shadowView)
() => hydrator.hydrateDynamicComponentView(view, 0, shadowView, null, null)
).toThrowError('There already is a bound component at element 0');
});
});
describe('hydrate', () => {
describe('hydrate... shared functionality', () => {
it('should hydrate existing child components', () => {
var hostPv = createHostProtoView(createProtoView());
var componentInstance = {};
var shadowView = new FakeAppView();
var shadowView = createEmptyView();
var hostView = createHostView(hostPv, shadowView, componentInstance);
renderer.spy('createView').andCallFake( (_) => {
renderer.spy('createInPlaceHostView').andCallFake( (a,b,c) => {
return [new ViewRef(), new ViewRef()];
});
hostView.hydrate(null, null, null, null);
hydrate(hostView);
expect(shadowView.spy('hydrate')).not.toHaveBeenCalled();
expect(shadowView.spy('internalHydrateRecurse')).toHaveBeenCalled();
expect(shadowView.hydrated()).toBe(true);
});
});
describe('dehydrate', () => {
describe('dehydrate... shared functionality', () => {
var hostView;
var shadowView;
function createAndHydrate(nestedProtoView) {
var componentInstance = {};
shadowView = new FakeAppView();
shadowView = createEmptyView();
var hostPv = createHostProtoView(nestedProtoView);
hostView = createHostView(hostPv, shadowView, componentInstance);
renderer.spy('createView').andCallFake( (_) => {
renderer.spy('createInPlaceHostView').andCallFake( (a,b,c) => {
return [new ViewRef(), new ViewRef()];
});
hostView.hydrate(null, null, null, null);
hydrate(hostView);
}
it('should dehydrate child components', () => {
createAndHydrate(createProtoView());
hostView.dehydrate();
dehydrate(hostView);
expect(shadowView.spy('dehydrate')).not.toHaveBeenCalled();
expect(shadowView.spy('internalDehydrateRecurse')).toHaveBeenCalled();
expect(shadowView.hydrated()).toBe(false);
});
it('should not clear static child components', () => {
createAndHydrate(createProtoView());
hostView.dehydrate();
dehydrate(hostView);
expect(hostView.componentChildViews[0]).toBe(shadowView);
expect(hostView.changeDetector.spy('removeShadowDomChild')).not.toHaveBeenCalled();
@@ -170,7 +189,7 @@ export function main() {
it('should clear dynamic child components', () => {
createAndHydrate(null);
hostView.dehydrate();
dehydrate(hostView);
expect(hostView.componentChildViews[0]).toBe(null);
expect(hostView.changeDetector.spy('removeShadowDomChild')).toHaveBeenCalledWith(shadowView.changeDetector);
@@ -204,10 +223,3 @@ class SpyElementInjector extends SpyObject {
constructor(){super(ElementInjector);}
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;
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();
renderer.createRootProtoView(rootEl, 'someComponentId').then( (rootProtoView) => {
renderer.createHostProtoView('someComponentId').then( (rootProtoView) => {
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[0].delegate.rootNodes[0]).toEqual(rootEl);
async.done();
@@ -52,7 +52,7 @@ export function main() {
it('should add a static component', inject([AsyncTestCompleter], (async) => {
createRenderer();
renderer.createRootProtoView(rootEl, 'someComponentId').then( (rootProtoView) => {
renderer.createHostProtoView('someComponentId').then( (rootProtoView) => {
var template = new ViewDefinition({
componentId: 'someComponent',
template: 'hello',
@@ -60,7 +60,7 @@ export function main() {
});
renderer.compile(template).then( (pv) => {
renderer.mergeChildComponentProtoViews(rootProtoView.render, [pv.render]);
renderer.createView(rootProtoView.render);
renderer.createInPlaceHostView(null, rootEl, rootProtoView.render);
expect(rootEl).toHaveText('hello');
async.done();
});
@@ -69,16 +69,15 @@ export function main() {
it('should add a a dynamic component', inject([AsyncTestCompleter], (async) => {
createRenderer();
renderer.createRootProtoView(rootEl, 'someComponentId').then( (rootProtoView) => {
renderer.createHostProtoView('someComponentId').then( (rootProtoView) => {
var template = new ViewDefinition({
componentId: 'someComponent',
template: 'hello',
directives: []
});
renderer.compile(template).then( (pv) => {
var rootViewRef = renderer.createView(rootProtoView.render)[0];
var childComponentViewRef = renderer.createView(pv.render)[0];
renderer.setDynamicComponentView(rootViewRef, 0, childComponentViewRef);
var rootViewRef = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render)[0];
renderer.createDynamicComponentView(rootViewRef, 0, pv.render)[0];
expect(rootEl).toHaveText('hello');
async.done();
});
@@ -93,8 +92,8 @@ export function main() {
directives: []
})]
});
compileRoot(rootEl, 'someComponent').then( (rootProtoView) => {
var viewRefs = renderer.createView(rootProtoView.render);
compileRoot('someComponent').then( (rootProtoView) => {
var viewRefs = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render);
renderer.setText(viewRefs[1], 0, 'hello');
expect(rootEl).toHaveText('hello');
async.done();
@@ -109,8 +108,8 @@ export function main() {
directives: []
})]
});
compileRoot(rootEl, 'someComponent').then( (rootProtoView) => {
var viewRefs = renderer.createView(rootProtoView.render);
compileRoot('someComponent').then( (rootProtoView) => {
var viewRefs = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render);
renderer.setElementProperty(viewRefs[1], 0, 'value', 'hello');
expect(DOM.childNodes(rootEl)[0].value).toEqual('hello');
async.done();
@@ -125,18 +124,20 @@ export function main() {
directives: []
})]
});
compileRoot(rootEl, 'someComponent').then( (rootProtoView) => {
var viewRef = renderer.createView(rootProtoView.render)[1];
compileRoot('someComponent').then( (rootProtoView) => {
var viewRef = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render)[1];
var vcProtoViewRef = rootProtoView.elementBinders[0]
.nestedProtoView.elementBinders[0].nestedProtoView.render;
var vcRef = new ViewContainerRef(viewRef, 0);
var childViewRef = renderer.createView(vcProtoViewRef)[0];
expect(rootEl).toHaveText('');
renderer.insertViewIntoContainer(vcRef, childViewRef);
var childViewRef = renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
expect(rootEl).toHaveText('hello');
renderer.detachViewFromContainer(vcRef, 0);
expect(rootEl).toHaveText('');
renderer.insertViewIntoContainer(vcRef, 0, childViewRef);
expect(rootEl).toHaveText('hello');
renderer.destroyViewInContainer(vcRef, 0);
expect(rootEl).toHaveText('');
async.done();
});
@@ -151,14 +152,16 @@ export function main() {
})],
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]
.nestedProtoView.elementBinders[0].nestedProtoView.render;
var vcRef = new ViewContainerRef(viewRef, 0);
var viewRef1 = renderer.createView(vcProtoViewRef)[0];
renderer.destroyView(viewRef1);
var viewRef2 = renderer.createView(vcProtoViewRef)[0];
var viewRef3 = renderer.createView(vcProtoViewRef)[0];
var viewRef1 = renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
renderer.destroyViewInContainer(vcRef, 0);
var viewRef2 = renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
var viewRef3 = renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
expect(viewRef2.delegate).toBe(viewRef1.delegate);
expect(viewRef3.delegate).not.toBe(viewRef1.delegate);
@@ -176,8 +179,8 @@ export function main() {
directives: []
})]
});
compileRoot(rootEl, 'someComponent').then( (rootProtoView) => {
var viewRef = renderer.createView(rootProtoView.render)[1];
compileRoot('someComponent').then( (rootProtoView) => {
var viewRef = renderer.createInPlaceHostView(null, rootEl, rootProtoView.render)[1];
var dispatcher = new LoggingEventDispatcher();
renderer.setEventDispatcher(viewRef, dispatcher);
var inputEl = DOM.childNodes(rootEl)[0];
+5 -3
View File
@@ -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 {StyleUrlResolver} from 'angular2/src/render/dom/shadow_dom/style_url_resolver';
import {ViewFactory} from 'angular2/src/render/dom/view/view_factory';
import {RenderViewHydrator} from 'angular2/src/render/dom/view/view_hydrator';
export class IntegrationTestbed {
renderer;
@@ -45,11 +46,12 @@ export class IntegrationTestbed {
this.eventPlugin = new FakeEventManagerPlugin();
var eventManager = new EventManager([this.eventPlugin], new FakeVmTurnZone());
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> {
return this.renderer.createRootProtoView(rootEl, componentId).then( (rootProtoView) => {
compileRoot(componentId):Promise<ProtoViewDto> {
return this.renderer.createHostProtoView(componentId).then( (rootProtoView) => {
return this._compileNestedProtoViews(rootProtoView, [
new DirectiveMetadata({
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';
export function main() {
describe('EmulatedScoped', () => {
describe('EmulatedScopedShadowDomStrategy', () => {
var xhr, styleHost, strategy;
beforeEach(() => {
@@ -47,7 +47,7 @@ export function main() {
it('should attach the view nodes as child of the host element', () => {
var host = el('<div><span>original content</span></div>');
var nodes = el('<div>view</div>');
var view = new RenderView(null, [nodes], [], [], [], [], null);
var view = new RenderView(null, [nodes], [], [], [], []);
strategy.attachTemplate(host, view);
var firstChild = DOM.firstChild(host);
@@ -28,7 +28,7 @@ import {RenderView} from 'angular2/src/render/dom/view/view';
export function main() {
var strategy;
describe('EmulatedUnscoped', () => {
describe('EmulatedUnscopedShadowDomStrategy', () => {
var styleHost;
beforeEach(() => {
@@ -42,7 +42,7 @@ export function main() {
it('should attach the view nodes as child of the host element', () => {
var host = el('<div><span>original content</span></div>');
var nodes = el('<div>view</div>');
var view = new RenderView(null, [nodes], [], [], [], [], null);
var view = new RenderView(null, [nodes], [], [], [], []);
strategy.attachTemplate(host, view);
var firstChild = DOM.firstChild(host);
@@ -35,7 +35,7 @@ export function main() {
it('should attach the view nodes to the shadow root', () => {
var host = el('<div><span>original content</span></div>');
var nodes = el('<div>view</div>');
var view = new RenderView(null, [nodes], [], [], [], [], null);
var view = new RenderView(null, [nodes], [], [], [], []);
strategy.attachTemplate(host, view);
var shadowRoot = DOM.getShadowRoot(host);
@@ -60,7 +60,7 @@ export function main() {
templates: ListWrapper.concat(templates, componentTemplates)
});
renderer = testbed.renderer;
compileRoot = (rootEl, componentId) => testbed.compileRoot(rootEl, componentId);
compileRoot = (rootEl) => testbed.compileRoot(rootEl);
compile = (componentId) => testbed.compile(componentId);
}
@@ -78,8 +78,8 @@ export function main() {
directives: [simple]
})]
});
compileRoot(rootEl, 'main').then( (pv) => {
renderer.createView(pv.render);
compileRoot('main').then( (pv) => {
renderer.createInPlaceHostView(null, rootEl, pv.render);
expect(rootEl).toHaveText('SIMPLE(A)');
@@ -97,11 +97,10 @@ export function main() {
directives: [dynamicComponent]
})]
});
compileRoot(rootEl, 'main').then( (rootPv) => {
compileRoot('main').then( (rootPv) => {
compile('simple').then( (simplePv) => {
var views = renderer.createView(rootPv.render);
var simpleViews = renderer.createView(simplePv.render);
renderer.setDynamicComponentView(views[1], 0, simpleViews[0]);
var views = renderer.createInPlaceHostView(null, rootEl, rootPv.render);
renderer.createDynamicComponentView(views[1], 0, simplePv.render);
expect(rootEl).toHaveText('SIMPLE(A)');
@@ -122,8 +121,8 @@ export function main() {
directives: [multipleContentTagsComponent]
})]
});
compileRoot(rootEl, 'main').then( (pv) => {
renderer.createView(pv.render);
compileRoot('main').then( (pv) => {
renderer.createInPlaceHostView(null, rootEl, pv.render);
expect(rootEl).toHaveText('(A, BC)');
@@ -142,8 +141,8 @@ export function main() {
directives: [multipleContentTagsComponent]
})]
});
compileRoot(rootEl, 'main').then( (pv) => {
renderer.createView(pv.render);
compileRoot('main').then( (pv) => {
renderer.createInPlaceHostView(null, rootEl, pv.render);
expect(rootEl).toHaveText('(, BAC)');
@@ -162,20 +161,18 @@ export function main() {
directives: [multipleContentTagsComponent, manualViewportDirective]
})]
});
compileRoot(rootEl, 'main').then( (pv) => {
var viewRefs = renderer.createView(pv.render);
compileRoot('main').then( (pv) => {
var viewRefs = renderer.createInPlaceHostView(null, rootEl, pv.render);
var vcRef = new ViewContainerRef(viewRefs[1], 1);
var vcProtoViewRef = pv.elementBinders[0].nestedProtoView
.elementBinders[1].nestedProtoView.render;
var childViewRef = renderer.createView(vcProtoViewRef)[0];
expect(rootEl).toHaveText('(, B)');
renderer.insertViewIntoContainer(vcRef, childViewRef);
renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
expect(rootEl).toHaveText('(, AB)');
renderer.detachViewFromContainer(vcRef, 0);
renderer.destroyViewInContainer(vcRef, 0);
expect(rootEl).toHaveText('(, B)');
@@ -194,20 +191,18 @@ export function main() {
directives: [multipleContentTagsComponent, manualViewportDirective]
})]
});
compileRoot(rootEl, 'main').then( (pv) => {
var viewRefs = renderer.createView(pv.render);
compileRoot('main').then( (pv) => {
var viewRefs = renderer.createInPlaceHostView(null, rootEl, pv.render);
var vcRef = new ViewContainerRef(viewRefs[1], 1);
var vcProtoViewRef = pv.elementBinders[0].nestedProtoView
.elementBinders[1].nestedProtoView.render;
var childViewRef = renderer.createView(vcProtoViewRef)[0];
expect(rootEl).toHaveText('(, B)');
renderer.insertViewIntoContainer(vcRef, childViewRef);
renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
expect(rootEl).toHaveText('(A, B)');
renderer.detachViewFromContainer(vcRef, 0);
renderer.destroyViewInContainer(vcRef, 0);
expect(rootEl).toHaveText('(, B)');
@@ -226,8 +221,8 @@ export function main() {
directives: [outerWithIndirectNestedComponent]
})]
});
compileRoot(rootEl, 'main').then( (pv) => {
renderer.createView(pv.render);
compileRoot('main').then( (pv) => {
renderer.createInPlaceHostView(null, rootEl, pv.render);
expect(rootEl).toHaveText('OUTER(SIMPLE(AB))');
@@ -247,16 +242,14 @@ export function main() {
directives: [outerComponent, manualViewportDirective]
})]
});
compileRoot(rootEl, 'main').then( (pv) => {
var viewRefs = renderer.createView(pv.render);
compileRoot('main').then( (pv) => {
var viewRefs = renderer.createInPlaceHostView(null, rootEl, pv.render);
var vcRef = new ViewContainerRef(viewRefs[1], 1);
var vcProtoViewRef = pv.elementBinders[0].nestedProtoView
.elementBinders[1].nestedProtoView.render;
var childViewRef = renderer.createView(vcProtoViewRef)[0];
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)))');
async.done();
@@ -275,21 +268,20 @@ export function main() {
directives: [conditionalContentComponent]
})]
});
compileRoot(rootEl, 'main').then( (pv) => {
var viewRefs = renderer.createView(pv.render);
compileRoot('main').then( (pv) => {
var viewRefs = renderer.createInPlaceHostView(null, rootEl, pv.render);
var vcRef = new ViewContainerRef(viewRefs[2], 0);
var vcProtoViewRef = pv.elementBinders[0].nestedProtoView
.elementBinders[0].nestedProtoView
.elementBinders[0].nestedProtoView.render;
var childViewRef = renderer.createView(vcProtoViewRef)[0];
expect(rootEl).toHaveText('(, ABC)');
renderer.insertViewIntoContainer(vcRef, childViewRef);
renderer.createViewInContainer(vcRef, 0, vcProtoViewRef)[0];
expect(rootEl).toHaveText('(A, BC)');
renderer.detachViewFromContainer(vcRef, 0);
renderer.destroyViewInContainer(vcRef, 0);
expect(rootEl).toHaveText('(, ABC)');
@@ -42,7 +42,6 @@ export function main() {
}
return new RenderProtoView({
element: rootEl,
isRootView: false,
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 {EventManager} from 'angular2/src/render/dom/events/event_manager';
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() {
describe('RenderView', () => {
describe('RenderViewHydrator', () => {
var shadowDomStrategy;
var eventManager;
var viewFactory;
var viewHydrator;
function createProtoView({rootEl, binders}={}) {
if (isBlank(rootEl)) {
@@ -39,7 +43,6 @@ export function main() {
}
return new RenderProtoView({
element: rootEl,
isRootView: false,
elementBinders: binders
});
}
@@ -67,83 +70,85 @@ export function main() {
function createEmptyView() {
var root = el('<div><div></div></div>');
return new RenderView(createProtoView(), [DOM.childNodes(root)[0]],
[], [], [], [], eventManager);
[], [], [], []);
}
function createHostView(pv, shadowDomView) {
var view = new RenderView(pv, [el('<div></div>')],
[], [el('<div></div>')], [], [], eventManager);
view.setComponentView(shadowDomStrategy, 0, shadowDomView);
[], [el('<div></div>')], [], []);
viewFactory.setComponentView(view, 0, shadowDomView);
return view;
}
function hydrate(view) {
viewHydrator.hydrateInPlaceHostView(null, view);
}
function dehydrate(view) {
viewHydrator.dehydrateInPlaceHostView(null, view);
}
beforeEach( () => {
eventManager = new SpyEventManager();
shadowDomStrategy = new SpyShadowDomStrategy();
shadowDomStrategy.spy('constructLightDom').andCallFake( (lightDomView, shadowDomView, el) => {
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', () => {
var shadowView = new SpyRenderView();
it('should redistribute', () => {
var shadowView = createEmptyView();
var hostPv = createHostProtoView(createProtoView());
var hostView = createHostView(hostPv, shadowView);
hostView.hydrate(null);
hostView.setComponentView(shadowDomStrategy, 0, shadowView);
viewHydrator.hydrateDynamicComponentView(hostView, 0, shadowView);
var lightDomSpy:SpyLightDom = hostView.lightDoms[0];
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', () => {
var hostPv = createHostProtoView(createProtoView());
var shadowView = new SpyRenderView();
var hostView = createHostView(hostPv, shadowView);
var shadowView = createEmptyView();
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;
function createAndHydrate(nestedProtoView, shadowView) {
var hostPv = createHostProtoView(nestedProtoView);
hostView = createHostView(hostPv, shadowView);
hostView.hydrate(null);
hydrate(hostView);
}
it('should dehydrate child components', () => {
var shadowView = new SpyRenderView();
var shadowView = createEmptyView();
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', () => {
var shadowView = createEmptyView();
createAndHydrate(createProtoView(), shadowView);
hostView.dehydrate();
dehydrate(hostView);
expect(hostView.componentChildViews[0]).toBe(shadowView);
expect(shadowView.rootNodes[0].parentNode).toBeTruthy();
@@ -152,7 +157,7 @@ export function main() {
it('should clear dynamic child components', () => {
var shadowView = createEmptyView();
createAndHydrate(null, shadowView);
hostView.dehydrate();
dehydrate(hostView);
expect(hostView.componentChildViews[0]).toBe(null);
expect(shadowView.rootNodes[0].parentNode).toBe(null);
@@ -184,10 +189,3 @@ class SpyLightDom extends SpyObject {
noSuchMethod(m){return super.noSuchMethod(m)}
}
@proxy
@IMPLEMENTS(RenderView)
class SpyRenderView extends SpyObject {
constructor(){super(RenderView);}
noSuchMethod(m){return super.noSuchMethod(m)}
}