feat(view): implemented loading component next to existing location
This commit is contained in:
parent
77b31ab42f
commit
681d06386d
|
@ -75,7 +75,7 @@ function _injectorBindings(appComponentType): List<Binding> {
|
||||||
// We need to do this here to ensure that we create Testability and
|
// We need to do this here to ensure that we create Testability and
|
||||||
// it's ready on the window for users.
|
// it's ready on the window for users.
|
||||||
registry.registerApplication(appElement, testability);
|
registry.registerApplication(appElement, testability);
|
||||||
return dynamicComponentLoader.loadIntoNewLocation(appElement, appComponentAnnotatedType.type, null, injector);
|
return dynamicComponentLoader.loadIntoNewLocation(appElement, appComponentAnnotatedType.type, injector);
|
||||||
}, [DynamicComponentLoader, Injector, appElementToken, appComponentAnnotatedTypeToken,
|
}, [DynamicComponentLoader, Injector, appElementToken, appComponentAnnotatedTypeToken,
|
||||||
Testability, TestabilityRegistry]),
|
Testability, TestabilityRegistry]),
|
||||||
|
|
||||||
|
|
|
@ -16,11 +16,13 @@ export class ComponentRef {
|
||||||
location:ElementRef;
|
location:ElementRef;
|
||||||
instance:any;
|
instance:any;
|
||||||
componentView:AppView;
|
componentView:AppView;
|
||||||
|
_dispose:Function;
|
||||||
|
|
||||||
constructor(location:ElementRef, instance:any, componentView:AppView){
|
constructor(location:ElementRef, instance:any, componentView:AppView, dispose:Function){
|
||||||
this.location = location;
|
this.location = location;
|
||||||
this.instance = instance;
|
this.instance = instance;
|
||||||
this.componentView = componentView;
|
this.componentView = componentView;
|
||||||
|
this._dispose = dispose;
|
||||||
}
|
}
|
||||||
|
|
||||||
get injector() {
|
get injector() {
|
||||||
|
@ -30,6 +32,10 @@ export class ComponentRef {
|
||||||
get hostView() {
|
get hostView() {
|
||||||
return this.location.hostView;
|
return this.location.hostView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dispose() {
|
||||||
|
this._dispose();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -72,16 +78,16 @@ export class DynamicComponentLoader {
|
||||||
// from the component child views
|
// from the component child views
|
||||||
// See ViewFactory.returnView
|
// See ViewFactory.returnView
|
||||||
// See AppViewHydrator.dehydrateDynamicComponentView
|
// See AppViewHydrator.dehydrateDynamicComponentView
|
||||||
return new ComponentRef(location, location.elementInjector.getDynamicallyLoadedComponent(), componentView);
|
var dispose = () => {throw "Not implemented";};
|
||||||
|
return new ComponentRef(location, location.elementInjector.getDynamicallyLoadedComponent(), componentView, dispose);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a component as a child of the View given by the provided ElementRef. The loaded
|
* Loads a component in the element specified by elementOrSelector. The loaded component receives
|
||||||
* component receives injection normally as a hosted view.
|
* injection normally as a hosted view.
|
||||||
*/
|
*/
|
||||||
loadIntoNewLocation(elementOrSelector:any, type:Type, location:ElementRef,
|
loadIntoNewLocation(elementOrSelector:any, type:Type, injector:Injector = null):Promise<ComponentRef> {
|
||||||
injector:Injector = null):Promise<ComponentRef> {
|
|
||||||
this._assertTypeIsComponent(type);
|
this._assertTypeIsComponent(type);
|
||||||
|
|
||||||
return this._compiler.compileInHost(type).then(hostProtoView => {
|
return this._compiler.compileInHost(type).then(hostProtoView => {
|
||||||
|
@ -91,9 +97,30 @@ export class DynamicComponentLoader {
|
||||||
// TODO(vsavkin): return a component ref that dehydrates the host view
|
// TODO(vsavkin): return a component ref that dehydrates the host view
|
||||||
// See ViewFactory.returnView
|
// See ViewFactory.returnView
|
||||||
// See AppViewHydrator.dehydrateInPlaceHostView
|
// See AppViewHydrator.dehydrateInPlaceHostView
|
||||||
var newLocation = new ElementRef(hostView.elementInjectors[0]);
|
var newLocation = hostView.elementInjectors[0].getElementRef();
|
||||||
var component = hostView.elementInjectors[0].getComponent();
|
var component = hostView.elementInjectors[0].getComponent();
|
||||||
return new ComponentRef(newLocation, component, hostView.componentChildViews[0]);
|
var dispose = () => {throw "Not implemented";};
|
||||||
|
return new ComponentRef(newLocation, component, hostView.componentChildViews[0], dispose);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads a component next to the provided ElementRef. The loaded component receives
|
||||||
|
* injection normally as a hosted view.
|
||||||
|
*/
|
||||||
|
loadNextToExistingLocation(type:Type, location:ElementRef, injector:Injector = null):Promise<ComponentRef> {
|
||||||
|
this._assertTypeIsComponent(type);
|
||||||
|
|
||||||
|
return this._compiler.compileInHost(type).then(hostProtoView => {
|
||||||
|
var hostView = location.viewContainer.create(-1, hostProtoView, injector);
|
||||||
|
|
||||||
|
var newLocation = hostView.elementInjectors[0].getElementRef();
|
||||||
|
var component = hostView.elementInjectors[0].getComponent();
|
||||||
|
var dispose = () => {
|
||||||
|
var index = location.viewContainer.indexOf(hostView);
|
||||||
|
location.viewContainer.remove(index);
|
||||||
|
};
|
||||||
|
return new ComponentRef(newLocation, component, hostView.componentChildViews[0], dispose);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -672,6 +672,10 @@ export class ElementInjector extends TreeNode {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getElementRef() {
|
||||||
|
return new ElementRef(this);
|
||||||
|
}
|
||||||
|
|
||||||
getDynamicallyLoadedComponent() {
|
getDynamicallyLoadedComponent() {
|
||||||
return this._dynamicallyCreatedComponent;
|
return this._dynamicallyCreatedComponent;
|
||||||
}
|
}
|
||||||
|
@ -741,7 +745,7 @@ export class ElementInjector extends TreeNode {
|
||||||
if (isPresent(dep.attributeName)) return this._buildAttribute(dep);
|
if (isPresent(dep.attributeName)) return this._buildAttribute(dep);
|
||||||
if (isPresent(dep.queryDirective)) return this._findQuery(dep.queryDirective).list;
|
if (isPresent(dep.queryDirective)) return this._findQuery(dep.queryDirective).list;
|
||||||
if (dep.key.id === StaticKeys.instance().elementRefId) {
|
if (dep.key.id === StaticKeys.instance().elementRefId) {
|
||||||
return new ElementRef(this);
|
return this.getElementRef();
|
||||||
}
|
}
|
||||||
return this._getByKey(dep.key, dep.depth, dep.optional, requestor);
|
return this._getByKey(dep.key, dep.depth, dep.optional, requestor);
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,10 @@ export class ViewContainer {
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
indexOf(view:viewModule.AppView) {
|
||||||
|
return ListWrapper.indexOf(this._views, view);
|
||||||
|
}
|
||||||
|
|
||||||
remove(atIndex=-1) {
|
remove(atIndex=-1) {
|
||||||
if (atIndex == -1) atIndex = this._views.length - 1;
|
if (atIndex == -1) atIndex = this._views.length - 1;
|
||||||
var view = this._views[atIndex];
|
var view = this._views[atIndex];
|
||||||
|
|
|
@ -129,6 +129,9 @@ export class ListWrapper {
|
||||||
static filter(array, pred:Function) {
|
static filter(array, pred:Function) {
|
||||||
return array.filter(pred);
|
return array.filter(pred);
|
||||||
}
|
}
|
||||||
|
static indexOf(array, value, startIndex = -1) {
|
||||||
|
return array.indexOf(value, startIndex);
|
||||||
|
}
|
||||||
static any(list:List, pred:Function) {
|
static any(list:List, pred:Function) {
|
||||||
for (var i = 0 ; i < list.length; ++i) {
|
for (var i = 0 ; i < list.length; ++i) {
|
||||||
if (pred(list[i])) return true;
|
if (pred(list[i])) return true;
|
||||||
|
|
|
@ -114,6 +114,9 @@ export class ListWrapper {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
static indexOf(array: List<any>, value, startIndex = -1) {
|
||||||
|
return array.indexOf(value, startIndex);
|
||||||
|
}
|
||||||
static reduce<T>(list: List<T>,
|
static reduce<T>(list: List<T>,
|
||||||
fn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T,
|
fn: (previousValue: T, currentValue: T, currentIndex: number, array: T[]) => T,
|
||||||
init: T) {
|
init: T) {
|
||||||
|
|
|
@ -11,118 +11,194 @@ import {
|
||||||
inject,
|
inject,
|
||||||
beforeEachBindings,
|
beforeEachBindings,
|
||||||
it,
|
it,
|
||||||
xit,
|
xit
|
||||||
SpyObject, proxy
|
} from 'angular2/test_lib';
|
||||||
} from 'angular2/test_lib';
|
|
||||||
import {IMPLEMENTS} from 'angular2/src/facade/lang';
|
import {TestBed} from 'angular2/src/test_lib/test_bed';
|
||||||
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
|
||||||
import {Promise, PromiseWrapper} from 'angular2/src/facade/async';
|
import {Decorator, Component, Viewport, DynamicComponent} from 'angular2/src/core/annotations/annotations';
|
||||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
import {View} from 'angular2/src/core/annotations/view';
|
||||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||||
import {Decorator, Viewport, Component} from 'angular2/src/core/annotations/annotations';
|
import {ElementRef} from 'angular2/src/core/compiler/element_injector';
|
||||||
import {ElementRef, ElementInjector, ProtoElementInjector, PreBuiltObjects} from 'angular2/src/core/compiler/element_injector';
|
import {If} from 'angular2/src/directives/if';
|
||||||
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 {AppViewHydrator} from 'angular2/src/core/compiler/view_hydrator';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe("DynamicComponentLoader", () => {
|
describe('DynamicComponentLoader', function () {
|
||||||
var compiler;
|
describe("loading into existing location", () => {
|
||||||
var viewFactory;
|
it('should work', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
var directiveMetadataReader;
|
tb.overrideView(MyComp, new View({
|
||||||
var viewHydrator;
|
template: '<dynamic-comp #dynamic></dynamic-comp>',
|
||||||
var loader;
|
directives: [DynamicComp]
|
||||||
|
}));
|
||||||
|
|
||||||
beforeEach( () => {
|
tb.createView(MyComp).then((view) => {
|
||||||
compiler = new SpyCompiler();
|
var dynamicComponent = view.rawView.locals.get("dynamic");
|
||||||
viewFactory = new SpyViewFactory();
|
expect(dynamicComponent).toBeAnInstanceOf(DynamicComp);
|
||||||
viewHydrator = new SpyAppViewHydrator();
|
|
||||||
directiveMetadataReader = new DirectiveMetadataReader();
|
|
||||||
loader = new DynamicComponentLoader(compiler, directiveMetadataReader, viewFactory, viewHydrator);
|
|
||||||
});
|
|
||||||
|
|
||||||
function createProtoView() {
|
dynamicComponent.done.then((_) => {
|
||||||
return new AppProtoView(null, null);
|
view.detectChanges();
|
||||||
}
|
expect(view.rootNodes).toHaveText('hello');
|
||||||
|
|
||||||
function createEmptyView() {
|
|
||||||
var view = new AppView(null, null, null, createProtoView(), MapWrapper.create());
|
|
||||||
view.init(null, [], [], [], []);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
function createElementRef(view, boundElementIndex) {
|
|
||||||
var peli = new ProtoElementInjector(null, boundElementIndex, []);
|
|
||||||
var eli = new ElementInjector(peli, null);
|
|
||||||
var preBuiltObjects = new PreBuiltObjects(view, null, null);
|
|
||||||
eli.instantiateDirectives(null, null, null, preBuiltObjects);
|
|
||||||
return new ElementRef(eli);
|
|
||||||
}
|
|
||||||
|
|
||||||
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.loadIntoExistingLocation(SomeViewport, null))
|
|
||||||
.toThrowError("Could not load 'SomeViewport' because it is not a component.");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should compile, create and hydrate the view', inject([AsyncTestCompleter], (async) => {
|
|
||||||
var log = [];
|
|
||||||
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]);
|
|
||||||
});
|
|
||||||
viewFactory.spy('getView').andCallFake( (protoView) => {
|
|
||||||
ListWrapper.push(log, ['getView', protoView]);
|
|
||||||
return childView;
|
|
||||||
});
|
|
||||||
compiler.spy('compile').andCallFake( (_) => PromiseWrapper.resolve(protoView));
|
|
||||||
|
|
||||||
var elementRef = createElementRef(hostView, 23);
|
|
||||||
loader.loadIntoExistingLocation(SomeComponent, elementRef).then( (componentRef) => {
|
|
||||||
expect(log[0]).toEqual(['getView', protoView]);
|
|
||||||
expect(log[1]).toEqual(['hydrateDynamicComponentView', hostView, 23, childView]);
|
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
it('should inject dependencies of the dynamically-loaded component', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
tb.overrideView(MyComp, new View({
|
||||||
|
template: '<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 allow to destroy and create them via viewport directives',
|
||||||
|
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
||||||
|
tb.overrideView(MyComp, new View({
|
||||||
|
template: '<div><dynamic-comp #dynamic template="if: ctxBoolProp"></dynamic-comp></div>',
|
||||||
|
directives: [DynamicComp, If]
|
||||||
|
}));
|
||||||
|
|
||||||
|
tb.createView(MyComp).then((view) => {
|
||||||
|
view.context.ctxBoolProp = true;
|
||||||
|
view.detectChanges();
|
||||||
|
var dynamicComponent = view.rawView.viewContainers[0].get(0).locals.get("dynamic");
|
||||||
|
dynamicComponent.done.then((_) => {
|
||||||
|
view.detectChanges();
|
||||||
|
expect(view.rootNodes).toHaveText('hello');
|
||||||
|
|
||||||
|
view.context.ctxBoolProp = false;
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
expect(view.rawView.viewContainers[0].length).toBe(0);
|
||||||
|
expect(view.rootNodes).toHaveText('');
|
||||||
|
|
||||||
|
view.context.ctxBoolProp = true;
|
||||||
|
view.detectChanges();
|
||||||
|
|
||||||
|
var dynamicComponent = view.rawView.viewContainers[0].get(0).locals.get("dynamic");
|
||||||
|
return dynamicComponent.done;
|
||||||
|
}).then((_) => {
|
||||||
|
view.detectChanges();
|
||||||
|
expect(view.rootNodes).toHaveText('hello');
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("loading next to an existing location", () => {
|
||||||
|
it('should work', inject([DynamicComponentLoader, TestBed, AsyncTestCompleter],
|
||||||
|
(loader, tb, async) => {
|
||||||
|
tb.overrideView(MyComp, new View({
|
||||||
|
template: '<div><location #loc></location></div>',
|
||||||
|
directives: [Location]
|
||||||
|
}));
|
||||||
|
|
||||||
|
tb.createView(MyComp).then((view) => {
|
||||||
|
var location = view.rawView.locals.get("loc");
|
||||||
|
|
||||||
|
loader.loadNextToExistingLocation(DynamicallyLoaded, location.elementRef).then(ref => {
|
||||||
|
expect(view.rootNodes).toHaveText("Location;DynamicallyLoaded;")
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should return a disposable component ref', inject([DynamicComponentLoader, TestBed, AsyncTestCompleter],
|
||||||
|
(loader, tb, async) => {
|
||||||
|
tb.overrideView(MyComp, new View({
|
||||||
|
template: '<div><location #loc></location></div>',
|
||||||
|
directives: [Location]
|
||||||
|
}));
|
||||||
|
|
||||||
|
tb.createView(MyComp).then((view) => {
|
||||||
|
var location = view.rawView.locals.get("loc");
|
||||||
|
loader.loadNextToExistingLocation(DynamicallyLoaded, location.elementRef).then(ref => {
|
||||||
|
loader.loadNextToExistingLocation(DynamicallyLoaded2, location.elementRef).then(ref2 => {
|
||||||
|
expect(view.rootNodes).toHaveText("Location;DynamicallyLoaded;DynamicallyLoaded2;")
|
||||||
|
|
||||||
|
ref2.dispose();
|
||||||
|
|
||||||
|
expect(view.rootNodes).toHaveText("Location;DynamicallyLoaded;")
|
||||||
|
|
||||||
|
async.done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Decorator({selector: 'someDecorator'})
|
|
||||||
class SomeDecorator {}
|
|
||||||
|
|
||||||
@Viewport({selector: 'someViewport'})
|
class DynamicallyCreatedComponentService {
|
||||||
class SomeViewport {}
|
}
|
||||||
|
|
||||||
@Component({selector: 'someComponent'})
|
@DynamicComponent({
|
||||||
class SomeComponent {}
|
selector: 'dynamic-comp'
|
||||||
|
})
|
||||||
|
class DynamicComp {
|
||||||
|
done;
|
||||||
|
|
||||||
|
constructor(loader:DynamicComponentLoader, location:ElementRef) {
|
||||||
|
this.done = loader.loadIntoExistingLocation(DynamicallyCreatedCmp, location);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@proxy
|
@Component({
|
||||||
@IMPLEMENTS(Compiler)
|
selector: 'hello-cmp',
|
||||||
class SpyCompiler extends SpyObject {noSuchMethod(m){return super.noSuchMethod(m)}}
|
injectables: [DynamicallyCreatedComponentService]
|
||||||
|
})
|
||||||
|
@View({
|
||||||
|
template: "{{greeting}}"
|
||||||
|
})
|
||||||
|
class DynamicallyCreatedCmp {
|
||||||
|
greeting:string;
|
||||||
|
dynamicallyCreatedComponentService:DynamicallyCreatedComponentService;
|
||||||
|
|
||||||
@proxy
|
constructor(a:DynamicallyCreatedComponentService) {
|
||||||
@IMPLEMENTS(ViewFactory)
|
this.greeting = "hello";
|
||||||
class SpyViewFactory extends SpyObject {noSuchMethod(m){return super.noSuchMethod(m)}}
|
this.dynamicallyCreatedComponentService = a;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@proxy
|
@Component({selector: 'dummy'})
|
||||||
@IMPLEMENTS(AppViewHydrator)
|
@View({template: "DynamicallyLoaded;"})
|
||||||
class SpyAppViewHydrator extends SpyObject {noSuchMethod(m){return super.noSuchMethod(m)}}
|
class DynamicallyLoaded {
|
||||||
|
}
|
||||||
|
|
||||||
@proxy
|
@Component({selector: 'dummy'})
|
||||||
@IMPLEMENTS(AppView)
|
@View({template: "DynamicallyLoaded2;"})
|
||||||
class SpyAppView extends SpyObject {noSuchMethod(m){return super.noSuchMethod(m)}}
|
class DynamicallyLoaded2 {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'location'
|
||||||
|
})
|
||||||
|
@View({template: "Location;"})
|
||||||
|
class Location {
|
||||||
|
elementRef:ElementRef;
|
||||||
|
|
||||||
|
constructor(elementRef:ElementRef) {
|
||||||
|
this.elementRef = elementRef;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Component()
|
||||||
|
@View({
|
||||||
|
directives: []
|
||||||
|
})
|
||||||
|
class MyComp {
|
||||||
|
ctxBoolProp:boolean;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.ctxBoolProp = false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,8 +28,6 @@ import {Decorator, Component, Viewport, DynamicComponent} from 'angular2/src/cor
|
||||||
import {View} from 'angular2/src/core/annotations/view';
|
import {View} from 'angular2/src/core/annotations/view';
|
||||||
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
|
import {Parent, Ancestor} from 'angular2/src/core/annotations/visibility';
|
||||||
import {Attribute} from 'angular2/src/core/annotations/di';
|
import {Attribute} from 'angular2/src/core/annotations/di';
|
||||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
|
||||||
import {ElementRef} from 'angular2/src/core/compiler/element_injector';
|
|
||||||
|
|
||||||
import {If} from 'angular2/src/directives/if';
|
import {If} from 'angular2/src/directives/if';
|
||||||
|
|
||||||
|
@ -643,75 +641,6 @@ export function main() {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
describe("dynamic components", () => {
|
|
||||||
it('should support loading components dynamically', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
|
||||||
tb.overrideView(MyComp, new View({
|
|
||||||
template: '<dynamic-comp #dynamic></dynamic-comp>',
|
|
||||||
directives: [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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should inject dependencies of the dynamically-loaded component', inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
|
||||||
tb.overrideView(MyComp, new View({
|
|
||||||
template: '<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 allow to destroy and create them via viewport directives',
|
|
||||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
|
||||||
tb.overrideView(MyComp, new View({
|
|
||||||
template: '<div><dynamic-comp #dynamic template="if: ctxBoolProp"></dynamic-comp></div>',
|
|
||||||
directives: [DynamicComp, If]
|
|
||||||
}));
|
|
||||||
|
|
||||||
tb.createView(MyComp).then((view) => {
|
|
||||||
view.context.ctxBoolProp = true;
|
|
||||||
view.detectChanges();
|
|
||||||
var dynamicComponent = view.rawView.viewContainers[0].get(0).locals.get("dynamic");
|
|
||||||
dynamicComponent.done.then((_) => {
|
|
||||||
view.detectChanges();
|
|
||||||
expect(view.rootNodes).toHaveText('hello');
|
|
||||||
|
|
||||||
view.context.ctxBoolProp = false;
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
expect(view.rawView.viewContainers[0].length).toBe(0);
|
|
||||||
expect(view.rootNodes).toHaveText('');
|
|
||||||
|
|
||||||
view.context.ctxBoolProp = true;
|
|
||||||
view.detectChanges();
|
|
||||||
|
|
||||||
var dynamicComponent = view.rawView.viewContainers[0].get(0).locals.get("dynamic");
|
|
||||||
return dynamicComponent.done;
|
|
||||||
}).then((_) => {
|
|
||||||
view.detectChanges();
|
|
||||||
expect(view.rootNodes).toHaveText('hello');
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('dynamic ViewContainers', () => {
|
describe('dynamic ViewContainers', () => {
|
||||||
|
|
||||||
it('should allow to create a ViewContainer at any bound location',
|
it('should allow to create a ViewContainer at any bound location',
|
||||||
|
@ -868,34 +797,6 @@ class DynamicViewport {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DynamicallyCreatedComponentService {}
|
|
||||||
|
|
||||||
@DynamicComponent({
|
|
||||||
selector: 'dynamic-comp'
|
|
||||||
})
|
|
||||||
class DynamicComp {
|
|
||||||
done;
|
|
||||||
constructor(loader:DynamicComponentLoader, location:ElementRef) {
|
|
||||||
this.done = loader.loadIntoExistingLocation(DynamicallyCreatedCmp, location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: 'hello-cmp',
|
|
||||||
injectables: [DynamicallyCreatedComponentService]
|
|
||||||
})
|
|
||||||
@View({
|
|
||||||
template: "{{greeting}}"
|
|
||||||
})
|
|
||||||
class DynamicallyCreatedCmp {
|
|
||||||
greeting:string;
|
|
||||||
dynamicallyCreatedComponentService:DynamicallyCreatedComponentService;
|
|
||||||
constructor(a:DynamicallyCreatedComponentService) {
|
|
||||||
this.greeting = "hello";
|
|
||||||
this.dynamicallyCreatedComponentService = a;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Decorator({
|
@Decorator({
|
||||||
selector: '[my-dir]',
|
selector: '[my-dir]',
|
||||||
properties: {'dirProp':'elprop'}
|
properties: {'dirProp':'elprop'}
|
||||||
|
|
Loading…
Reference in New Issue