feat(view): generalized loading of dynamic components
This commit is contained in:
+2
-4
@@ -98,9 +98,8 @@ export function main() {
|
||||
it('should throw if bootstrapped Directive is not a Component', inject([AsyncTestCompleter], (async) => {
|
||||
var injectorPromise = bootstrap(HelloRootDirectiveIsNotCmp, testBindings, (e,t) => {throw e;});
|
||||
PromiseWrapper.then(injectorPromise, null, (reason) => {
|
||||
expect(reason.message).toContain('Only Components can be bootstrapped; ' +
|
||||
'Directive of HelloRootDirectiveIsNotCmp is not a Component');
|
||||
async.done();
|
||||
expect(reason.message).toContain(`Could not load 'HelloRootDirectiveIsNotCmp' because it is not a component.`);
|
||||
async.done();
|
||||
});
|
||||
}));
|
||||
|
||||
@@ -137,7 +136,6 @@ export function main() {
|
||||
it('should display hello world', inject([AsyncTestCompleter], (async) => {
|
||||
var injectorPromise = bootstrap(HelloRootCmp, testBindings);
|
||||
injectorPromise.then((injector) => {
|
||||
|
||||
expect(injector.get(appElementToken)).toHaveText('hello world!');
|
||||
async.done();
|
||||
});
|
||||
|
||||
+12
-9
@@ -1,6 +1,6 @@
|
||||
import {ddescribe, describe, it, iit, expect, beforeEach} from 'angular2/test_lib';
|
||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||
import {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader';
|
||||
import {DynamicComponentLoader, DirectiveRef} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||
import {Decorator, Viewport} from 'angular2/src/core/annotations/annotations';
|
||||
|
||||
@Decorator({selector: 'someDecorator'})
|
||||
@@ -10,23 +10,26 @@ class SomeDecorator {}
|
||||
class SomeViewport {}
|
||||
|
||||
export function main() {
|
||||
describe("PrivateComponentLoader", () => {
|
||||
describe("DynamicComponentLoader", () => {
|
||||
var loader;
|
||||
|
||||
beforeEach(() => {
|
||||
loader = new PrivateComponentLoader(null, new DirectiveMetadataReader(), null);
|
||||
loader = new DynamicComponentLoader(null, new DirectiveMetadataReader(), null, null);
|
||||
});
|
||||
|
||||
describe('Load errors', () => {
|
||||
it('should throw when trying to load a decorator', () => {
|
||||
expect(() => loader.load(SomeDecorator, null))
|
||||
describe("loadIntoExistingLocation", () => {
|
||||
describe('Load errors', () => {
|
||||
it('should throw when trying to load a decorator', () => {
|
||||
expect(() => loader.loadIntoExistingLocation(SomeDecorator, null))
|
||||
.toThrowError("Could not load 'SomeDecorator' because it is not a component.");
|
||||
});
|
||||
});
|
||||
|
||||
it('should throw when trying to load a viewport', () => {
|
||||
expect(() => loader.load(SomeViewport, null))
|
||||
it('should throw when trying to load a viewport', () => {
|
||||
expect(() => loader.loadIntoExistingLocation(SomeViewport, null))
|
||||
.toThrowError("Could not load 'SomeViewport' because it is not a component.");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
+41
-23
@@ -621,58 +621,76 @@ export function main() {
|
||||
});
|
||||
});
|
||||
|
||||
describe("createPrivateComponent", () => {
|
||||
it("should create a private component", () => {
|
||||
describe("dynamicallyCreateComponent", () => {
|
||||
it("should create a component dynamically", () => {
|
||||
var inj = injector([]);
|
||||
inj.createPrivateComponent(SimpleDirective, null);
|
||||
expect(inj.getPrivateComponent()).toBeAnInstanceOf(SimpleDirective);
|
||||
inj.dynamicallyCreateComponent(SimpleDirective, null, null);
|
||||
expect(inj.getDynamicallyLoadedComponent()).toBeAnInstanceOf(SimpleDirective);
|
||||
expect(inj.get(SimpleDirective)).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should inject parent dependencies into the private component", () => {
|
||||
it("should inject parent dependencies into the dynamically-loaded component", () => {
|
||||
var inj = parentChildInjectors([SimpleDirective], []);
|
||||
inj.createPrivateComponent(NeedDirectiveFromAncestor, null);
|
||||
expect(inj.getPrivateComponent()).toBeAnInstanceOf(NeedDirectiveFromAncestor);
|
||||
expect(inj.getPrivateComponent().dependency).toBeAnInstanceOf(SimpleDirective);
|
||||
inj.dynamicallyCreateComponent(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 private component", () => {
|
||||
var injWithPrivateComponent = injector([SimpleDirective]);
|
||||
injWithPrivateComponent.createPrivateComponent(SomeOtherDirective, null);
|
||||
it("should not inject the proxy component into the children of the dynamically-loaded component", () => {
|
||||
var injWithDynamicallyLoadedComponent = injector([SimpleDirective]);
|
||||
injWithDynamicallyLoadedComponent.dynamicallyCreateComponent(SomeOtherDirective, null, null);
|
||||
|
||||
var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false);
|
||||
var shadowDomInj = shadowDomProtoInjector.instantiate(null);
|
||||
|
||||
expect(() => shadowDomInj.instantiateDirectives(appInjector, injWithPrivateComponent, null, defaultPreBuiltObjects)).
|
||||
expect(() =>
|
||||
shadowDomInj.instantiateDirectives(appInjector, injWithDynamicallyLoadedComponent,null, defaultPreBuiltObjects)).
|
||||
toThrowError(new RegExp("No provider for SimpleDirective"));
|
||||
});
|
||||
|
||||
it("should inject the private component into the children of the private component", () => {
|
||||
var injWithPrivateComponent = injector([]);
|
||||
injWithPrivateComponent.createPrivateComponent(SimpleDirective, null);
|
||||
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);
|
||||
|
||||
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);
|
||||
|
||||
var shadowDomProtoInjector = new ProtoElementInjector(null, 0, [NeedDirectiveFromAncestor], false);
|
||||
var shadowDomInjector = shadowDomProtoInjector.instantiate(null);
|
||||
shadowDomInjector.instantiateDirectives(appInjector, injWithPrivateComponent, null, defaultPreBuiltObjects);
|
||||
shadowDomInjector.instantiateDirectives(appInjector, injWithDynamicallyLoadedComponent, null, defaultPreBuiltObjects);
|
||||
|
||||
expect(shadowDomInjector.get(NeedDirectiveFromAncestor)).toBeAnInstanceOf(NeedDirectiveFromAncestor);
|
||||
expect(shadowDomInjector.get(NeedDirectiveFromAncestor).dependency).toBeAnInstanceOf(SimpleDirective);
|
||||
});
|
||||
|
||||
it("should support rehydrating the private component", () => {
|
||||
it("should remove the dynamically-loaded component when dehydrating", () => {
|
||||
var inj = injector([]);
|
||||
inj.createPrivateComponent(
|
||||
DirectiveWithDestroy,
|
||||
new DummyDirective({lifecycle: [onDestroy]}));
|
||||
var dir = inj.getPrivateComponent();
|
||||
inj.dynamicallyCreateComponent(
|
||||
DirectiveWithDestroy,
|
||||
new DummyDirective({lifecycle: [onDestroy]}),
|
||||
null);
|
||||
var dir = inj.getDynamicallyLoadedComponent();
|
||||
|
||||
inj.clearDirectives();
|
||||
|
||||
expect(inj.getPrivateComponent()).toBe(null);
|
||||
expect(inj.getDynamicallyLoadedComponent()).toBe(null);
|
||||
expect(dir.onDestroyCounter).toBe(1);
|
||||
|
||||
inj.instantiateDirectives(null, null, null, null);
|
||||
|
||||
expect(inj.getPrivateComponent()).not.toBe(null);
|
||||
expect(inj.getDynamicallyLoadedComponent()).toBe(null);
|
||||
});
|
||||
|
||||
it("should inject services of the dynamically-loaded component", () => {
|
||||
var inj = injector([]);
|
||||
var appInjector = new Injector([bind("service").toValue("Service")]);
|
||||
inj.dynamicallyCreateComponent(NeedsService, null, appInjector);
|
||||
expect(inj.getDynamicallyLoadedComponent().service).toEqual("Service");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+43
-23
@@ -24,13 +24,12 @@ import {Injector, bind} from 'angular2/di';
|
||||
import {dynamicChangeDetection,
|
||||
ChangeDetection, DynamicChangeDetection, Pipe, PipeRegistry, BindingPropagationConfig, ON_PUSH} from 'angular2/change_detection';
|
||||
|
||||
import {PrivateComponentLocation} from 'angular2/src/core/compiler/private_component_location';
|
||||
import {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader';
|
||||
|
||||
import {Decorator, Component, Viewport, DynamicComponent} from 'angular2/src/core/annotations/annotations';
|
||||
import {Template} from 'angular2/src/core/annotations/template';
|
||||
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
|
||||
import {EventEmitter, Attribute} from 'angular2/src/core/annotations/di';
|
||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||
import {DirectiveRef} from 'angular2/src/core/compiler/element_injector';
|
||||
|
||||
import {If} from 'angular2/src/directives/if';
|
||||
|
||||
@@ -544,23 +543,40 @@ export function main() {
|
||||
}));
|
||||
}
|
||||
|
||||
it('should support dynamic components', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideTemplate(MyComp, new Template({
|
||||
inline: '<dynamic-comp #dynamic></dynamic-comp>',
|
||||
directives: [DynamicComp]
|
||||
}));
|
||||
tb.createView(MyComp, {context: ctx}).then((view) => {
|
||||
describe("dynamic components", () => {
|
||||
it('should support loading components dynamically', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideTemplate(MyComp, new Template({
|
||||
inline: '<dynamic-comp #dynamic></dynamic-comp>',
|
||||
directives: [DynamicComp]
|
||||
}));
|
||||
|
||||
var dynamicComponent = view.rawView.locals.get("dynamic");
|
||||
expect(dynamicComponent).toBeAnInstanceOf(DynamicComp);
|
||||
tb.createView(MyComp).then((view) => {
|
||||
var dynamicComponent = view.rawView.locals.get("dynamic");
|
||||
expect(dynamicComponent).toBeAnInstanceOf(DynamicComp);
|
||||
|
||||
dynamicComponent.done.then((_) => {
|
||||
view.detectChanges();
|
||||
expect(view.rootNodes).toHaveText('hello');
|
||||
async.done();
|
||||
dynamicComponent.done.then((_) => {
|
||||
view.detectChanges();
|
||||
expect(view.rootNodes).toHaveText('hello');
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
});
|
||||
}));
|
||||
}));
|
||||
|
||||
it('should inject dependencies of the dynamically-loaded component', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideTemplate(MyComp, new Template({
|
||||
inline: '<dynamic-comp #dynamic></dynamic-comp>',
|
||||
directives: [DynamicComp]
|
||||
}));
|
||||
|
||||
tb.createView(MyComp).then((view) => {
|
||||
var dynamicComponent = view.rawView.locals.get("dynamic");
|
||||
dynamicComponent.done.then((ref) => {
|
||||
expect(ref.instance.dynamicallyCreatedComponentService).toBeAnInstanceOf(DynamicallyCreatedComponentService);
|
||||
async.done();
|
||||
});
|
||||
});
|
||||
}));
|
||||
});
|
||||
|
||||
it('should support static attributes', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||
tb.overrideTemplate(MyComp, new Template({
|
||||
@@ -568,7 +584,6 @@ export function main() {
|
||||
directives: [NeedsAttribute]
|
||||
}));
|
||||
tb.createView(MyComp, {context: ctx}).then((view) => {
|
||||
|
||||
var injector = view.rawView.elementInjectors[0];
|
||||
var needsAttribute = injector.get(NeedsAttribute);
|
||||
expect(needsAttribute.typeAttribute).toEqual('text');
|
||||
@@ -643,27 +658,32 @@ export function main() {
|
||||
});
|
||||
}
|
||||
|
||||
class DynamicallyCreatedComponentService {
|
||||
}
|
||||
|
||||
@DynamicComponent({
|
||||
selector: 'dynamic-comp'
|
||||
})
|
||||
class DynamicComp {
|
||||
done;
|
||||
constructor(loader:PrivateComponentLoader, location:PrivateComponentLocation) {
|
||||
this.done = loader.load(HelloCmp, location);
|
||||
constructor(loader:DynamicComponentLoader, self:DirectiveRef) {
|
||||
this.done = loader.loadIntoExistingLocation(DynamicallyCreatedCmp, self);
|
||||
}
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'hello-cmp'
|
||||
selector: 'hello-cmp',
|
||||
services: [DynamicallyCreatedComponentService]
|
||||
})
|
||||
@Template({
|
||||
inline: "{{greeting}}"
|
||||
})
|
||||
class HelloCmp {
|
||||
class DynamicallyCreatedCmp {
|
||||
greeting:string;
|
||||
constructor() {
|
||||
dynamicallyCreatedComponentService:DynamicallyCreatedComponentService;
|
||||
constructor(a:DynamicallyCreatedComponentService) {
|
||||
this.greeting = "hello";
|
||||
this.dynamicallyCreatedComponentService = a;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user