fix(views): remove dynamic component views, free host views, free embedded views
Closes #2472 Closes #2339 BREAKING CHANGE - `Compiler.compile` has been removed, the only way to compile components dynamically is via `Compiler.compileInHost` - `DynamicComponentLoader.loadIntoExistingLocation` has changed: * renamed into `loadIntoLocation` * will always create the host element as well * requires an element with a variable inside of the host component view next to which it will load new component. - `DynamicComponentLoader.loadNextToExistingLocation` was renamed into `DynamicComponentLoader.loadNextToLocation` - `DynamicComponentLoader.loadIntoNewLocation` is removed * use `DynamicComponentLoader.loadNextToLocation` instead and then move the view nodes manually around via `DomRenderer.getRootNodes()` - `AppViewManager.{create,destroy}Free{Host,Embedded}View` was removed * use `AppViewManager.createViewInContainer` and then move the view nodes manually around via `DomRenderer.getRootNodes()` - `Renderer.detachFreeView` was removed. Use `DomRenderer.getRootNodes()` to get the root nodes of a view and detach them manually.
This commit is contained in:
@@ -41,13 +41,19 @@ import {RenderCompiler} from 'angular2/src/render/api';
|
||||
export function main() {
|
||||
describe('compiler', function() {
|
||||
var directiveResolver, tplResolver, renderCompiler, protoViewFactory, cmpUrlMapper,
|
||||
renderCompileRequests;
|
||||
renderCompileRequests, rootProtoView;
|
||||
|
||||
beforeEach(() => {
|
||||
directiveResolver = new DirectiveResolver();
|
||||
tplResolver = new FakeTemplateResolver();
|
||||
cmpUrlMapper = new RuntimeComponentUrlMapper();
|
||||
renderCompiler = new SpyRenderCompiler();
|
||||
renderCompiler.spy('compileHost')
|
||||
.andCallFake((componentId) => {
|
||||
return PromiseWrapper.resolve(createRenderProtoView(
|
||||
[createRenderComponentElementBinder(0)], renderApi.ViewType.HOST));
|
||||
});
|
||||
rootProtoView = createRootProtoView(directiveResolver, MainComponent);
|
||||
});
|
||||
|
||||
function createCompiler(renderCompileResults: List<renderApi.ProtoViewDto>,
|
||||
@@ -68,8 +74,9 @@ export function main() {
|
||||
|
||||
function captureTemplate(template: viewAnn.View): Promise<renderApi.ViewDefinition> {
|
||||
tplResolver.setView(MainComponent, template);
|
||||
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
||||
return compiler.compile(MainComponent)
|
||||
var compiler =
|
||||
createCompiler([createRenderProtoView()], [[rootProtoView], [createProtoView()]]);
|
||||
return compiler.compileInHost(MainComponent)
|
||||
.then((_) => {
|
||||
expect(renderCompileRequests.length).toBe(1);
|
||||
return renderCompileRequests[0];
|
||||
@@ -249,14 +256,14 @@ export function main() {
|
||||
|
||||
describe('call ProtoViewFactory', () => {
|
||||
|
||||
it('should pass the render protoView', inject([AsyncTestCompleter], (async) => {
|
||||
it('should pass the ProtoViewDto', inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var renderProtoView = createRenderProtoView();
|
||||
var expectedProtoView = createProtoView();
|
||||
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
||||
compiler.compile(MainComponent)
|
||||
var compiler = createCompiler([renderProtoView], [[rootProtoView], [expectedProtoView]]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((_) => {
|
||||
var request = protoViewFactory.requests[0];
|
||||
var request = protoViewFactory.requests[1];
|
||||
expect(request[1]).toBe(renderProtoView);
|
||||
async.done();
|
||||
});
|
||||
@@ -264,10 +271,11 @@ export function main() {
|
||||
|
||||
it('should pass the component binding', inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
||||
compiler.compile(MainComponent)
|
||||
var compiler =
|
||||
createCompiler([createRenderProtoView()], [[rootProtoView], [createProtoView()]]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((_) => {
|
||||
var request = protoViewFactory.requests[0];
|
||||
var request = protoViewFactory.requests[1];
|
||||
expect(request[0].key.token).toBe(MainComponent);
|
||||
async.done();
|
||||
});
|
||||
@@ -277,10 +285,11 @@ export function main() {
|
||||
tplResolver.setView(
|
||||
MainComponent,
|
||||
new viewAnn.View({template: '<div></div>', directives: [SomeDirective]}));
|
||||
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
||||
compiler.compile(MainComponent)
|
||||
var compiler =
|
||||
createCompiler([createRenderProtoView()], [[rootProtoView], [createProtoView()]]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((_) => {
|
||||
var request = protoViewFactory.requests[0];
|
||||
var request = protoViewFactory.requests[1];
|
||||
var binding = request[2][0];
|
||||
expect(binding.key.token).toBe(SomeDirective);
|
||||
async.done();
|
||||
@@ -290,12 +299,11 @@ export function main() {
|
||||
it('should use the protoView of the ProtoViewFactory',
|
||||
inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var renderProtoView = createRenderProtoView();
|
||||
var expectedProtoView = createProtoView();
|
||||
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
||||
compiler.compile(MainComponent)
|
||||
var compiler =
|
||||
createCompiler([createRenderProtoView()], [[rootProtoView], [createProtoView()]]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
||||
expect(internalProtoView(protoViewRef)).toBe(rootProtoView);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -312,10 +320,11 @@ export function main() {
|
||||
createRenderProtoView([createRenderComponentElementBinder(0)]),
|
||||
createRenderProtoView()
|
||||
],
|
||||
[[mainProtoView], [nestedProtoView]]);
|
||||
compiler.compile(MainComponent)
|
||||
[[rootProtoView], [mainProtoView], [nestedProtoView]]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
||||
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||
.toBe(mainProtoView);
|
||||
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
||||
async.done();
|
||||
});
|
||||
@@ -328,35 +337,68 @@ export function main() {
|
||||
var viewportProtoView =
|
||||
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)]);
|
||||
var nestedProtoView = createProtoView();
|
||||
var compiler = createCompiler([
|
||||
createRenderProtoView([
|
||||
createRenderViewportElementBinder(createRenderProtoView(
|
||||
[createRenderComponentElementBinder(0)], renderApi.ViewType.EMBEDDED))
|
||||
]),
|
||||
createRenderProtoView()
|
||||
],
|
||||
[[mainProtoView, viewportProtoView], [nestedProtoView]]);
|
||||
compiler.compile(MainComponent)
|
||||
var compiler = createCompiler(
|
||||
[
|
||||
createRenderProtoView([
|
||||
createRenderViewportElementBinder(createRenderProtoView(
|
||||
[createRenderComponentElementBinder(0)], renderApi.ViewType.EMBEDDED))
|
||||
]),
|
||||
createRenderProtoView()
|
||||
],
|
||||
[[rootProtoView], [mainProtoView, viewportProtoView], [nestedProtoView]]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
||||
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||
.toBe(mainProtoView);
|
||||
expect(viewportProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
||||
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should cache compiled components', inject([AsyncTestCompleter], (async) => {
|
||||
it('should cache compiled host components', inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var renderProtoView = createRenderProtoView();
|
||||
var expectedProtoView = createProtoView();
|
||||
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
||||
compiler.compile(MainComponent)
|
||||
var mainPv = createProtoView();
|
||||
var compiler = createCompiler([createRenderProtoView()], [[rootProtoView], [mainPv]]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
||||
return compiler.compile(MainComponent);
|
||||
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||
.toBe(mainPv);
|
||||
return compiler.compileInHost(MainComponent);
|
||||
})
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
||||
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||
.toBe(mainPv);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should cache compiled nested components', inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
tplResolver.setView(MainComponent2, new viewAnn.View({template: '<div></div>'}));
|
||||
tplResolver.setView(NestedComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var rootProtoView2 = createRootProtoView(directiveResolver, MainComponent2);
|
||||
var mainPv =
|
||||
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)]);
|
||||
var nestedPv = createProtoView([]);
|
||||
var compiler = createCompiler(
|
||||
[createRenderProtoView(), createRenderProtoView(), createRenderProtoView()],
|
||||
[[rootProtoView], [mainPv], [nestedPv], [rootProtoView2], [mainPv]]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef)
|
||||
.elementBinders[0]
|
||||
.nestedProtoView.elementBinders[0]
|
||||
.nestedProtoView)
|
||||
.toBe(nestedPv);
|
||||
return compiler.compileInHost(MainComponent2);
|
||||
})
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef)
|
||||
.elementBinders[0]
|
||||
.nestedProtoView.elementBinders[0]
|
||||
.nestedProtoView)
|
||||
.toBe(nestedPv);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
@@ -365,36 +407,40 @@ export function main() {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var renderProtoViewCompleter = PromiseWrapper.completer();
|
||||
var expectedProtoView = createProtoView();
|
||||
var compiler = createCompiler([renderProtoViewCompleter.promise], [[expectedProtoView]]);
|
||||
var compiler = createCompiler([renderProtoViewCompleter.promise],
|
||||
[[rootProtoView], [rootProtoView], [expectedProtoView]]);
|
||||
var result = PromiseWrapper.all([
|
||||
compiler.compileInHost(MainComponent),
|
||||
compiler.compileInHost(MainComponent),
|
||||
renderProtoViewCompleter.promise
|
||||
]);
|
||||
renderProtoViewCompleter.resolve(createRenderProtoView());
|
||||
PromiseWrapper.all([compiler.compile(MainComponent), compiler.compile(MainComponent)])
|
||||
.then((protoViewRefs) => {
|
||||
expect(internalProtoView(protoViewRefs[0])).toBe(expectedProtoView);
|
||||
expect(internalProtoView(protoViewRefs[1])).toBe(expectedProtoView);
|
||||
async.done();
|
||||
});
|
||||
result.then((protoViewRefs) => {
|
||||
expect(internalProtoView(protoViewRefs[0]).elementBinders[0].nestedProtoView)
|
||||
.toBe(expectedProtoView);
|
||||
expect(internalProtoView(protoViewRefs[1]).elementBinders[0].nestedProtoView)
|
||||
.toBe(expectedProtoView);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow recursive components', inject([AsyncTestCompleter], (async) => {
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var mainProtoView =
|
||||
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
|
||||
var compiler = createCompiler(
|
||||
[createRenderProtoView([createRenderComponentElementBinder(0)])], [[mainProtoView]]);
|
||||
compiler.compile(MainComponent)
|
||||
var compiler =
|
||||
createCompiler([createRenderProtoView([createRenderComponentElementBinder(0)])],
|
||||
[[rootProtoView], [mainProtoView]]);
|
||||
compiler.compileInHost(MainComponent)
|
||||
.then((protoViewRef) => {
|
||||
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
||||
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||
.toBe(mainProtoView);
|
||||
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should create host proto views', inject([AsyncTestCompleter], (async) => {
|
||||
renderCompiler.spy('compileHost')
|
||||
.andCallFake((componentId) => {
|
||||
return PromiseWrapper.resolve(createRenderProtoView(
|
||||
[createRenderComponentElementBinder(0)], renderApi.ViewType.HOST));
|
||||
});
|
||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||
var rootProtoView =
|
||||
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
|
||||
@@ -411,7 +457,7 @@ export function main() {
|
||||
|
||||
it('should throw for non component types', () => {
|
||||
var compiler = createCompiler([], []);
|
||||
expect(() => compiler.compile(SomeDirective))
|
||||
expect(() => compiler.compileInHost(SomeDirective))
|
||||
.toThrowError(
|
||||
`Could not load '${stringify(SomeDirective)}' because it is not a component.`);
|
||||
});
|
||||
@@ -424,7 +470,7 @@ function createDirectiveBinding(directiveResolver, type) {
|
||||
}
|
||||
|
||||
function createProtoView(elementBinders = null) {
|
||||
var pv = new AppProtoView(null, null, MapWrapper.create());
|
||||
var pv = new AppProtoView(null, null, MapWrapper.create(), null);
|
||||
if (isBlank(elementBinders)) {
|
||||
elementBinders = [];
|
||||
}
|
||||
@@ -462,10 +508,18 @@ function createRenderViewportElementBinder(nestedProtoView) {
|
||||
return new renderApi.ElementBinder({nestedProtoView: nestedProtoView});
|
||||
}
|
||||
|
||||
function createRootProtoView(directiveResolver, type) {
|
||||
return createProtoView([createComponentElementBinder(directiveResolver, type)]);
|
||||
}
|
||||
|
||||
@Component({selector: 'main-comp'})
|
||||
class MainComponent {
|
||||
}
|
||||
|
||||
@Component({selector: 'main-comp2'})
|
||||
class MainComponent2 {
|
||||
}
|
||||
|
||||
@Component({selector: 'nested'})
|
||||
class NestedComponent {
|
||||
}
|
||||
|
||||
@@ -12,123 +12,101 @@ import {
|
||||
beforeEachBindings,
|
||||
it,
|
||||
xit,
|
||||
viewRootNodes
|
||||
viewRootNodes,
|
||||
TestComponentBuilder
|
||||
} from 'angular2/test_lib';
|
||||
|
||||
import {TestBed, ViewProxy} from 'angular2/src/test_lib/test_bed';
|
||||
import {Injector} from 'angular2/di';
|
||||
import {Component, View, onDestroy} from 'angular2/annotations';
|
||||
import {Locals} from 'angular2/change_detection';
|
||||
import * as viewAnn from 'angular2/src/core/annotations_impl/view';
|
||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||
import {NgIf} from 'angular2/src/directives/ng_if';
|
||||
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
||||
|
||||
export function main() {
|
||||
describe('DynamicComponentLoader', function() {
|
||||
describe("loading into existing location", () => {
|
||||
function ijTestBed(fn: (ts: TestBed, async: AsyncTestCompleter) => void) {
|
||||
return inject([TestBed, AsyncTestCompleter], fn);
|
||||
}
|
||||
describe("loading into a location", () => {
|
||||
it('should work',
|
||||
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(
|
||||
MyComp,
|
||||
new viewAnn.View(
|
||||
{template: '<location #loc></location>', directives: [Location]}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
|
||||
it('should work', ijTestBed((tb: TestBed, async) => {
|
||||
tb.overrideView(MyComp, new viewAnn.View({
|
||||
template: '<dynamic-comp #dynamic></dynamic-comp>',
|
||||
directives: [DynamicComp]
|
||||
}));
|
||||
loader.loadIntoLocation(DynamicallyLoaded, tc.elementRef, 'loc')
|
||||
.then(ref => {
|
||||
expect(tc.domElement).toHaveText("Location;DynamicallyLoaded;");
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
tb.createView(MyComp).then((view) => {
|
||||
var dynamicComponent = view.rawView.locals.get("dynamic");
|
||||
expect(dynamicComponent).toBeAnInstanceOf(DynamicComp);
|
||||
it('should return a disposable component ref',
|
||||
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(
|
||||
MyComp,
|
||||
new viewAnn.View(
|
||||
{template: '<location #loc></location>', directives: [Location]}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
|
||||
dynamicComponent.done.then((_) => {
|
||||
view.detectChanges();
|
||||
expect(view.rootNodes).toHaveText('hello');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
loader.loadIntoLocation(DynamicallyLoaded, tc.elementRef, 'loc')
|
||||
.then(ref => {
|
||||
ref.dispose();
|
||||
expect(tc.domElement).toHaveText("Location;");
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should inject dependencies of the dynamically-loaded component',
|
||||
ijTestBed((tb: TestBed, async) => {
|
||||
tb.overrideView(MyComp, new viewAnn.View({
|
||||
template: '<dynamic-comp #dynamic></dynamic-comp>',
|
||||
directives: [DynamicComp]
|
||||
}));
|
||||
it('should update host properties',
|
||||
inject(
|
||||
[DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(
|
||||
MyComp, new viewAnn.View(
|
||||
{template: '<location #loc></location>', directives: [Location]}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
loader.loadIntoLocation(DynamicallyLoadedWithHostProps, tc.elementRef, 'loc')
|
||||
.then(ref => {
|
||||
ref.instance.id = "new value";
|
||||
|
||||
tb.createView(MyComp).then((view) => {
|
||||
var dynamicComponent = view.rawView.locals.get("dynamic");
|
||||
dynamicComponent.done.then((ref) => {
|
||||
expect(ref.instance.dynamicallyCreatedComponentService)
|
||||
.toBeAnInstanceOf(DynamicallyCreatedComponentService);
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
tc.detectChanges();
|
||||
|
||||
it('should allow destroying dynamically-loaded components',
|
||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideView(MyComp, new viewAnn.View({
|
||||
template: '<dynamic-comp #dynamic></dynamic-comp>',
|
||||
directives: [DynamicComp]
|
||||
}));
|
||||
var newlyInsertedElement = DOM.childNodes(tc.domElement)[1];
|
||||
expect(newlyInsertedElement.id)
|
||||
.toEqual("new value")
|
||||
|
||||
tb.createView(MyComp).then((view) => {
|
||||
var dynamicComponent = (<Locals>view.rawView.locals).get("dynamic");
|
||||
dynamicComponent.done.then((ref) => {
|
||||
view.detectChanges();
|
||||
expect(view.rootNodes).toHaveText("hello");
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
ref.dispose();
|
||||
|
||||
expect(ref.instance.destroyed).toBe(true);
|
||||
expect(view.rootNodes).toHaveText("");
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
it('should allow to destroy and create them via viewcontainer directives',
|
||||
ijTestBed((tb: TestBed, async) => {
|
||||
tb.overrideView(MyComp, new viewAnn.View({
|
||||
template:
|
||||
'<div><dynamic-comp #dynamic template="ng-if: ctxBoolProp"></dynamic-comp></div>',
|
||||
directives: [DynamicComp, NgIf]
|
||||
}));
|
||||
|
||||
tb.createView(MyComp).then((view) => {
|
||||
view.context.ctxBoolProp = true;
|
||||
view.detectChanges();
|
||||
var dynamicComponent = view.rawView.viewContainers[0].views[0].locals.get("dynamic");
|
||||
var promise = dynamicComponent.done.then((_) => {
|
||||
view.detectChanges();
|
||||
expect(view.rootNodes).toHaveText('hello');
|
||||
|
||||
view.context.ctxBoolProp = false;
|
||||
view.detectChanges();
|
||||
|
||||
expect(view.rawView.viewContainers[0].views.length).toBe(0);
|
||||
expect(view.rootNodes).toHaveText('');
|
||||
|
||||
view.context.ctxBoolProp = true;
|
||||
view.detectChanges();
|
||||
|
||||
var dynamicComponent = view.rawView.viewContainers[0].views[0].locals.get("dynamic");
|
||||
return dynamicComponent.done;
|
||||
});
|
||||
promise.then((_) => {
|
||||
view.detectChanges();
|
||||
expect(view.rootNodes).toHaveText('hello');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
it('should throw if the variable does not exist',
|
||||
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||
(loader, tcb: TestComponentBuilder, async) => {
|
||||
tcb.overrideView(
|
||||
MyComp,
|
||||
new viewAnn.View(
|
||||
{template: '<location #loc></location>', directives: [Location]}))
|
||||
.createAsync(MyComp)
|
||||
.then((tc) => {
|
||||
expect(() => loader.loadIntoLocation(DynamicallyLoadedWithHostProps,
|
||||
tc.elementRef, 'someUnknownVariable'))
|
||||
.toThrowError('Could not find variable someUnknownVariable');
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
describe("loading next to an existing location", () => {
|
||||
describe("loading next to a location", () => {
|
||||
it('should work',
|
||||
inject([DynamicComponentLoader, TestBed, AsyncTestCompleter], (loader, tb: TestBed,
|
||||
async) => {
|
||||
@@ -140,7 +118,7 @@ export function main() {
|
||||
tb.createView(MyComp).then((view) => {
|
||||
var location = view.rawView.locals.get("loc");
|
||||
|
||||
loader.loadNextToExistingLocation(DynamicallyLoaded, location.elementRef)
|
||||
loader.loadNextToLocation(DynamicallyLoaded, location.elementRef)
|
||||
.then(ref => {
|
||||
expect(view.rootNodes).toHaveText("Location;DynamicallyLoaded;");
|
||||
async.done();
|
||||
@@ -158,9 +136,9 @@ export function main() {
|
||||
|
||||
tb.createView(MyComp).then((view) => {
|
||||
var location = view.rawView.locals.get("loc");
|
||||
loader.loadNextToExistingLocation(DynamicallyLoaded, location.elementRef)
|
||||
loader.loadNextToLocation(DynamicallyLoaded, location.elementRef)
|
||||
.then(ref => {
|
||||
loader.loadNextToExistingLocation(DynamicallyLoaded2, location.elementRef)
|
||||
loader.loadNextToLocation(DynamicallyLoaded2, location.elementRef)
|
||||
.then(ref2 => {
|
||||
expect(view.rootNodes)
|
||||
.toHaveText("Location;DynamicallyLoaded;DynamicallyLoaded2;")
|
||||
@@ -187,7 +165,7 @@ export function main() {
|
||||
tb.createView(MyComp).then((view) => {
|
||||
var location = view.rawView.locals.get("loc");
|
||||
|
||||
loader.loadNextToExistingLocation(DynamicallyLoadedWithHostProps, location.elementRef)
|
||||
loader.loadNextToLocation(DynamicallyLoadedWithHostProps, location.elementRef)
|
||||
.then(ref => {
|
||||
ref.instance.id = "new value";
|
||||
|
||||
@@ -203,38 +181,6 @@ export function main() {
|
||||
}));
|
||||
});
|
||||
|
||||
describe('loading into a new location', () => {
|
||||
it('should allow to create, update and destroy components',
|
||||
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
|
||||
tb.overrideView(MyComp, new viewAnn.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();
|
||||
});
|
||||
});
|
||||
}));
|
||||
|
||||
});
|
||||
|
||||
describe('loadAsRoot', () => {
|
||||
it('should allow to create, update and destroy components',
|
||||
inject([TestBed, AsyncTestCompleter, DynamicComponentLoader, DOCUMENT_TOKEN, Injector],
|
||||
@@ -270,25 +216,6 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
@Component({selector: 'imp-ng-cmp'})
|
||||
@View({template: ''})
|
||||
class ImperativeViewComponentUsingNgComponent {
|
||||
done;
|
||||
|
||||
constructor(self: ElementRef, dynamicComponentLoader: DynamicComponentLoader,
|
||||
viewManager: AppViewManager, renderer: DomRenderer) {
|
||||
var div = el('<div id="impHost"></div>');
|
||||
var shadowViewRef = viewManager.getComponentView(self);
|
||||
renderer.setComponentViewRootNodes(shadowViewRef.render, [div]);
|
||||
this.done = dynamicComponentLoader.loadIntoNewLocation(ChildComp, self, null)
|
||||
.then((componentRef) => {
|
||||
var element = renderer.getRootNodes(componentRef.hostView.render)[0];
|
||||
DOM.appendChild(div, element);
|
||||
return componentRef;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'child-cmp',
|
||||
})
|
||||
@@ -301,15 +228,6 @@ class ChildComp {
|
||||
|
||||
class DynamicallyCreatedComponentService {}
|
||||
|
||||
@Component({selector: 'dynamic-comp'})
|
||||
class DynamicComp {
|
||||
done;
|
||||
|
||||
constructor(loader: DynamicComponentLoader, location: ElementRef) {
|
||||
this.done = loader.loadIntoExistingLocation(DynamicallyCreatedCmp, location);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'hello-cmp',
|
||||
appInjector: [DynamicallyCreatedComponentService],
|
||||
|
||||
@@ -671,7 +671,7 @@ export function main() {
|
||||
});
|
||||
|
||||
it("should instantiate directives that depend on pre built objects", () => {
|
||||
var protoView = new AppProtoView(null, null, null);
|
||||
var protoView = new AppProtoView(null, null, null, null);
|
||||
var bindings = ListWrapper.concat([NeedsProtoViewRef], extraBindings);
|
||||
var inj = injector(bindings, null, false, new PreBuiltObjects(null, null, protoView));
|
||||
|
||||
@@ -871,100 +871,6 @@ export function main() {
|
||||
}));
|
||||
});
|
||||
|
||||
describe("dynamicallyCreateComponent", () => {
|
||||
it("should create a component dynamically", () => {
|
||||
var inj = injector(extraBindings);
|
||||
|
||||
inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null),
|
||||
appInjector);
|
||||
expect(inj.getDynamicallyLoadedComponent()).toBeAnInstanceOf(SimpleDirective);
|
||||
expect(inj.get(SimpleDirective)).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should inject parent dependencies into the dynamically-loaded component", () => {
|
||||
var inj = parentChildInjectors(ListWrapper.concat([SimpleDirective], extraBindings), []);
|
||||
inj.dynamicallyCreateComponent(
|
||||
DirectiveBinding.createFromType(NeedsDirectiveFromAncestor, null), appInjector);
|
||||
expect(inj.getDynamicallyLoadedComponent()).toBeAnInstanceOf(NeedsDirectiveFromAncestor);
|
||||
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(
|
||||
DirectiveBinding.createFromType(SomeOtherDirective, null), appInjector);
|
||||
|
||||
var shadowDomProtoInjector =
|
||||
createPei(null, 0, ListWrapper.concat([NeedsDirectiveFromAncestor], extraBindings));
|
||||
var shadowDomInj = shadowDomProtoInjector.instantiate(null);
|
||||
|
||||
expect(() => shadowDomInj.hydrate(appInjector, injWithDynamicallyLoadedComponent,
|
||||
defaultPreBuiltObjects))
|
||||
.toThrowError(containsRegexp(`No provider for ${stringify(SimpleDirective) }`));
|
||||
});
|
||||
|
||||
it("should not inject the dynamically-loaded component into directives on the same element",
|
||||
() => {
|
||||
var dynamicComp =
|
||||
DirectiveBinding.createFromType(SomeOtherDirective, new dirAnn.Component());
|
||||
var proto = createPei(
|
||||
null, 0, ListWrapper.concat([dynamicComp, NeedsDirective], extraBindings), 1, true);
|
||||
var inj = proto.instantiate(null);
|
||||
inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(SimpleDirective, null),
|
||||
appInjector);
|
||||
|
||||
expect(() => inj.hydrate(Injector.resolveAndCreate([]), null, null))
|
||||
.toThrowError(
|
||||
`No provider for SimpleDirective! (${stringify(NeedsDirective) } -> ${stringify(SimpleDirective) })`);
|
||||
});
|
||||
|
||||
it("should inject the dynamically-loaded component into the children of the dynamically-loaded component",
|
||||
() => {
|
||||
var componentDirective = DirectiveBinding.createFromType(SimpleDirective, null);
|
||||
var injWithDynamicallyLoadedComponent = injector([]);
|
||||
injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(componentDirective,
|
||||
appInjector);
|
||||
|
||||
var shadowDomProtoInjector =
|
||||
createPei(null, 0, ListWrapper.concat([NeedsDirectiveFromAncestor], extraBindings));
|
||||
var shadowDomInjector = shadowDomProtoInjector.instantiate(null);
|
||||
shadowDomInjector.hydrate(appInjector, injWithDynamicallyLoadedComponent,
|
||||
defaultPreBuiltObjects);
|
||||
|
||||
expect(shadowDomInjector.get(NeedsDirectiveFromAncestor))
|
||||
.toBeAnInstanceOf(NeedsDirectiveFromAncestor);
|
||||
expect(shadowDomInjector.get(NeedsDirectiveFromAncestor).dependency)
|
||||
.toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should remove the dynamically-loaded component when dehydrating", () => {
|
||||
var inj = injector(extraBindings);
|
||||
inj.dynamicallyCreateComponent(
|
||||
DirectiveBinding.createFromType(DirectiveWithDestroy,
|
||||
new dirAnn.Directive({lifecycle: [onDestroy]})),
|
||||
appInjector);
|
||||
var dir = inj.getDynamicallyLoadedComponent();
|
||||
|
||||
inj.dehydrate();
|
||||
|
||||
expect(inj.getDynamicallyLoadedComponent()).toBe(null);
|
||||
expect(dir.onDestroyCounter).toBe(1);
|
||||
|
||||
inj.hydrate(null, null, null);
|
||||
|
||||
expect(inj.getDynamicallyLoadedComponent()).toBe(null);
|
||||
});
|
||||
|
||||
it("should inject services of the dynamically-loaded component", () => {
|
||||
var inj = injector(extraBindings);
|
||||
var appInjector = Injector.resolveAndCreate([bind("service").toValue("Service")]);
|
||||
inj.dynamicallyCreateComponent(DirectiveBinding.createFromType(NeedsService, null),
|
||||
appInjector);
|
||||
expect(inj.getDynamicallyLoadedComponent().service).toEqual("Service");
|
||||
});
|
||||
});
|
||||
|
||||
describe('static attributes', () => {
|
||||
it('should be injectable', () => {
|
||||
var attributes = MapWrapper.create();
|
||||
@@ -1016,7 +922,7 @@ export function main() {
|
||||
});
|
||||
|
||||
it("should inject ProtoViewRef", () => {
|
||||
var protoView = new AppProtoView(null, null, null);
|
||||
var protoView = new AppProtoView(null, null, null, null);
|
||||
var inj = injector(ListWrapper.concat([NeedsProtoViewRef], extraBindings), null, false,
|
||||
new PreBuiltObjects(null, null, protoView));
|
||||
|
||||
|
||||
@@ -1090,6 +1090,16 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report a meaningful error when a component is missing view annotation',
|
||||
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
|
||||
PromiseWrapper.catchError(tb.createView(ComponentWithoutView, {context: ctx}), (e) => {
|
||||
expect(e.message).toEqual(
|
||||
`No View annotation found on component ${stringify(ComponentWithoutView)}`);
|
||||
async.done();
|
||||
return null;
|
||||
});
|
||||
}));
|
||||
|
||||
it('should report a meaningful error when a directive is null',
|
||||
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
|
||||
|
||||
@@ -1175,7 +1185,7 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should support free embedded views',
|
||||
it('should support moving embedded views around',
|
||||
inject([TestBed, AsyncTestCompleter, ANCHOR_ELEMENT], (tb, async, anchorElement) => {
|
||||
tb.overrideView(MyComp, new viewAnn.View({
|
||||
template: '<div><div *some-impvp="ctxBoolProp">hello</div></div>',
|
||||
@@ -1730,20 +1740,19 @@ class ChildConsumingEventBus {
|
||||
class SomeImperativeViewport {
|
||||
view: ViewRef;
|
||||
anchor;
|
||||
constructor(public element: ElementRef, public protoView: ProtoViewRef,
|
||||
public viewManager: AppViewManager, public renderer: DomRenderer,
|
||||
@Inject(ANCHOR_ELEMENT) anchor) {
|
||||
constructor(public vc: ViewContainerRef, public protoView: ProtoViewRef,
|
||||
public renderer: DomRenderer, @Inject(ANCHOR_ELEMENT) anchor) {
|
||||
this.view = null;
|
||||
this.anchor = anchor;
|
||||
}
|
||||
|
||||
set someImpvp(value: boolean) {
|
||||
if (isPresent(this.view)) {
|
||||
this.viewManager.destroyFreeEmbeddedView(this.element, this.view);
|
||||
this.vc.clear();
|
||||
this.view = null;
|
||||
}
|
||||
if (value) {
|
||||
this.view = this.viewManager.createFreeEmbeddedView(this.element, this.protoView);
|
||||
this.view = this.vc.create(this.protoView);
|
||||
var nodes = this.renderer.getRootNodes(this.view.render);
|
||||
for (var i = 0; i < nodes.length; i++) {
|
||||
DOM.appendChild(this.anchor, nodes[i]);
|
||||
@@ -1755,3 +1764,7 @@ class SomeImperativeViewport {
|
||||
@Directive({selector: '[export-dir]', exportAs: 'dir'})
|
||||
class ExportDir {
|
||||
}
|
||||
|
||||
@Component({selector: 'comp'})
|
||||
class ComponentWithoutView {
|
||||
}
|
||||
|
||||
@@ -21,7 +21,8 @@ import {ChangeDetection, ChangeDetectorDefinition} from 'angular2/change_detecti
|
||||
import {
|
||||
ProtoViewFactory,
|
||||
getChangeDetectorDefinitions,
|
||||
createDirectiveVariableBindings
|
||||
createDirectiveVariableBindings,
|
||||
createVariableLocations
|
||||
} from 'angular2/src/core/compiler/proto_view_factory';
|
||||
import {Component, Directive} from 'angular2/annotations';
|
||||
import {Key} from 'angular2/di';
|
||||
@@ -139,6 +140,18 @@ export function main() {
|
||||
}).not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('createVariableLocations', () => {
|
||||
it('should merge the names in the template for all ElementBinders', () => {
|
||||
expect(createVariableLocations([
|
||||
new renderApi.ElementBinder(
|
||||
{variableBindings: MapWrapper.createFromStringMap({"x": "a"})}),
|
||||
new renderApi.ElementBinder(
|
||||
{variableBindings: MapWrapper.createFromStringMap({"y": "b"})})
|
||||
|
||||
])).toEqual(MapWrapper.createFromStringMap({'a': 0, 'b': 1}));
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ export function main() {
|
||||
|
||||
function wrapView(view: AppView): ViewRef { return new ViewRef(view); }
|
||||
|
||||
function createProtoView() { return new AppProtoView(null, null, null); }
|
||||
function createProtoView() { return new AppProtoView(null, null, null, null); }
|
||||
|
||||
function createView() { return new AppView(null, createProtoView(), MapWrapper.create()); }
|
||||
|
||||
|
||||
@@ -77,7 +77,7 @@ export function main() {
|
||||
staticChildComponentCount++;
|
||||
}
|
||||
}
|
||||
var res = new AppProtoView(new MockProtoViewRef(staticChildComponentCount), null, null);
|
||||
var res = new AppProtoView(new MockProtoViewRef(staticChildComponentCount), null, null, null);
|
||||
res.elementBinders = binders;
|
||||
return res;
|
||||
}
|
||||
@@ -155,139 +155,35 @@ export function main() {
|
||||
viewPool.spy('returnView').andReturn(true);
|
||||
});
|
||||
|
||||
describe('createDynamicComponentView', () => {
|
||||
|
||||
describe('basic functionality', () => {
|
||||
var hostView, componentProtoView;
|
||||
beforeEach(() => {
|
||||
hostView = createView(createProtoView([createComponentElBinder(null)]));
|
||||
componentProtoView = createProtoView();
|
||||
});
|
||||
|
||||
it('should create the view', () => {
|
||||
expect(internalView(manager.createDynamicComponentView(
|
||||
elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null)))
|
||||
.toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(componentProtoView);
|
||||
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(createdViews[0]);
|
||||
});
|
||||
|
||||
it('should get the view from the pool', () => {
|
||||
var createdView;
|
||||
viewPool.spy('getView').andCallFake((protoView) => {
|
||||
createdView = createView(protoView);
|
||||
return createdView;
|
||||
});
|
||||
expect(internalView(manager.createDynamicComponentView(
|
||||
elementRef(wrapView(hostView), 0), wrapPv(componentProtoView), null, null)))
|
||||
.toBe(createdView);
|
||||
expect(utils.spy('createView')).not.toHaveBeenCalled();
|
||||
expect(renderer.spy('createView')).not.toHaveBeenCalled();
|
||||
expect(viewListener.spy('viewCreated')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should attach the view', () => {
|
||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||
wrapPv(componentProtoView), null, null)
|
||||
expect(utils.spy('attachComponentView'))
|
||||
.toHaveBeenCalledWith(hostView, 0, createdViews[0]);
|
||||
expect(renderer.spy('attachComponentView'))
|
||||
.toHaveBeenCalledWith(hostView.render, 0, createdViews[0].render);
|
||||
});
|
||||
|
||||
it('should hydrate the dynamic component', () => {
|
||||
var injector = new Injector([], null, false);
|
||||
var componentBinding = bind(SomeComponent).toClass(SomeComponent);
|
||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||
wrapPv(componentProtoView), componentBinding,
|
||||
injector);
|
||||
expect(utils.spy('hydrateDynamicComponentInElementInjector'))
|
||||
.toHaveBeenCalledWith(hostView, 0, componentBinding, injector);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||
wrapPv(componentProtoView), null, null);
|
||||
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(hostView, 0);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||
});
|
||||
|
||||
it('should create and set the render view', () => {
|
||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||
wrapPv(componentProtoView), null, null);
|
||||
expect(renderer.spy('createView')).toHaveBeenCalledWith(componentProtoView.render);
|
||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||
});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||
wrapPv(componentProtoView), null, null);
|
||||
var cmpView = createdViews[0];
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error cases', () => {
|
||||
|
||||
it('should not allow to use non component indices', () => {
|
||||
var hostView = createView(createProtoView([createEmptyElBinder()]));
|
||||
var componentProtoView = createProtoView();
|
||||
expect(() => manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||
wrapPv(componentProtoView), null, null))
|
||||
.toThrowError('There is no dynamic component directive at element 0');
|
||||
});
|
||||
|
||||
it('should not allow to use static component indices', () => {
|
||||
var hostView = createView(createProtoView([createComponentElBinder(createProtoView())]));
|
||||
var componentProtoView = createProtoView();
|
||||
expect(() => manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||
wrapPv(componentProtoView), null, null))
|
||||
.toThrowError('There is no dynamic component directive at element 0');
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('recursively destroy dynamic child component views', () => {
|
||||
// TODO
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('static child components', () => {
|
||||
|
||||
describe('recursively create when not cached', () => {
|
||||
var hostView, componentProtoView, nestedProtoView;
|
||||
var rootProtoView, hostProtoView, componentProtoView, hostView, componentView;
|
||||
beforeEach(() => {
|
||||
hostView = createView(createProtoView([createComponentElBinder(null)]));
|
||||
nestedProtoView = createProtoView();
|
||||
componentProtoView = createProtoView([createComponentElBinder(nestedProtoView)]);
|
||||
componentProtoView = createProtoView();
|
||||
hostProtoView = createProtoView([createComponentElBinder(componentProtoView)]);
|
||||
rootProtoView = createProtoView([createComponentElBinder(hostProtoView)]);
|
||||
manager.createRootHostView(wrapPv(rootProtoView), null, null);
|
||||
hostView = createdViews[1];
|
||||
componentView = createdViews[2];
|
||||
});
|
||||
|
||||
it('should create the view', () => {
|
||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||
wrapPv(componentProtoView), null, null);
|
||||
expect(createdViews[0].proto).toBe(componentProtoView);
|
||||
expect(createdViews[1].proto).toBe(nestedProtoView);
|
||||
expect(hostView.proto).toBe(hostProtoView);
|
||||
expect(componentView.proto).toBe(componentProtoView);
|
||||
});
|
||||
|
||||
it('should hydrate the view', () => {
|
||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||
wrapPv(componentProtoView), null, null);
|
||||
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(createdViews[0], 0);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(hostView, 0);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(componentView.render);
|
||||
});
|
||||
|
||||
it('should set the render view', () => {
|
||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||
wrapPv(componentProtoView), null, null);
|
||||
expect(createdViews[1].render).toBe(createdRenderViews[1])
|
||||
});
|
||||
it('should set the render view',
|
||||
() => {expect(componentView.render).toBe(createdRenderViews[2])});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
||||
wrapPv(componentProtoView), null, null);
|
||||
var cmpView = createdViews[1];
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||
expect(renderer.spy('setEventDispatcher'))
|
||||
.toHaveBeenCalledWith(componentView.render, componentView);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -302,200 +198,6 @@ export function main() {
|
||||
|
||||
});
|
||||
|
||||
describe('createFreeHostView', () => {
|
||||
|
||||
// Note: We don't add tests for recursion or viewpool here as we assume that
|
||||
// this is using the same mechanism as the other methods...
|
||||
|
||||
describe('basic functionality', () => {
|
||||
var parentHostView, parentView, hostProtoView;
|
||||
beforeEach(() => {
|
||||
parentHostView = createView(createProtoView([createComponentElBinder(null)]));
|
||||
parentView = createView();
|
||||
utils.attachComponentView(parentHostView, 0, parentView);
|
||||
hostProtoView = createProtoView([createComponentElBinder(null)]);
|
||||
});
|
||||
|
||||
it('should create the view', () => {
|
||||
expect(internalView(manager.createFreeHostView(elementRef(wrapView(parentHostView), 0),
|
||||
wrapPv(hostProtoView), null)))
|
||||
.toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(hostProtoView);
|
||||
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(createdViews[0]);
|
||||
});
|
||||
|
||||
it('should attachAndHydrate the view', () => {
|
||||
var injector = new Injector([], null, false);
|
||||
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView),
|
||||
injector);
|
||||
expect(utils.spy('attachAndHydrateFreeHostView'))
|
||||
.toHaveBeenCalledWith(parentHostView, 0, createdViews[0], injector);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||
});
|
||||
|
||||
it('should create and set the render view', () => {
|
||||
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView),
|
||||
null);
|
||||
expect(renderer.spy('createView')).toHaveBeenCalledWith(hostProtoView.render);
|
||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||
});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
manager.createFreeHostView(elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView),
|
||||
null);
|
||||
var cmpView = createdViews[0];
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('destroyFreeHostView', () => {
|
||||
describe('basic functionality', () => {
|
||||
var parentHostView, parentView, hostProtoView, hostView, hostRenderViewRef;
|
||||
beforeEach(() => {
|
||||
parentHostView = createView(createProtoView([createComponentElBinder(null)]));
|
||||
parentView = createView();
|
||||
utils.attachComponentView(parentHostView, 0, parentView);
|
||||
hostProtoView = createProtoView([createComponentElBinder(null)]);
|
||||
hostView = internalView(manager.createFreeHostView(
|
||||
elementRef(wrapView(parentHostView), 0), wrapPv(hostProtoView), null));
|
||||
hostRenderViewRef = hostView.render;
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(utils.spy('detachFreeHostView')).toHaveBeenCalledWith(parentView, hostView);
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(hostView);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(hostView.render);
|
||||
});
|
||||
|
||||
it('should detach the render view', () => {
|
||||
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(renderer.spy('detachFreeView')).toHaveBeenCalledWith(hostRenderViewRef);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(hostView);
|
||||
expect(renderer.spy('destroyView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should destroy the view if the pool is full', () => {
|
||||
viewPool.spy('returnView').andReturn(false);
|
||||
manager.destroyFreeHostView(elementRef(wrapView(parentHostView), 0), wrapView(hostView));
|
||||
expect(renderer.spy('destroyView')).toHaveBeenCalledWith(hostView.render);
|
||||
expect(viewListener.spy('viewDestroyed')).toHaveBeenCalledWith(hostView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('recursively destroy inPlaceHostViews', () => {
|
||||
// TODO
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('createFreeEmbeddedView', () => {
|
||||
|
||||
// Note: We don't add tests for recursion or viewpool here as we assume that
|
||||
// this is using the same mechanism as the other methods...
|
||||
|
||||
describe('basic functionality', () => {
|
||||
var parentView, childProtoView;
|
||||
beforeEach(() => {
|
||||
parentView = createView(createProtoView([createEmptyElBinder()]));
|
||||
childProtoView = createProtoView();
|
||||
});
|
||||
|
||||
it('should create the view', () => {
|
||||
expect(internalView(manager.createFreeEmbeddedView(elementRef(wrapView(parentView), 0),
|
||||
wrapPv(childProtoView), null)))
|
||||
.toBe(createdViews[0]);
|
||||
expect(createdViews[0].proto).toBe(childProtoView);
|
||||
expect(viewListener.spy('viewCreated')).toHaveBeenCalledWith(createdViews[0]);
|
||||
});
|
||||
|
||||
it('should attachAndHydrate the view', () => {
|
||||
var injector = new Injector([], null, false);
|
||||
manager.createFreeEmbeddedView(elementRef(wrapView(parentView), 0),
|
||||
wrapPv(childProtoView), injector);
|
||||
expect(utils.spy('attachAndHydrateFreeEmbeddedView'))
|
||||
.toHaveBeenCalledWith(parentView, 0, createdViews[0], injector);
|
||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
||||
});
|
||||
|
||||
it('should create and set the render view', () => {
|
||||
manager.createFreeEmbeddedView(elementRef(wrapView(parentView), 0),
|
||||
wrapPv(childProtoView), null);
|
||||
expect(renderer.spy('createView')).toHaveBeenCalledWith(childProtoView.render);
|
||||
expect(createdViews[0].render).toBe(createdRenderViews[0]);
|
||||
});
|
||||
|
||||
it('should set the event dispatcher', () => {
|
||||
manager.createFreeEmbeddedView(elementRef(wrapView(parentView), 0),
|
||||
wrapPv(childProtoView), null);
|
||||
var cmpView = createdViews[0];
|
||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
describe('destroyFreeEmbeddedView', () => {
|
||||
describe('basic functionality', () => {
|
||||
var parentView, childProtoView, childView;
|
||||
beforeEach(() => {
|
||||
parentView = createView(createProtoView([createEmptyElBinder()]));
|
||||
childProtoView = createProtoView();
|
||||
childView = internalView(manager.createFreeEmbeddedView(
|
||||
elementRef(wrapView(parentView), 0), wrapPv(childProtoView), null));
|
||||
});
|
||||
|
||||
it('should detach', () => {
|
||||
manager.destroyFreeEmbeddedView(elementRef(wrapView(parentView), 0), wrapView(childView));
|
||||
expect(utils.spy('detachFreeEmbeddedView'))
|
||||
.toHaveBeenCalledWith(parentView, 0, childView);
|
||||
});
|
||||
|
||||
it('should dehydrate', () => {
|
||||
manager.destroyFreeEmbeddedView(elementRef(wrapView(parentView), 0), wrapView(childView));
|
||||
expect(utils.spy('dehydrateView')).toHaveBeenCalledWith(childView);
|
||||
expect(renderer.spy('dehydrateView')).toHaveBeenCalledWith(childView.render);
|
||||
});
|
||||
|
||||
it('should detach the render view', () => {
|
||||
manager.destroyFreeEmbeddedView(elementRef(wrapView(parentView), 0), wrapView(childView));
|
||||
expect(renderer.spy('detachFreeView')).toHaveBeenCalledWith(childView.render);
|
||||
});
|
||||
|
||||
it('should return the view to the pool', () => {
|
||||
manager.destroyFreeEmbeddedView(elementRef(wrapView(parentView), 0), wrapView(childView));
|
||||
expect(viewPool.spy('returnView')).toHaveBeenCalledWith(childView);
|
||||
expect(renderer.spy('destroyView')).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should destroy the view if the pool is full', () => {
|
||||
viewPool.spy('returnView').andReturn(false);
|
||||
manager.destroyFreeEmbeddedView(elementRef(wrapView(parentView), 0), wrapView(childView));
|
||||
expect(renderer.spy('destroyView')).toHaveBeenCalledWith(childView.render);
|
||||
expect(viewListener.spy('viewDestroyed')).toHaveBeenCalledWith(childView);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('recursively destroyFreeEmbeddedView', () => {
|
||||
// TODO
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('createRootHostView', () => {
|
||||
|
||||
var hostProtoView;
|
||||
@@ -698,7 +400,6 @@ export function main() {
|
||||
describe('detachViewInContainer', () => {
|
||||
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -61,13 +61,14 @@ export function main() {
|
||||
if (isBlank(binders)) {
|
||||
binders = [];
|
||||
}
|
||||
var res = new AppProtoView(null, null, null);
|
||||
var res = new AppProtoView(null, null, null, null);
|
||||
res.elementBinders = binders;
|
||||
return res;
|
||||
}
|
||||
|
||||
function createElementInjector() {
|
||||
var host = new SpyElementInjector();
|
||||
var appInjector = new SpyInjector();
|
||||
return SpyObject.stub(new SpyElementInjector(),
|
||||
{
|
||||
'isExportingComponent': false,
|
||||
@@ -75,8 +76,8 @@ export function main() {
|
||||
'getEventEmitterAccessors': [],
|
||||
'getHostActionAccessors': [],
|
||||
'getComponent': null,
|
||||
'getDynamicallyLoadedComponent': null,
|
||||
'getHost': host
|
||||
'getHost': host,
|
||||
'getShadowDomAppInjector': appInjector
|
||||
},
|
||||
{});
|
||||
}
|
||||
@@ -99,21 +100,7 @@ export function main() {
|
||||
|
||||
beforeEach(() => {
|
||||
directiveResolver = new DirectiveResolver();
|
||||
utils = new AppViewManagerUtils(directiveResolver);
|
||||
});
|
||||
|
||||
describe('hydrateDynamicComponentInElementInjector', () => {
|
||||
|
||||
it('should not allow to overwrite an existing component', () => {
|
||||
var hostView = createView(createProtoView([createComponentElBinder(createProtoView())]));
|
||||
var componentBinding = bind(SomeComponent).toClass(SomeComponent);
|
||||
SpyObject.stub(hostView.elementInjectors[0],
|
||||
{'getDynamicallyLoadedComponent': new SomeComponent()});
|
||||
expect(() => utils.hydrateDynamicComponentInElementInjector(hostView, 0, componentBinding,
|
||||
null))
|
||||
.toThrowError('There already is a dynamic component loaded at element 0');
|
||||
});
|
||||
|
||||
utils = new AppViewManagerUtils();
|
||||
});
|
||||
|
||||
describe("hydrateComponentView", () => {
|
||||
@@ -246,6 +233,16 @@ export function main() {
|
||||
childView.preBuiltObjects[0]);
|
||||
});
|
||||
|
||||
it('should use the shadowDomAppInjector of the context elementInjector if there is no host',
|
||||
() => {
|
||||
createViews();
|
||||
parentView.elementInjectors[0].spy('getHost').andReturn(null);
|
||||
utils.hydrateViewInContainer(parentView, 0, parentView, 0, 0, null);
|
||||
expect(childView.rootElementInjectors[0].spy('hydrate'))
|
||||
.toHaveBeenCalledWith(parentView.elementInjectors[0].getShadowDomAppInjector(), null,
|
||||
childView.preBuiltObjects[0]);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('hydrateRootHostView', () => {
|
||||
@@ -295,3 +292,10 @@ class SpyPreBuiltObjects extends SpyObject {
|
||||
constructor() { super(PreBuiltObjects); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||
}
|
||||
|
||||
@proxy
|
||||
@IMPLEMENTS(Injector)
|
||||
class SpyInjector extends SpyObject {
|
||||
constructor() { super(Injector); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ export function main() {
|
||||
|
||||
function createViewPool({capacity}): AppViewPool { return new AppViewPool(capacity); }
|
||||
|
||||
function createProtoView() { return new AppProtoView(null, null, null); }
|
||||
function createProtoView() { return new AppProtoView(null, null, null, null); }
|
||||
|
||||
function createView(pv) { return new AppView(null, pv, MapWrapper.create()); }
|
||||
|
||||
|
||||
@@ -40,20 +40,6 @@ export function main() {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should create and destroy free views',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compiler.compileHost(someComponent)
|
||||
.then((hostProtoViewDto) => {
|
||||
var view = new TestView(tb.renderer.createView(hostProtoViewDto.render));
|
||||
var hostElement = tb.renderer.getRootNodes(view.viewRef)[0];
|
||||
DOM.appendChild(tb.rootEl, hostElement);
|
||||
|
||||
tb.renderer.detachFreeView(view.viewRef);
|
||||
expect(DOM.parentElement(hostElement)).toBeFalsy();
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should attach and detach component views',
|
||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||
tb.compileAll([
|
||||
|
||||
Reference in New Issue
Block a user