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:
parent
df6acedd25
commit
5dee8e26cc
|
@ -848,54 +848,6 @@ export interface ComponentArgs {
|
||||||
* ```
|
* ```
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* Dynamically loading a component at runtime:
|
|
||||||
*
|
|
||||||
* Regular Angular components are statically resolved. Dynamic components allows to resolve a
|
|
||||||
* component at runtime
|
|
||||||
* instead by providing a placeholder into which a regular Angular component can be dynamically
|
|
||||||
* loaded. Once loaded,
|
|
||||||
* the dynamically-loaded component becomes permanent and cannot be changed.
|
|
||||||
* Dynamic components are declared just like components, but without a `@View` annotation.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* ## Example
|
|
||||||
*
|
|
||||||
* Here we have `DynamicComp` which acts as the placeholder for `HelloCmp`. At runtime, the dynamic
|
|
||||||
* component
|
|
||||||
* `DynamicComp` requests loading of the `HelloCmp` component.
|
|
||||||
*
|
|
||||||
* There is nothing special about `HelloCmp`, which is a regular Angular component. It can also be
|
|
||||||
* used in other static
|
|
||||||
* locations.
|
|
||||||
*
|
|
||||||
* ```
|
|
||||||
* @Component({
|
|
||||||
* selector: 'dynamic-comp'
|
|
||||||
* })
|
|
||||||
* class DynamicComp {
|
|
||||||
* helloCmp:HelloCmp;
|
|
||||||
* constructor(loader:DynamicComponentLoader, location:ElementRef) {
|
|
||||||
* loader.load(HelloCmp, location).then((helloCmp) => {
|
|
||||||
* this.helloCmp = helloCmp;
|
|
||||||
* });
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
*
|
|
||||||
* @Component({
|
|
||||||
* selector: 'hello-cmp'
|
|
||||||
* })
|
|
||||||
* @View({
|
|
||||||
* template: "{{greeting}}"
|
|
||||||
* })
|
|
||||||
* class HelloCmp {
|
|
||||||
* greeting:string;
|
|
||||||
* constructor() {
|
|
||||||
* this.greeting = "hello";
|
|
||||||
* }
|
|
||||||
* }
|
|
||||||
* ```
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* @exportedAs angular2/annotations
|
* @exportedAs angular2/annotations
|
||||||
*/
|
*/
|
||||||
@CONST()
|
@CONST()
|
||||||
|
|
|
@ -33,6 +33,7 @@ import * as renderApi from 'angular2/src/render/api';
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class CompilerCache {
|
export class CompilerCache {
|
||||||
_cache: Map<Type, AppProtoView> = MapWrapper.create();
|
_cache: Map<Type, AppProtoView> = MapWrapper.create();
|
||||||
|
_hostCache: Map<Type, AppProtoView> = MapWrapper.create();
|
||||||
|
|
||||||
set(component: Type, protoView: AppProtoView): void {
|
set(component: Type, protoView: AppProtoView): void {
|
||||||
MapWrapper.set(this._cache, component, protoView);
|
MapWrapper.set(this._cache, component, protoView);
|
||||||
|
@ -43,7 +44,19 @@ export class CompilerCache {
|
||||||
return normalizeBlank(result);
|
return normalizeBlank(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
clear(): void { MapWrapper.clear(this._cache); }
|
setHost(component: Type, protoView: AppProtoView): void {
|
||||||
|
MapWrapper.set(this._hostCache, component, protoView);
|
||||||
|
}
|
||||||
|
|
||||||
|
getHost(component: Type): AppProtoView {
|
||||||
|
var result = MapWrapper.get(this._hostCache, component);
|
||||||
|
return normalizeBlank(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
clear(): void {
|
||||||
|
MapWrapper.clear(this._cache);
|
||||||
|
MapWrapper.clear(this._hostCache);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -94,20 +107,19 @@ export class Compiler {
|
||||||
Compiler._assertTypeIsComponent(componentBinding);
|
Compiler._assertTypeIsComponent(componentBinding);
|
||||||
|
|
||||||
var directiveMetadata = componentBinding.metadata;
|
var directiveMetadata = componentBinding.metadata;
|
||||||
return this._render.compileHost(directiveMetadata)
|
var hostPvPromise;
|
||||||
.then((hostRenderPv) => {
|
var component = <Type>componentBinding.key.token;
|
||||||
return this._compileNestedProtoViews(componentBinding, hostRenderPv, [componentBinding]);
|
var hostAppProtoView = this._compilerCache.getHost(component);
|
||||||
})
|
if (isPresent(hostAppProtoView)) {
|
||||||
.then((appProtoView) => { return new ProtoViewRef(appProtoView); });
|
hostPvPromise = PromiseWrapper.resolve(hostAppProtoView);
|
||||||
}
|
} else {
|
||||||
|
hostPvPromise = this._render.compileHost(directiveMetadata)
|
||||||
compile(component: Type): Promise<ProtoViewRef> {
|
.then((hostRenderPv) => {
|
||||||
var componentBinding = this._bindDirective(component);
|
return this._compileNestedProtoViews(componentBinding, hostRenderPv,
|
||||||
Compiler._assertTypeIsComponent(componentBinding);
|
[componentBinding]);
|
||||||
var pvOrPromise = this._compile(componentBinding);
|
});
|
||||||
var pvPromise = isPromise(pvOrPromise) ? <Promise<AppProtoView>>pvOrPromise :
|
}
|
||||||
PromiseWrapper.resolve(pvOrPromise);
|
return hostPvPromise.then((hostAppProtoView) => { return new ProtoViewRef(hostAppProtoView); });
|
||||||
return pvPromise.then((appProtoView) => { return new ProtoViewRef(appProtoView); });
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private _compile(componentBinding: DirectiveBinding): Promise<AppProtoView>| AppProtoView {
|
private _compile(componentBinding: DirectiveBinding): Promise<AppProtoView>| AppProtoView {
|
||||||
|
@ -128,9 +140,6 @@ export class Compiler {
|
||||||
return pvPromise;
|
return pvPromise;
|
||||||
}
|
}
|
||||||
var template = this._templateResolver.resolve(component);
|
var template = this._templateResolver.resolve(component);
|
||||||
if (isBlank(template)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var directives = this._flattenDirectives(template);
|
var directives = this._flattenDirectives(template);
|
||||||
|
|
||||||
|
@ -160,16 +169,17 @@ export class Compiler {
|
||||||
var protoViews =
|
var protoViews =
|
||||||
this._protoViewFactory.createAppProtoViews(componentBinding, renderPv, directives);
|
this._protoViewFactory.createAppProtoViews(componentBinding, renderPv, directives);
|
||||||
var protoView = protoViews[0];
|
var protoView = protoViews[0];
|
||||||
// TODO(tbosch): we should be caching host protoViews as well!
|
if (isPresent(componentBinding)) {
|
||||||
// -> need a separate cache for this...
|
|
||||||
if (renderPv.type === renderApi.ViewType.COMPONENT && isPresent(componentBinding)) {
|
|
||||||
// Populate the cache before compiling the nested components,
|
|
||||||
// so that components can reference themselves in their template.
|
|
||||||
var component = componentBinding.key.token;
|
var component = componentBinding.key.token;
|
||||||
this._compilerCache.set(component, protoView);
|
if (renderPv.type === renderApi.ViewType.COMPONENT) {
|
||||||
MapWrapper.delete(this._compiling, component);
|
// Populate the cache before compiling the nested components,
|
||||||
|
// so that components can reference themselves in their template.
|
||||||
|
this._compilerCache.set(component, protoView);
|
||||||
|
MapWrapper.delete(this._compiling, component);
|
||||||
|
} else {
|
||||||
|
this._compilerCache.setHost(component, protoView);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var nestedPVPromises = [];
|
var nestedPVPromises = [];
|
||||||
ListWrapper.forEach(this._collectComponentElementBinders(protoViews), (elementBinder) => {
|
ListWrapper.forEach(this._collectComponentElementBinders(protoViews), (elementBinder) => {
|
||||||
var nestedComponent = elementBinder.componentDirective;
|
var nestedComponent = elementBinder.componentDirective;
|
||||||
|
@ -179,7 +189,7 @@ export class Compiler {
|
||||||
if (isPromise(nestedCall)) {
|
if (isPromise(nestedCall)) {
|
||||||
ListWrapper.push(nestedPVPromises,
|
ListWrapper.push(nestedPVPromises,
|
||||||
(<Promise<AppProtoView>>nestedCall).then(elementBinderDone));
|
(<Promise<AppProtoView>>nestedCall).then(elementBinderDone));
|
||||||
} else if (isPresent(nestedCall)) {
|
} else {
|
||||||
elementBinderDone(<AppProtoView>nestedCall);
|
elementBinderDone(<AppProtoView>nestedCall);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -25,31 +25,14 @@ export class ComponentRef {
|
||||||
export class DynamicComponentLoader {
|
export class DynamicComponentLoader {
|
||||||
constructor(private _compiler: Compiler, private _viewManager: AppViewManager) {}
|
constructor(private _compiler: Compiler, private _viewManager: AppViewManager) {}
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads a component into the location given by the provided ElementRef. The loaded component
|
|
||||||
* receives injection as if it in the place of the provided ElementRef.
|
|
||||||
*/
|
|
||||||
loadIntoExistingLocation(typeOrBinding, location: ElementRef,
|
|
||||||
injector: Injector = null): Promise<ComponentRef> {
|
|
||||||
var binding = this._getBinding(typeOrBinding);
|
|
||||||
return this._compiler.compile(binding.token)
|
|
||||||
.then(componentProtoViewRef => {
|
|
||||||
this._viewManager.createDynamicComponentView(location, componentProtoViewRef, binding,
|
|
||||||
injector);
|
|
||||||
var component = this._viewManager.getComponent(location);
|
|
||||||
var dispose = () => { this._viewManager.destroyDynamicComponent(location); };
|
|
||||||
return new ComponentRef(location, component, dispose);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a root component that is placed at the first element that matches the
|
* Loads a root component that is placed at the first element that matches the
|
||||||
* component's selector.
|
* component's selector.
|
||||||
* The loaded component receives injection normally as a hosted view.
|
* The loaded component receives injection normally as a hosted view.
|
||||||
*/
|
*/
|
||||||
loadAsRoot(typeOrBinding, overrideSelector: string = null,
|
loadAsRoot(typeOrBinding: Type | Binding, overrideSelector: string = null,
|
||||||
injector: Injector = null): Promise<ComponentRef> {
|
injector: Injector = null): Promise<ComponentRef> {
|
||||||
return this._compiler.compileInHost(this._getBinding(typeOrBinding))
|
return this._compiler.compileInHost(typeOrBinding)
|
||||||
.then(hostProtoViewRef => {
|
.then(hostProtoViewRef => {
|
||||||
var hostViewRef =
|
var hostViewRef =
|
||||||
this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
|
this._viewManager.createRootHostView(hostProtoViewRef, overrideSelector, injector);
|
||||||
|
@ -62,55 +45,37 @@ export class DynamicComponentLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a component into a free host view that is not yet attached to
|
* Loads a component into the component view of the provided ElementRef
|
||||||
* a parent on the render side, although it is attached to a parent in the injector hierarchy.
|
* next to the element with the given name
|
||||||
* The loaded component receives injection normally as a hosted view.
|
* The loaded component receives
|
||||||
|
* injection normally as a hosted view.
|
||||||
*/
|
*/
|
||||||
loadIntoNewLocation(typeOrBinding, parentComponentLocation: ElementRef,
|
loadIntoLocation(typeOrBinding: Type | Binding, hostLocation: ElementRef, anchorName: string,
|
||||||
injector: Injector = null): Promise<ComponentRef> {
|
injector: Injector = null): Promise<ComponentRef> {
|
||||||
return this._compiler.compileInHost(this._getBinding(typeOrBinding))
|
return this.loadNextToLocation(
|
||||||
.then(hostProtoViewRef => {
|
typeOrBinding, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName),
|
||||||
var hostViewRef = this._viewManager.createFreeHostView(parentComponentLocation,
|
injector);
|
||||||
hostProtoViewRef, injector);
|
|
||||||
var newLocation = new ElementRef(hostViewRef, 0);
|
|
||||||
var component = this._viewManager.getComponent(newLocation);
|
|
||||||
|
|
||||||
var dispose = () => {
|
|
||||||
this._viewManager.destroyFreeHostView(parentComponentLocation, hostViewRef);
|
|
||||||
};
|
|
||||||
return new ComponentRef(newLocation, component, dispose);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Loads a component next to the provided ElementRef. The loaded component receives
|
* Loads a component next to the provided ElementRef. The loaded component receives
|
||||||
* injection normally as a hosted view.
|
* injection normally as a hosted view.
|
||||||
*/
|
*/
|
||||||
loadNextToExistingLocation(typeOrBinding, location: ElementRef,
|
loadNextToLocation(typeOrBinding: Type | Binding, location: ElementRef,
|
||||||
injector: Injector = null): Promise<ComponentRef> {
|
injector: Injector = null): Promise<ComponentRef> {
|
||||||
var binding = this._getBinding(typeOrBinding);
|
return this._compiler.compileInHost(typeOrBinding)
|
||||||
return this._compiler.compileInHost(binding).then(hostProtoViewRef => {
|
.then(hostProtoViewRef => {
|
||||||
var viewContainer = this._viewManager.getViewContainer(location);
|
var viewContainer = this._viewManager.getViewContainer(location);
|
||||||
var hostViewRef =
|
var hostViewRef =
|
||||||
viewContainer.create(hostProtoViewRef, viewContainer.length, null, injector);
|
viewContainer.create(hostProtoViewRef, viewContainer.length, null, injector);
|
||||||
var newLocation = new ElementRef(hostViewRef, 0);
|
var newLocation = new ElementRef(hostViewRef, 0);
|
||||||
var component = this._viewManager.getComponent(newLocation);
|
var component = this._viewManager.getComponent(newLocation);
|
||||||
|
|
||||||
var dispose = () => {
|
var dispose = () => {
|
||||||
var index = viewContainer.indexOf(hostViewRef);
|
var index = viewContainer.indexOf(hostViewRef);
|
||||||
viewContainer.remove(index);
|
viewContainer.remove(index);
|
||||||
};
|
};
|
||||||
return new ComponentRef(newLocation, component, dispose);
|
return new ComponentRef(newLocation, component, dispose);
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
private _getBinding(typeOrBinding): Binding {
|
|
||||||
var binding;
|
|
||||||
if (typeOrBinding instanceof Binding) {
|
|
||||||
binding = typeOrBinding;
|
|
||||||
} else {
|
|
||||||
binding = bind(typeOrBinding).toClass(typeOrBinding);
|
|
||||||
}
|
|
||||||
return binding;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,10 +24,6 @@ export class ElementBinder {
|
||||||
return isPresent(this.componentDirective) && isPresent(this.nestedProtoView);
|
return isPresent(this.componentDirective) && isPresent(this.nestedProtoView);
|
||||||
}
|
}
|
||||||
|
|
||||||
hasDynamicComponent() {
|
|
||||||
return isPresent(this.componentDirective) && isBlank(this.nestedProtoView);
|
|
||||||
}
|
|
||||||
|
|
||||||
hasEmbeddedProtoView() {
|
hasEmbeddedProtoView() {
|
||||||
return !isPresent(this.componentDirective) && isPresent(this.nestedProtoView);
|
return !isPresent(this.componentDirective) && isPresent(this.nestedProtoView);
|
||||||
}
|
}
|
||||||
|
|
|
@ -662,9 +662,6 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||||
private _preBuiltObjects = null;
|
private _preBuiltObjects = null;
|
||||||
private _constructionCounter: number = 0;
|
private _constructionCounter: number = 0;
|
||||||
|
|
||||||
private _dynamicallyCreatedComponent: any;
|
|
||||||
private _dynamicallyCreatedComponentBinding: DirectiveBinding;
|
|
||||||
|
|
||||||
// Queries are added during construction or linking with a new parent.
|
// Queries are added during construction or linking with a new parent.
|
||||||
// They are never removed.
|
// They are never removed.
|
||||||
private _query0: QueryRef;
|
private _query0: QueryRef;
|
||||||
|
@ -693,20 +690,10 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||||
this._lightDomAppInjector = null;
|
this._lightDomAppInjector = null;
|
||||||
this._shadowDomAppInjector = null;
|
this._shadowDomAppInjector = null;
|
||||||
this._strategy.callOnDestroy();
|
this._strategy.callOnDestroy();
|
||||||
this.destroyDynamicComponent();
|
|
||||||
this._strategy.clearInstances();
|
this._strategy.clearInstances();
|
||||||
this._constructionCounter = 0;
|
this._constructionCounter = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
destroyDynamicComponent(): void {
|
|
||||||
if (isPresent(this._dynamicallyCreatedComponentBinding) &&
|
|
||||||
this._dynamicallyCreatedComponentBinding.callOnDestroy) {
|
|
||||||
this._dynamicallyCreatedComponent.onDestroy();
|
|
||||||
this._dynamicallyCreatedComponentBinding = null;
|
|
||||||
this._dynamicallyCreatedComponent = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
onAllChangesDone(): void {
|
onAllChangesDone(): void {
|
||||||
if (isPresent(this._query0) && this._query0.originator === this)
|
if (isPresent(this._query0) && this._query0.originator === this)
|
||||||
this._query0.list.fireCallbacks();
|
this._query0.list.fireCallbacks();
|
||||||
|
@ -743,14 +730,6 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dynamicallyCreateComponent(componentDirective: DirectiveBinding, parentInjector: Injector): any {
|
|
||||||
this._shadowDomAppInjector =
|
|
||||||
this._createShadowDomAppInjector(componentDirective, parentInjector);
|
|
||||||
this._dynamicallyCreatedComponentBinding = componentDirective;
|
|
||||||
this._dynamicallyCreatedComponent = this._new(this._dynamicallyCreatedComponentBinding);
|
|
||||||
return this._dynamicallyCreatedComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
private _checkShadowDomAppInjector(shadowDomAppInjector: Injector): void {
|
private _checkShadowDomAppInjector(shadowDomAppInjector: Injector): void {
|
||||||
if (this._proto._firstBindingIsComponent && isBlank(shadowDomAppInjector)) {
|
if (this._proto._firstBindingIsComponent && isBlank(shadowDomAppInjector)) {
|
||||||
throw new BaseException(
|
throw new BaseException(
|
||||||
|
@ -761,18 +740,7 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
get(token): any {
|
get(token): any { return this._getByKey(Key.get(token), self, false, null); }
|
||||||
if (this._isDynamicallyLoadedComponent(token)) {
|
|
||||||
return this._dynamicallyCreatedComponent;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this._getByKey(Key.get(token), self, false, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
private _isDynamicallyLoadedComponent(token): boolean {
|
|
||||||
return isPresent(this._dynamicallyCreatedComponentBinding) &&
|
|
||||||
Key.get(token) === this._dynamicallyCreatedComponentBinding.key;
|
|
||||||
}
|
|
||||||
|
|
||||||
hasDirective(type: Type): boolean {
|
hasDirective(type: Type): boolean {
|
||||||
return this._strategy.getObjByKeyId(Key.get(type).id, LIGHT_DOM_AND_SHADOW_DOM) !== _undefined;
|
return this._strategy.getObjByKeyId(Key.get(type).id, LIGHT_DOM_AND_SHADOW_DOM) !== _undefined;
|
||||||
|
@ -796,17 +764,10 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||||
return new ViewContainerRef(this._preBuiltObjects.viewManager, this.getElementRef());
|
return new ViewContainerRef(this._preBuiltObjects.viewManager, this.getElementRef());
|
||||||
}
|
}
|
||||||
|
|
||||||
getDynamicallyLoadedComponent(): any { return this._dynamicallyCreatedComponent; }
|
|
||||||
|
|
||||||
directParent(): ElementInjector { return this._proto.distanceToParent < 2 ? this.parent : null; }
|
directParent(): ElementInjector { return this._proto.distanceToParent < 2 ? this.parent : null; }
|
||||||
|
|
||||||
private _isComponentKey(key: Key): boolean { return this._strategy.isComponentKey(key); }
|
private _isComponentKey(key: Key): boolean { return this._strategy.isComponentKey(key); }
|
||||||
|
|
||||||
private _isDynamicallyLoadedComponentKey(key: Key): boolean {
|
|
||||||
return isPresent(this._dynamicallyCreatedComponentBinding) &&
|
|
||||||
key.id === this._dynamicallyCreatedComponentBinding.key.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
_new(binding: ResolvedBinding): any {
|
_new(binding: ResolvedBinding): any {
|
||||||
if (this._constructionCounter++ > this._strategy.getMaxDirectives()) {
|
if (this._constructionCounter++ > this._strategy.getMaxDirectives()) {
|
||||||
throw new CyclicDependencyError(binding.key);
|
throw new CyclicDependencyError(binding.key);
|
||||||
|
@ -1113,8 +1074,6 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||||
|
|
||||||
if (isPresent(this._host) && this._host._isComponentKey(key)) {
|
if (isPresent(this._host) && this._host._isComponentKey(key)) {
|
||||||
return this._host.getComponent();
|
return this._host.getComponent();
|
||||||
} else if (isPresent(this._host) && this._host._isDynamicallyLoadedComponentKey(key)) {
|
|
||||||
return this._host.getDynamicallyLoadedComponent();
|
|
||||||
} else if (optional) {
|
} else if (optional) {
|
||||||
return this._appInjector(requestor).getOptional(key);
|
return this._appInjector(requestor).getOptional(key);
|
||||||
} else {
|
} else {
|
||||||
|
@ -1123,8 +1082,7 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _appInjector(requestor: Key): Injector {
|
private _appInjector(requestor: Key): Injector {
|
||||||
if (isPresent(requestor) &&
|
if (isPresent(requestor) && this._isComponentKey(requestor)) {
|
||||||
(this._isComponentKey(requestor) || this._isDynamicallyLoadedComponentKey(requestor))) {
|
|
||||||
return this._shadowDomAppInjector;
|
return this._shadowDomAppInjector;
|
||||||
} else {
|
} else {
|
||||||
return this._lightDomAppInjector;
|
return this._lightDomAppInjector;
|
||||||
|
|
|
@ -231,7 +231,8 @@ function _createAppProtoView(
|
||||||
renderProtoView: renderApi.ProtoViewDto, protoChangeDetector: ProtoChangeDetector,
|
renderProtoView: renderApi.ProtoViewDto, protoChangeDetector: ProtoChangeDetector,
|
||||||
variableBindings: Map<string, string>, allDirectives: List<DirectiveBinding>): AppProtoView {
|
variableBindings: Map<string, string>, allDirectives: List<DirectiveBinding>): AppProtoView {
|
||||||
var elementBinders = renderProtoView.elementBinders;
|
var elementBinders = renderProtoView.elementBinders;
|
||||||
var protoView = new AppProtoView(renderProtoView.render, protoChangeDetector, variableBindings);
|
var protoView = new AppProtoView(renderProtoView.render, protoChangeDetector, variableBindings,
|
||||||
|
createVariableLocations(elementBinders));
|
||||||
_createElementBinders(protoView, elementBinders, allDirectives);
|
_createElementBinders(protoView, elementBinders, allDirectives);
|
||||||
_bindDirectiveEvents(protoView, elementBinders);
|
_bindDirectiveEvents(protoView, elementBinders);
|
||||||
|
|
||||||
|
@ -276,6 +277,18 @@ function _createVariableNames(parentVariableNames, renderProtoView): List<string
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createVariableLocations(
|
||||||
|
elementBinders: List<renderApi.ElementBinder>): Map<string, number> {
|
||||||
|
var variableLocations = MapWrapper.create();
|
||||||
|
for (var i = 0; i < elementBinders.length; i++) {
|
||||||
|
var binder = elementBinders[i];
|
||||||
|
MapWrapper.forEach(binder.variableBindings, (mappedName, varName) => {
|
||||||
|
MapWrapper.set(variableLocations, mappedName, i);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return variableLocations;
|
||||||
|
}
|
||||||
|
|
||||||
function _createElementBinders(protoView, elementBinders, allDirectiveBindings) {
|
function _createElementBinders(protoView, elementBinders, allDirectiveBindings) {
|
||||||
for (var i = 0; i < elementBinders.length; i++) {
|
for (var i = 0; i < elementBinders.length; i++) {
|
||||||
var renderElementBinder = elementBinders[i];
|
var renderElementBinder = elementBinders[i];
|
||||||
|
|
|
@ -30,7 +30,6 @@ export class TemplateResolver {
|
||||||
return annotation;
|
return annotation;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// No annotation = dynamic component!
|
throw new BaseException(`No View annotation found on component ${stringify(component)}`);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,6 @@ import {EventDispatcher} from 'angular2/src/render/api';
|
||||||
export class AppViewContainer {
|
export class AppViewContainer {
|
||||||
// The order in this list matches the DOM order.
|
// The order in this list matches the DOM order.
|
||||||
views: List<AppView> = [];
|
views: List<AppView> = [];
|
||||||
freeViews: List<AppView> = [];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -39,9 +38,6 @@ export class AppView implements ChangeDispatcher, EventDispatcher {
|
||||||
elementInjectors: List<ElementInjector> = null;
|
elementInjectors: List<ElementInjector> = null;
|
||||||
changeDetector: ChangeDetector = null;
|
changeDetector: ChangeDetector = null;
|
||||||
componentChildViews: List<AppView> = null;
|
componentChildViews: List<AppView> = null;
|
||||||
/// Host views that were added by an imperative view.
|
|
||||||
/// This is a dynamically growing / shrinking array.
|
|
||||||
freeHostViews: List<AppView> = [];
|
|
||||||
viewContainers: List<AppViewContainer>;
|
viewContainers: List<AppViewContainer>;
|
||||||
preBuiltObjects: List<PreBuiltObjects> = null;
|
preBuiltObjects: List<PreBuiltObjects> = null;
|
||||||
|
|
||||||
|
@ -170,7 +166,8 @@ export class AppProtoView {
|
||||||
|
|
||||||
constructor(public render: renderApi.RenderProtoViewRef,
|
constructor(public render: renderApi.RenderProtoViewRef,
|
||||||
public protoChangeDetector: ProtoChangeDetector,
|
public protoChangeDetector: ProtoChangeDetector,
|
||||||
public variableBindings: Map<string, string>) {
|
public variableBindings: Map<string, string>,
|
||||||
|
public variableLocations: Map<string, number>) {
|
||||||
if (isPresent(variableBindings)) {
|
if (isPresent(variableBindings)) {
|
||||||
MapWrapper.forEach(variableBindings, (templateName, _) => {
|
MapWrapper.forEach(variableBindings, (templateName, _) => {
|
||||||
MapWrapper.set(this.protoLocals, templateName, null);
|
MapWrapper.set(this.protoLocals, templateName, null);
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {Renderer, RenderViewRef} from 'angular2/src/render/api';
|
||||||
import {AppViewManagerUtils} from './view_manager_utils';
|
import {AppViewManagerUtils} from './view_manager_utils';
|
||||||
import {AppViewPool} from './view_pool';
|
import {AppViewPool} from './view_pool';
|
||||||
import {AppViewListener} from './view_listener';
|
import {AppViewListener} from './view_listener';
|
||||||
|
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Entry point for creating, moving views in the view hierarchy and destroying views.
|
* Entry point for creating, moving views in the view hierarchy and destroying views.
|
||||||
|
@ -30,33 +31,30 @@ export class AppViewManager {
|
||||||
return hostView.elementInjectors[location.boundElementIndex].getViewContainerRef();
|
return hostView.elementInjectors[location.boundElementIndex].getViewContainerRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an ElementRef for the element with the given variable name
|
||||||
|
* in the component view of the component at the provided ElementRef.
|
||||||
|
*/
|
||||||
|
getNamedElementInComponentView(hostLocation: ElementRef, variableName: string): ElementRef {
|
||||||
|
var hostView = internalView(hostLocation.parentView);
|
||||||
|
var boundElementIndex = hostLocation.boundElementIndex;
|
||||||
|
var componentView = hostView.componentChildViews[boundElementIndex];
|
||||||
|
if (isBlank(componentView)) {
|
||||||
|
throw new BaseException(`There is no component directive at element ${boundElementIndex}`);
|
||||||
|
}
|
||||||
|
var elementIndex = MapWrapper.get(componentView.proto.variableLocations, variableName);
|
||||||
|
if (isBlank(elementIndex)) {
|
||||||
|
throw new BaseException(`Could not find variable ${variableName}`);
|
||||||
|
}
|
||||||
|
return new ElementRef(new ViewRef(componentView), elementIndex);
|
||||||
|
}
|
||||||
|
|
||||||
getComponent(hostLocation: ElementRef): any {
|
getComponent(hostLocation: ElementRef): any {
|
||||||
var hostView = internalView(hostLocation.parentView);
|
var hostView = internalView(hostLocation.parentView);
|
||||||
var boundElementIndex = hostLocation.boundElementIndex;
|
var boundElementIndex = hostLocation.boundElementIndex;
|
||||||
return this._utils.getComponentInstance(hostView, boundElementIndex);
|
return this._utils.getComponentInstance(hostView, boundElementIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
createDynamicComponentView(hostLocation: ElementRef, componentProtoViewRef: ProtoViewRef,
|
|
||||||
componentBinding: Binding, injector: Injector): ViewRef {
|
|
||||||
var componentProtoView = internalProtoView(componentProtoViewRef);
|
|
||||||
var hostView = internalView(hostLocation.parentView);
|
|
||||||
var boundElementIndex = hostLocation.boundElementIndex;
|
|
||||||
var binder = hostView.proto.elementBinders[boundElementIndex];
|
|
||||||
if (!binder.hasDynamicComponent()) {
|
|
||||||
throw new BaseException(
|
|
||||||
`There is no dynamic component directive at element ${boundElementIndex}`)
|
|
||||||
}
|
|
||||||
var componentView = this._createPooledView(componentProtoView);
|
|
||||||
this._renderer.attachComponentView(hostView.render, boundElementIndex, componentView.render);
|
|
||||||
this._utils.attachComponentView(hostView, boundElementIndex, componentView);
|
|
||||||
this._utils.hydrateDynamicComponentInElementInjector(hostView, boundElementIndex,
|
|
||||||
componentBinding, injector);
|
|
||||||
this._utils.hydrateComponentView(hostView, boundElementIndex);
|
|
||||||
this._viewHydrateRecurse(componentView);
|
|
||||||
|
|
||||||
return new ViewRef(componentView);
|
|
||||||
}
|
|
||||||
|
|
||||||
createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
|
createRootHostView(hostProtoViewRef: ProtoViewRef, overrideSelector: string,
|
||||||
injector: Injector): ViewRef {
|
injector: Injector): ViewRef {
|
||||||
var hostProtoView = internalProtoView(hostProtoViewRef);
|
var hostProtoView = internalProtoView(hostProtoViewRef);
|
||||||
|
@ -86,51 +84,6 @@ export class AppViewManager {
|
||||||
this._viewListener.viewDestroyed(hostView);
|
this._viewListener.viewDestroyed(hostView);
|
||||||
}
|
}
|
||||||
|
|
||||||
createFreeHostView(parentComponentLocation: ElementRef, hostProtoViewRef: ProtoViewRef,
|
|
||||||
injector: Injector): ViewRef {
|
|
||||||
var hostProtoView = internalProtoView(hostProtoViewRef);
|
|
||||||
var hostView = this._createPooledView(hostProtoView);
|
|
||||||
var parentComponentHostView = internalView(parentComponentLocation.parentView);
|
|
||||||
var parentComponentBoundElementIndex = parentComponentLocation.boundElementIndex;
|
|
||||||
this._utils.attachAndHydrateFreeHostView(parentComponentHostView,
|
|
||||||
parentComponentBoundElementIndex, hostView, injector);
|
|
||||||
this._viewHydrateRecurse(hostView);
|
|
||||||
return new ViewRef(hostView);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroyFreeHostView(parentComponentLocation: ElementRef, hostViewRef: ViewRef) {
|
|
||||||
var hostView = internalView(hostViewRef);
|
|
||||||
var parentView = internalView(parentComponentLocation.parentView)
|
|
||||||
.componentChildViews[parentComponentLocation.boundElementIndex];
|
|
||||||
this._destroyFreeHostView(parentView, hostView);
|
|
||||||
}
|
|
||||||
|
|
||||||
createFreeEmbeddedView(location: ElementRef, protoViewRef: ProtoViewRef,
|
|
||||||
injector: Injector = null): ViewRef {
|
|
||||||
var protoView = internalProtoView(protoViewRef);
|
|
||||||
var parentView = internalView(location.parentView);
|
|
||||||
var boundElementIndex = location.boundElementIndex;
|
|
||||||
|
|
||||||
var view = this._createPooledView(protoView);
|
|
||||||
this._utils.attachAndHydrateFreeEmbeddedView(parentView, boundElementIndex, view, injector);
|
|
||||||
this._viewHydrateRecurse(view);
|
|
||||||
return new ViewRef(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
destroyFreeEmbeddedView(location: ElementRef, viewRef: ViewRef) {
|
|
||||||
var parentView = internalView(location.parentView);
|
|
||||||
var boundElementIndex = location.boundElementIndex;
|
|
||||||
this._destroyFreeEmbeddedView(parentView, boundElementIndex, internalView(viewRef));
|
|
||||||
}
|
|
||||||
|
|
||||||
destroyDynamicComponent(location: ElementRef) {
|
|
||||||
var hostView = internalView(location.parentView);
|
|
||||||
var ei = hostView.elementInjectors[location.boundElementIndex];
|
|
||||||
var componentView = hostView.componentChildViews[location.boundElementIndex];
|
|
||||||
ei.destroyDynamicComponent();
|
|
||||||
this._destroyComponentView(hostView, location.boundElementIndex, componentView);
|
|
||||||
}
|
|
||||||
|
|
||||||
createViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
|
createViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
|
||||||
protoViewRef: ProtoViewRef, context: ElementRef = null,
|
protoViewRef: ProtoViewRef, context: ElementRef = null,
|
||||||
injector: Injector = null): ViewRef {
|
injector: Injector = null): ViewRef {
|
||||||
|
@ -239,20 +192,6 @@ export class AppViewManager {
|
||||||
this._destroyPooledView(componentView);
|
this._destroyPooledView(componentView);
|
||||||
}
|
}
|
||||||
|
|
||||||
_destroyFreeHostView(parentView, hostView) {
|
|
||||||
this._viewDehydrateRecurse(hostView, true);
|
|
||||||
this._renderer.detachFreeView(hostView.render);
|
|
||||||
this._utils.detachFreeHostView(parentView, hostView);
|
|
||||||
this._destroyPooledView(hostView);
|
|
||||||
}
|
|
||||||
|
|
||||||
_destroyFreeEmbeddedView(parentView, boundElementIndex, view) {
|
|
||||||
this._viewDehydrateRecurse(view, false);
|
|
||||||
this._renderer.detachFreeView(view.render);
|
|
||||||
this._utils.detachFreeEmbeddedView(parentView, boundElementIndex, view);
|
|
||||||
this._destroyPooledView(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
_viewHydrateRecurse(view: viewModule.AppView) {
|
_viewHydrateRecurse(view: viewModule.AppView) {
|
||||||
this._renderer.hydrateView(view.render);
|
this._renderer.hydrateView(view.render);
|
||||||
|
|
||||||
|
@ -272,7 +211,7 @@ export class AppViewManager {
|
||||||
for (var i = 0; i < binders.length; i++) {
|
for (var i = 0; i < binders.length; i++) {
|
||||||
var componentView = view.componentChildViews[i];
|
var componentView = view.componentChildViews[i];
|
||||||
if (isPresent(componentView)) {
|
if (isPresent(componentView)) {
|
||||||
if (binders[i].hasDynamicComponent() || forceDestroyComponents) {
|
if (forceDestroyComponents) {
|
||||||
this._destroyComponentView(view, i, componentView);
|
this._destroyComponentView(view, i, componentView);
|
||||||
} else {
|
} else {
|
||||||
this._viewDehydrateRecurse(componentView, false);
|
this._viewDehydrateRecurse(componentView, false);
|
||||||
|
@ -283,16 +222,7 @@ export class AppViewManager {
|
||||||
for (var j = vc.views.length - 1; j >= 0; j--) {
|
for (var j = vc.views.length - 1; j >= 0; j--) {
|
||||||
this._destroyViewInContainer(view, i, j);
|
this._destroyViewInContainer(view, i, j);
|
||||||
}
|
}
|
||||||
for (var j = vc.freeViews.length - 1; j >= 0; j--) {
|
|
||||||
this._destroyFreeEmbeddedView(view, i, j);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// freeHostViews
|
|
||||||
for (var i = view.freeHostViews.length - 1; i >= 0; i--) {
|
|
||||||
var hostView = view.freeHostViews[i];
|
|
||||||
this._destroyFreeHostView(view, hostView);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,21 +6,15 @@ import * as viewModule from './view';
|
||||||
import * as avmModule from './view_manager';
|
import * as avmModule from './view_manager';
|
||||||
import {Renderer} from 'angular2/src/render/api';
|
import {Renderer} from 'angular2/src/render/api';
|
||||||
import {Locals} from 'angular2/change_detection';
|
import {Locals} from 'angular2/change_detection';
|
||||||
import {DirectiveResolver} from './directive_resolver';
|
|
||||||
import {RenderViewRef} from 'angular2/src/render/api';
|
import {RenderViewRef} from 'angular2/src/render/api';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class AppViewManagerUtils {
|
export class AppViewManagerUtils {
|
||||||
constructor(public _directiveResolver: DirectiveResolver) {}
|
constructor() {}
|
||||||
|
|
||||||
getComponentInstance(parentView: viewModule.AppView, boundElementIndex: number): any {
|
getComponentInstance(parentView: viewModule.AppView, boundElementIndex: number): any {
|
||||||
var binder = parentView.proto.elementBinders[boundElementIndex];
|
|
||||||
var eli = parentView.elementInjectors[boundElementIndex];
|
var eli = parentView.elementInjectors[boundElementIndex];
|
||||||
if (binder.hasDynamicComponent()) {
|
return eli.getComponent();
|
||||||
return eli.getDynamicallyLoadedComponent();
|
|
||||||
} else {
|
|
||||||
return eli.getComponent();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
createView(protoView: viewModule.AppProtoView, renderView: RenderViewRef,
|
createView(protoView: viewModule.AppProtoView, renderView: RenderViewRef,
|
||||||
|
@ -92,44 +86,6 @@ export class AppViewManagerUtils {
|
||||||
this._hydrateView(hostView, injector, null, new Object(), null);
|
this._hydrateView(hostView, injector, null, new Object(), null);
|
||||||
}
|
}
|
||||||
|
|
||||||
attachAndHydrateFreeHostView(parentComponentHostView: viewModule.AppView,
|
|
||||||
parentComponentBoundElementIndex: number,
|
|
||||||
hostView: viewModule.AppView, injector: Injector = null) {
|
|
||||||
var hostElementInjector =
|
|
||||||
parentComponentHostView.elementInjectors[parentComponentBoundElementIndex];
|
|
||||||
var parentView = parentComponentHostView.componentChildViews[parentComponentBoundElementIndex];
|
|
||||||
parentView.changeDetector.addChild(hostView.changeDetector);
|
|
||||||
ListWrapper.push(parentView.freeHostViews, hostView);
|
|
||||||
this._hydrateView(hostView, injector, hostElementInjector, new Object(), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
detachFreeHostView(parentView: viewModule.AppView, hostView: viewModule.AppView) {
|
|
||||||
parentView.changeDetector.removeChild(hostView.changeDetector);
|
|
||||||
ListWrapper.remove(parentView.freeHostViews, hostView);
|
|
||||||
}
|
|
||||||
|
|
||||||
attachAndHydrateFreeEmbeddedView(parentView: viewModule.AppView, boundElementIndex: number,
|
|
||||||
view: viewModule.AppView, injector: Injector = null) {
|
|
||||||
parentView.changeDetector.addChild(view.changeDetector);
|
|
||||||
var viewContainer = this._getOrCreateViewContainer(parentView, boundElementIndex);
|
|
||||||
ListWrapper.push(viewContainer.freeViews, view);
|
|
||||||
var elementInjector = parentView.elementInjectors[boundElementIndex];
|
|
||||||
for (var i = view.rootElementInjectors.length - 1; i >= 0; i--) {
|
|
||||||
view.rootElementInjectors[i].link(elementInjector);
|
|
||||||
}
|
|
||||||
this._hydrateView(view, injector, elementInjector, parentView.context, parentView.locals);
|
|
||||||
}
|
|
||||||
|
|
||||||
detachFreeEmbeddedView(parentView: viewModule.AppView, boundElementIndex: number,
|
|
||||||
view: viewModule.AppView) {
|
|
||||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
|
||||||
view.changeDetector.remove();
|
|
||||||
ListWrapper.remove(viewContainer.freeViews, view);
|
|
||||||
for (var i = 0; i < view.rootElementInjectors.length; ++i) {
|
|
||||||
view.rootElementInjectors[i].unlink();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
attachViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
|
attachViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
|
||||||
contextView: viewModule.AppView, contextBoundElementIndex: number,
|
contextView: viewModule.AppView, contextBoundElementIndex: number,
|
||||||
atIndex: number, view: viewModule.AppView) {
|
atIndex: number, view: viewModule.AppView) {
|
||||||
|
@ -172,23 +128,12 @@ export class AppViewManagerUtils {
|
||||||
}
|
}
|
||||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||||
var view = viewContainer.views[atIndex];
|
var view = viewContainer.views[atIndex];
|
||||||
var elementInjector = contextView.elementInjectors[contextBoundElementIndex].getHost();
|
var elementInjector = contextView.elementInjectors[contextBoundElementIndex];
|
||||||
this._hydrateView(view, injector, elementInjector, contextView.context, contextView.locals);
|
if (isBlank(elementInjector.getHost()) && isBlank(injector)) {
|
||||||
}
|
injector = elementInjector.getShadowDomAppInjector();
|
||||||
|
|
||||||
hydrateDynamicComponentInElementInjector(hostView: viewModule.AppView, boundElementIndex: number,
|
|
||||||
componentBinding: Binding, injector: Injector = null) {
|
|
||||||
var elementInjector = hostView.elementInjectors[boundElementIndex];
|
|
||||||
if (isPresent(elementInjector.getDynamicallyLoadedComponent())) {
|
|
||||||
throw new BaseException(
|
|
||||||
`There already is a dynamic component loaded at element ${boundElementIndex}`);
|
|
||||||
}
|
}
|
||||||
if (isBlank(injector)) {
|
this._hydrateView(view, injector, elementInjector.getHost(), contextView.context,
|
||||||
injector = elementInjector.getLightDomAppInjector();
|
contextView.locals);
|
||||||
}
|
|
||||||
var annotation = this._directiveResolver.resolve(componentBinding.token);
|
|
||||||
var componentDirective = eli.DirectiveBinding.createFromBinding(componentBinding, annotation);
|
|
||||||
elementInjector.dynamicallyCreateComponent(componentDirective, injector);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_hydrateView(view: viewModule.AppView, appInjector: Injector,
|
_hydrateView(view: viewModule.AppView, appInjector: Injector,
|
||||||
|
|
|
@ -35,19 +35,14 @@ export class DebugElement {
|
||||||
return this._elementInjector.getComponent();
|
return this._elementInjector.getComponent();
|
||||||
}
|
}
|
||||||
|
|
||||||
get dynamicallyCreatedComponentInstance(): any {
|
|
||||||
if (!isPresent(this._elementInjector)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return this._elementInjector.getDynamicallyLoadedComponent();
|
|
||||||
}
|
|
||||||
|
|
||||||
get domElement(): any {
|
get domElement(): any {
|
||||||
return resolveInternalDomView(this._parentView.render)
|
return resolveInternalDomView(this._parentView.render)
|
||||||
.boundElements[this._boundElementIndex]
|
.boundElements[this._boundElementIndex]
|
||||||
.element;
|
.element;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get elementRef(): ElementRef { return this._elementInjector.getElementRef(); }
|
||||||
|
|
||||||
getDirectiveInstance(directiveIndex: number): any {
|
getDirectiveInstance(directiveIndex: number): any {
|
||||||
return this._elementInjector.getDirectiveAtIndex(directiveIndex);
|
return this._elementInjector.getDirectiveAtIndex(directiveIndex);
|
||||||
}
|
}
|
||||||
|
|
|
@ -80,10 +80,6 @@ export class MockTemplateResolver extends TemplateResolver {
|
||||||
if (isBlank(view)) {
|
if (isBlank(view)) {
|
||||||
view = super.resolve(component);
|
view = super.resolve(component);
|
||||||
}
|
}
|
||||||
if (isBlank(view)) {
|
|
||||||
// dynamic components
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var directives = view.directives;
|
var directives = view.directives;
|
||||||
var overrides = MapWrapper.get(this._directiveOverrides, component);
|
var overrides = MapWrapper.get(this._directiveOverrides, component);
|
||||||
|
|
|
@ -307,11 +307,6 @@ export class Renderer {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Detaches a free view's element from the DOM.
|
|
||||||
*/
|
|
||||||
detachFreeView(view: RenderViewRef) {}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a regular view out of the given ProtoView
|
* Creates a regular view out of the given ProtoView
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -44,11 +44,6 @@ export class DomRenderer extends Renderer {
|
||||||
return new DomViewRef(this._createView(hostProtoView, element));
|
return new DomViewRef(this._createView(hostProtoView, element));
|
||||||
}
|
}
|
||||||
|
|
||||||
detachFreeView(viewRef: RenderViewRef) {
|
|
||||||
var view = resolveInternalDomView(viewRef);
|
|
||||||
this._removeViewNodes(view);
|
|
||||||
}
|
|
||||||
|
|
||||||
createView(protoViewRef: RenderProtoViewRef): RenderViewRef {
|
createView(protoViewRef: RenderProtoViewRef): RenderViewRef {
|
||||||
var protoView = resolveInternalDomProtoView(protoViewRef);
|
var protoView = resolveInternalDomProtoView(protoViewRef);
|
||||||
return new DomViewRef(this._createView(protoView, null));
|
return new DomViewRef(this._createView(protoView, null));
|
||||||
|
|
|
@ -64,8 +64,8 @@ export class RouterOutlet {
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return this.deactivate()
|
return this.deactivate()
|
||||||
.then((_) => this._loader.loadNextToExistingLocation(instruction.component,
|
.then((_) => this._loader.loadNextToLocation(instruction.component, this._elementRef,
|
||||||
this._elementRef, outletInjector))
|
outletInjector))
|
||||||
.then((componentRef) => {
|
.then((componentRef) => {
|
||||||
this._componentRef = componentRef;
|
this._componentRef = componentRef;
|
||||||
return this._childRouter.commit(instruction.child);
|
return this._childRouter.commit(instruction.child);
|
||||||
|
|
|
@ -41,13 +41,19 @@ import {RenderCompiler} from 'angular2/src/render/api';
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('compiler', function() {
|
describe('compiler', function() {
|
||||||
var directiveResolver, tplResolver, renderCompiler, protoViewFactory, cmpUrlMapper,
|
var directiveResolver, tplResolver, renderCompiler, protoViewFactory, cmpUrlMapper,
|
||||||
renderCompileRequests;
|
renderCompileRequests, rootProtoView;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
directiveResolver = new DirectiveResolver();
|
directiveResolver = new DirectiveResolver();
|
||||||
tplResolver = new FakeTemplateResolver();
|
tplResolver = new FakeTemplateResolver();
|
||||||
cmpUrlMapper = new RuntimeComponentUrlMapper();
|
cmpUrlMapper = new RuntimeComponentUrlMapper();
|
||||||
renderCompiler = new SpyRenderCompiler();
|
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>,
|
function createCompiler(renderCompileResults: List<renderApi.ProtoViewDto>,
|
||||||
|
@ -68,8 +74,9 @@ export function main() {
|
||||||
|
|
||||||
function captureTemplate(template: viewAnn.View): Promise<renderApi.ViewDefinition> {
|
function captureTemplate(template: viewAnn.View): Promise<renderApi.ViewDefinition> {
|
||||||
tplResolver.setView(MainComponent, template);
|
tplResolver.setView(MainComponent, template);
|
||||||
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
var compiler =
|
||||||
return compiler.compile(MainComponent)
|
createCompiler([createRenderProtoView()], [[rootProtoView], [createProtoView()]]);
|
||||||
|
return compiler.compileInHost(MainComponent)
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
expect(renderCompileRequests.length).toBe(1);
|
expect(renderCompileRequests.length).toBe(1);
|
||||||
return renderCompileRequests[0];
|
return renderCompileRequests[0];
|
||||||
|
@ -249,14 +256,14 @@ export function main() {
|
||||||
|
|
||||||
describe('call ProtoViewFactory', () => {
|
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>'}));
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
var renderProtoView = createRenderProtoView();
|
var renderProtoView = createRenderProtoView();
|
||||||
var expectedProtoView = createProtoView();
|
var expectedProtoView = createProtoView();
|
||||||
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
var compiler = createCompiler([renderProtoView], [[rootProtoView], [expectedProtoView]]);
|
||||||
compiler.compile(MainComponent)
|
compiler.compileInHost(MainComponent)
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
var request = protoViewFactory.requests[0];
|
var request = protoViewFactory.requests[1];
|
||||||
expect(request[1]).toBe(renderProtoView);
|
expect(request[1]).toBe(renderProtoView);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
|
@ -264,10 +271,11 @@ export function main() {
|
||||||
|
|
||||||
it('should pass the component binding', inject([AsyncTestCompleter], (async) => {
|
it('should pass the component binding', inject([AsyncTestCompleter], (async) => {
|
||||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
var compiler =
|
||||||
compiler.compile(MainComponent)
|
createCompiler([createRenderProtoView()], [[rootProtoView], [createProtoView()]]);
|
||||||
|
compiler.compileInHost(MainComponent)
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
var request = protoViewFactory.requests[0];
|
var request = protoViewFactory.requests[1];
|
||||||
expect(request[0].key.token).toBe(MainComponent);
|
expect(request[0].key.token).toBe(MainComponent);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
|
@ -277,10 +285,11 @@ export function main() {
|
||||||
tplResolver.setView(
|
tplResolver.setView(
|
||||||
MainComponent,
|
MainComponent,
|
||||||
new viewAnn.View({template: '<div></div>', directives: [SomeDirective]}));
|
new viewAnn.View({template: '<div></div>', directives: [SomeDirective]}));
|
||||||
var compiler = createCompiler([createRenderProtoView()], [[createProtoView()]]);
|
var compiler =
|
||||||
compiler.compile(MainComponent)
|
createCompiler([createRenderProtoView()], [[rootProtoView], [createProtoView()]]);
|
||||||
|
compiler.compileInHost(MainComponent)
|
||||||
.then((_) => {
|
.then((_) => {
|
||||||
var request = protoViewFactory.requests[0];
|
var request = protoViewFactory.requests[1];
|
||||||
var binding = request[2][0];
|
var binding = request[2][0];
|
||||||
expect(binding.key.token).toBe(SomeDirective);
|
expect(binding.key.token).toBe(SomeDirective);
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -290,12 +299,11 @@ export function main() {
|
||||||
it('should use the protoView of the ProtoViewFactory',
|
it('should use the protoView of the ProtoViewFactory',
|
||||||
inject([AsyncTestCompleter], (async) => {
|
inject([AsyncTestCompleter], (async) => {
|
||||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
var renderProtoView = createRenderProtoView();
|
var compiler =
|
||||||
var expectedProtoView = createProtoView();
|
createCompiler([createRenderProtoView()], [[rootProtoView], [createProtoView()]]);
|
||||||
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
compiler.compileInHost(MainComponent)
|
||||||
compiler.compile(MainComponent)
|
|
||||||
.then((protoViewRef) => {
|
.then((protoViewRef) => {
|
||||||
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
expect(internalProtoView(protoViewRef)).toBe(rootProtoView);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -312,10 +320,11 @@ export function main() {
|
||||||
createRenderProtoView([createRenderComponentElementBinder(0)]),
|
createRenderProtoView([createRenderComponentElementBinder(0)]),
|
||||||
createRenderProtoView()
|
createRenderProtoView()
|
||||||
],
|
],
|
||||||
[[mainProtoView], [nestedProtoView]]);
|
[[rootProtoView], [mainProtoView], [nestedProtoView]]);
|
||||||
compiler.compile(MainComponent)
|
compiler.compileInHost(MainComponent)
|
||||||
.then((protoViewRef) => {
|
.then((protoViewRef) => {
|
||||||
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||||
|
.toBe(mainProtoView);
|
||||||
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
|
@ -328,35 +337,68 @@ export function main() {
|
||||||
var viewportProtoView =
|
var viewportProtoView =
|
||||||
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)]);
|
createProtoView([createComponentElementBinder(directiveResolver, NestedComponent)]);
|
||||||
var nestedProtoView = createProtoView();
|
var nestedProtoView = createProtoView();
|
||||||
var compiler = createCompiler([
|
var compiler = createCompiler(
|
||||||
createRenderProtoView([
|
[
|
||||||
createRenderViewportElementBinder(createRenderProtoView(
|
createRenderProtoView([
|
||||||
[createRenderComponentElementBinder(0)], renderApi.ViewType.EMBEDDED))
|
createRenderViewportElementBinder(createRenderProtoView(
|
||||||
]),
|
[createRenderComponentElementBinder(0)], renderApi.ViewType.EMBEDDED))
|
||||||
createRenderProtoView()
|
]),
|
||||||
],
|
createRenderProtoView()
|
||||||
[[mainProtoView, viewportProtoView], [nestedProtoView]]);
|
],
|
||||||
compiler.compile(MainComponent)
|
[[rootProtoView], [mainProtoView, viewportProtoView], [nestedProtoView]]);
|
||||||
|
compiler.compileInHost(MainComponent)
|
||||||
.then((protoViewRef) => {
|
.then((protoViewRef) => {
|
||||||
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||||
|
.toBe(mainProtoView);
|
||||||
expect(viewportProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
expect(viewportProtoView.elementBinders[0].nestedProtoView).toBe(nestedProtoView);
|
||||||
|
|
||||||
async.done();
|
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>'}));
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
var renderProtoView = createRenderProtoView();
|
var mainPv = createProtoView();
|
||||||
var expectedProtoView = createProtoView();
|
var compiler = createCompiler([createRenderProtoView()], [[rootProtoView], [mainPv]]);
|
||||||
var compiler = createCompiler([renderProtoView], [[expectedProtoView]]);
|
compiler.compileInHost(MainComponent)
|
||||||
compiler.compile(MainComponent)
|
|
||||||
.then((protoViewRef) => {
|
.then((protoViewRef) => {
|
||||||
expect(internalProtoView(protoViewRef)).toBe(expectedProtoView);
|
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||||
return compiler.compile(MainComponent);
|
.toBe(mainPv);
|
||||||
|
return compiler.compileInHost(MainComponent);
|
||||||
})
|
})
|
||||||
.then((protoViewRef) => {
|
.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();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
@ -365,36 +407,40 @@ export function main() {
|
||||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
var renderProtoViewCompleter = PromiseWrapper.completer();
|
var renderProtoViewCompleter = PromiseWrapper.completer();
|
||||||
var expectedProtoView = createProtoView();
|
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());
|
renderProtoViewCompleter.resolve(createRenderProtoView());
|
||||||
PromiseWrapper.all([compiler.compile(MainComponent), compiler.compile(MainComponent)])
|
result.then((protoViewRefs) => {
|
||||||
.then((protoViewRefs) => {
|
expect(internalProtoView(protoViewRefs[0]).elementBinders[0].nestedProtoView)
|
||||||
expect(internalProtoView(protoViewRefs[0])).toBe(expectedProtoView);
|
.toBe(expectedProtoView);
|
||||||
expect(internalProtoView(protoViewRefs[1])).toBe(expectedProtoView);
|
expect(internalProtoView(protoViewRefs[1]).elementBinders[0].nestedProtoView)
|
||||||
async.done();
|
.toBe(expectedProtoView);
|
||||||
});
|
async.done();
|
||||||
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should allow recursive components', inject([AsyncTestCompleter], (async) => {
|
it('should allow recursive components', inject([AsyncTestCompleter], (async) => {
|
||||||
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
var mainProtoView =
|
var mainProtoView =
|
||||||
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
|
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
|
||||||
var compiler = createCompiler(
|
var compiler =
|
||||||
[createRenderProtoView([createRenderComponentElementBinder(0)])], [[mainProtoView]]);
|
createCompiler([createRenderProtoView([createRenderComponentElementBinder(0)])],
|
||||||
compiler.compile(MainComponent)
|
[[rootProtoView], [mainProtoView]]);
|
||||||
|
compiler.compileInHost(MainComponent)
|
||||||
.then((protoViewRef) => {
|
.then((protoViewRef) => {
|
||||||
expect(internalProtoView(protoViewRef)).toBe(mainProtoView);
|
expect(internalProtoView(protoViewRef).elementBinders[0].nestedProtoView)
|
||||||
|
.toBe(mainProtoView);
|
||||||
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView);
|
expect(mainProtoView.elementBinders[0].nestedProtoView).toBe(mainProtoView);
|
||||||
async.done();
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should create host proto views', inject([AsyncTestCompleter], (async) => {
|
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>'}));
|
tplResolver.setView(MainComponent, new viewAnn.View({template: '<div></div>'}));
|
||||||
var rootProtoView =
|
var rootProtoView =
|
||||||
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
|
createProtoView([createComponentElementBinder(directiveResolver, MainComponent)]);
|
||||||
|
@ -411,7 +457,7 @@ export function main() {
|
||||||
|
|
||||||
it('should throw for non component types', () => {
|
it('should throw for non component types', () => {
|
||||||
var compiler = createCompiler([], []);
|
var compiler = createCompiler([], []);
|
||||||
expect(() => compiler.compile(SomeDirective))
|
expect(() => compiler.compileInHost(SomeDirective))
|
||||||
.toThrowError(
|
.toThrowError(
|
||||||
`Could not load '${stringify(SomeDirective)}' because it is not a component.`);
|
`Could not load '${stringify(SomeDirective)}' because it is not a component.`);
|
||||||
});
|
});
|
||||||
|
@ -424,7 +470,7 @@ function createDirectiveBinding(directiveResolver, type) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createProtoView(elementBinders = null) {
|
function createProtoView(elementBinders = null) {
|
||||||
var pv = new AppProtoView(null, null, MapWrapper.create());
|
var pv = new AppProtoView(null, null, MapWrapper.create(), null);
|
||||||
if (isBlank(elementBinders)) {
|
if (isBlank(elementBinders)) {
|
||||||
elementBinders = [];
|
elementBinders = [];
|
||||||
}
|
}
|
||||||
|
@ -462,10 +508,18 @@ function createRenderViewportElementBinder(nestedProtoView) {
|
||||||
return new renderApi.ElementBinder({nestedProtoView: nestedProtoView});
|
return new renderApi.ElementBinder({nestedProtoView: nestedProtoView});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createRootProtoView(directiveResolver, type) {
|
||||||
|
return createProtoView([createComponentElementBinder(directiveResolver, type)]);
|
||||||
|
}
|
||||||
|
|
||||||
@Component({selector: 'main-comp'})
|
@Component({selector: 'main-comp'})
|
||||||
class MainComponent {
|
class MainComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'main-comp2'})
|
||||||
|
class MainComponent2 {
|
||||||
|
}
|
||||||
|
|
||||||
@Component({selector: 'nested'})
|
@Component({selector: 'nested'})
|
||||||
class NestedComponent {
|
class NestedComponent {
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,123 +12,101 @@ import {
|
||||||
beforeEachBindings,
|
beforeEachBindings,
|
||||||
it,
|
it,
|
||||||
xit,
|
xit,
|
||||||
viewRootNodes
|
viewRootNodes,
|
||||||
|
TestComponentBuilder
|
||||||
} from 'angular2/test_lib';
|
} from 'angular2/test_lib';
|
||||||
|
|
||||||
import {TestBed, ViewProxy} from 'angular2/src/test_lib/test_bed';
|
import {TestBed, ViewProxy} from 'angular2/src/test_lib/test_bed';
|
||||||
import {Injector} from 'angular2/di';
|
import {Injector} from 'angular2/di';
|
||||||
import {Component, View, onDestroy} from 'angular2/annotations';
|
import {Component, View, onDestroy} from 'angular2/annotations';
|
||||||
import {Locals} from 'angular2/change_detection';
|
|
||||||
import * as viewAnn from 'angular2/src/core/annotations_impl/view';
|
import * as viewAnn from 'angular2/src/core/annotations_impl/view';
|
||||||
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
import {DynamicComponentLoader} from 'angular2/src/core/compiler/dynamic_component_loader';
|
||||||
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
import {ElementRef} from 'angular2/src/core/compiler/element_ref';
|
||||||
import {NgIf} from 'angular2/src/directives/ng_if';
|
import {DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
||||||
import {DomRenderer, DOCUMENT_TOKEN} from 'angular2/src/render/dom/dom_renderer';
|
|
||||||
import {DOM} from 'angular2/src/dom/dom_adapter';
|
import {DOM} from 'angular2/src/dom/dom_adapter';
|
||||||
import {AppViewManager} from 'angular2/src/core/compiler/view_manager';
|
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('DynamicComponentLoader', function() {
|
describe('DynamicComponentLoader', function() {
|
||||||
describe("loading into existing location", () => {
|
describe("loading into a location", () => {
|
||||||
function ijTestBed(fn: (ts: TestBed, async: AsyncTestCompleter) => void) {
|
it('should work',
|
||||||
return inject([TestBed, AsyncTestCompleter], fn);
|
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) => {
|
loader.loadIntoLocation(DynamicallyLoaded, tc.elementRef, 'loc')
|
||||||
tb.overrideView(MyComp, new viewAnn.View({
|
.then(ref => {
|
||||||
template: '<dynamic-comp #dynamic></dynamic-comp>',
|
expect(tc.domElement).toHaveText("Location;DynamicallyLoaded;");
|
||||||
directives: [DynamicComp]
|
async.done();
|
||||||
}));
|
});
|
||||||
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
tb.createView(MyComp).then((view) => {
|
it('should return a disposable component ref',
|
||||||
var dynamicComponent = view.rawView.locals.get("dynamic");
|
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||||
expect(dynamicComponent).toBeAnInstanceOf(DynamicComp);
|
(loader, tcb: TestComponentBuilder, async) => {
|
||||||
|
tcb.overrideView(
|
||||||
|
MyComp,
|
||||||
|
new viewAnn.View(
|
||||||
|
{template: '<location #loc></location>', directives: [Location]}))
|
||||||
|
.createAsync(MyComp)
|
||||||
|
.then((tc) => {
|
||||||
|
|
||||||
dynamicComponent.done.then((_) => {
|
loader.loadIntoLocation(DynamicallyLoaded, tc.elementRef, 'loc')
|
||||||
view.detectChanges();
|
.then(ref => {
|
||||||
expect(view.rootNodes).toHaveText('hello');
|
ref.dispose();
|
||||||
async.done();
|
expect(tc.domElement).toHaveText("Location;");
|
||||||
});
|
async.done();
|
||||||
});
|
});
|
||||||
}));
|
});
|
||||||
|
}));
|
||||||
|
|
||||||
it('should inject dependencies of the dynamically-loaded component',
|
it('should update host properties',
|
||||||
ijTestBed((tb: TestBed, async) => {
|
inject(
|
||||||
tb.overrideView(MyComp, new viewAnn.View({
|
[DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||||
template: '<dynamic-comp #dynamic></dynamic-comp>',
|
(loader, tcb: TestComponentBuilder, async) => {
|
||||||
directives: [DynamicComp]
|
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) => {
|
tc.detectChanges();
|
||||||
var dynamicComponent = view.rawView.locals.get("dynamic");
|
|
||||||
dynamicComponent.done.then((ref) => {
|
|
||||||
expect(ref.instance.dynamicallyCreatedComponentService)
|
|
||||||
.toBeAnInstanceOf(DynamicallyCreatedComponentService);
|
|
||||||
async.done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
|
|
||||||
it('should allow destroying dynamically-loaded components',
|
var newlyInsertedElement = DOM.childNodes(tc.domElement)[1];
|
||||||
inject([TestBed, AsyncTestCompleter], (tb, async) => {
|
expect(newlyInsertedElement.id)
|
||||||
tb.overrideView(MyComp, new viewAnn.View({
|
.toEqual("new value")
|
||||||
template: '<dynamic-comp #dynamic></dynamic-comp>',
|
|
||||||
directives: [DynamicComp]
|
|
||||||
}));
|
|
||||||
|
|
||||||
tb.createView(MyComp).then((view) => {
|
async.done();
|
||||||
var dynamicComponent = (<Locals>view.rawView.locals).get("dynamic");
|
});
|
||||||
dynamicComponent.done.then((ref) => {
|
});
|
||||||
view.detectChanges();
|
}));
|
||||||
expect(view.rootNodes).toHaveText("hello");
|
|
||||||
|
|
||||||
ref.dispose();
|
it('should throw if the variable does not exist',
|
||||||
|
inject([DynamicComponentLoader, TestComponentBuilder, AsyncTestCompleter],
|
||||||
expect(ref.instance.destroyed).toBe(true);
|
(loader, tcb: TestComponentBuilder, async) => {
|
||||||
expect(view.rootNodes).toHaveText("");
|
tcb.overrideView(
|
||||||
async.done();
|
MyComp,
|
||||||
});
|
new viewAnn.View(
|
||||||
});
|
{template: '<location #loc></location>', directives: [Location]}))
|
||||||
}));
|
.createAsync(MyComp)
|
||||||
|
.then((tc) => {
|
||||||
it('should allow to destroy and create them via viewcontainer directives',
|
expect(() => loader.loadIntoLocation(DynamicallyLoadedWithHostProps,
|
||||||
ijTestBed((tb: TestBed, async) => {
|
tc.elementRef, 'someUnknownVariable'))
|
||||||
tb.overrideView(MyComp, new viewAnn.View({
|
.toThrowError('Could not find variable someUnknownVariable');
|
||||||
template:
|
async.done();
|
||||||
'<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();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}));
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("loading next to an existing location", () => {
|
describe("loading next to a location", () => {
|
||||||
it('should work',
|
it('should work',
|
||||||
inject([DynamicComponentLoader, TestBed, AsyncTestCompleter], (loader, tb: TestBed,
|
inject([DynamicComponentLoader, TestBed, AsyncTestCompleter], (loader, tb: TestBed,
|
||||||
async) => {
|
async) => {
|
||||||
|
@ -140,7 +118,7 @@ export function main() {
|
||||||
tb.createView(MyComp).then((view) => {
|
tb.createView(MyComp).then((view) => {
|
||||||
var location = view.rawView.locals.get("loc");
|
var location = view.rawView.locals.get("loc");
|
||||||
|
|
||||||
loader.loadNextToExistingLocation(DynamicallyLoaded, location.elementRef)
|
loader.loadNextToLocation(DynamicallyLoaded, location.elementRef)
|
||||||
.then(ref => {
|
.then(ref => {
|
||||||
expect(view.rootNodes).toHaveText("Location;DynamicallyLoaded;");
|
expect(view.rootNodes).toHaveText("Location;DynamicallyLoaded;");
|
||||||
async.done();
|
async.done();
|
||||||
|
@ -158,9 +136,9 @@ export function main() {
|
||||||
|
|
||||||
tb.createView(MyComp).then((view) => {
|
tb.createView(MyComp).then((view) => {
|
||||||
var location = view.rawView.locals.get("loc");
|
var location = view.rawView.locals.get("loc");
|
||||||
loader.loadNextToExistingLocation(DynamicallyLoaded, location.elementRef)
|
loader.loadNextToLocation(DynamicallyLoaded, location.elementRef)
|
||||||
.then(ref => {
|
.then(ref => {
|
||||||
loader.loadNextToExistingLocation(DynamicallyLoaded2, location.elementRef)
|
loader.loadNextToLocation(DynamicallyLoaded2, location.elementRef)
|
||||||
.then(ref2 => {
|
.then(ref2 => {
|
||||||
expect(view.rootNodes)
|
expect(view.rootNodes)
|
||||||
.toHaveText("Location;DynamicallyLoaded;DynamicallyLoaded2;")
|
.toHaveText("Location;DynamicallyLoaded;DynamicallyLoaded2;")
|
||||||
|
@ -187,7 +165,7 @@ export function main() {
|
||||||
tb.createView(MyComp).then((view) => {
|
tb.createView(MyComp).then((view) => {
|
||||||
var location = view.rawView.locals.get("loc");
|
var location = view.rawView.locals.get("loc");
|
||||||
|
|
||||||
loader.loadNextToExistingLocation(DynamicallyLoadedWithHostProps, location.elementRef)
|
loader.loadNextToLocation(DynamicallyLoadedWithHostProps, location.elementRef)
|
||||||
.then(ref => {
|
.then(ref => {
|
||||||
ref.instance.id = "new value";
|
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', () => {
|
describe('loadAsRoot', () => {
|
||||||
it('should allow to create, update and destroy components',
|
it('should allow to create, update and destroy components',
|
||||||
inject([TestBed, AsyncTestCompleter, DynamicComponentLoader, DOCUMENT_TOKEN, Injector],
|
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({
|
@Component({
|
||||||
selector: 'child-cmp',
|
selector: 'child-cmp',
|
||||||
})
|
})
|
||||||
|
@ -301,15 +228,6 @@ class ChildComp {
|
||||||
|
|
||||||
class DynamicallyCreatedComponentService {}
|
class DynamicallyCreatedComponentService {}
|
||||||
|
|
||||||
@Component({selector: 'dynamic-comp'})
|
|
||||||
class DynamicComp {
|
|
||||||
done;
|
|
||||||
|
|
||||||
constructor(loader: DynamicComponentLoader, location: ElementRef) {
|
|
||||||
this.done = loader.loadIntoExistingLocation(DynamicallyCreatedCmp, location);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'hello-cmp',
|
selector: 'hello-cmp',
|
||||||
appInjector: [DynamicallyCreatedComponentService],
|
appInjector: [DynamicallyCreatedComponentService],
|
||||||
|
|
|
@ -671,7 +671,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should instantiate directives that depend on pre built objects", () => {
|
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 bindings = ListWrapper.concat([NeedsProtoViewRef], extraBindings);
|
||||||
var inj = injector(bindings, null, false, new PreBuiltObjects(null, null, protoView));
|
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', () => {
|
describe('static attributes', () => {
|
||||||
it('should be injectable', () => {
|
it('should be injectable', () => {
|
||||||
var attributes = MapWrapper.create();
|
var attributes = MapWrapper.create();
|
||||||
|
@ -1016,7 +922,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should inject ProtoViewRef", () => {
|
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,
|
var inj = injector(ListWrapper.concat([NeedsProtoViewRef], extraBindings), null, false,
|
||||||
new PreBuiltObjects(null, null, protoView));
|
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',
|
it('should report a meaningful error when a directive is null',
|
||||||
inject([TestBed, AsyncTestCompleter], (tb: TestBed, async) => {
|
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) => {
|
inject([TestBed, AsyncTestCompleter, ANCHOR_ELEMENT], (tb, async, anchorElement) => {
|
||||||
tb.overrideView(MyComp, new viewAnn.View({
|
tb.overrideView(MyComp, new viewAnn.View({
|
||||||
template: '<div><div *some-impvp="ctxBoolProp">hello</div></div>',
|
template: '<div><div *some-impvp="ctxBoolProp">hello</div></div>',
|
||||||
|
@ -1730,20 +1740,19 @@ class ChildConsumingEventBus {
|
||||||
class SomeImperativeViewport {
|
class SomeImperativeViewport {
|
||||||
view: ViewRef;
|
view: ViewRef;
|
||||||
anchor;
|
anchor;
|
||||||
constructor(public element: ElementRef, public protoView: ProtoViewRef,
|
constructor(public vc: ViewContainerRef, public protoView: ProtoViewRef,
|
||||||
public viewManager: AppViewManager, public renderer: DomRenderer,
|
public renderer: DomRenderer, @Inject(ANCHOR_ELEMENT) anchor) {
|
||||||
@Inject(ANCHOR_ELEMENT) anchor) {
|
|
||||||
this.view = null;
|
this.view = null;
|
||||||
this.anchor = anchor;
|
this.anchor = anchor;
|
||||||
}
|
}
|
||||||
|
|
||||||
set someImpvp(value: boolean) {
|
set someImpvp(value: boolean) {
|
||||||
if (isPresent(this.view)) {
|
if (isPresent(this.view)) {
|
||||||
this.viewManager.destroyFreeEmbeddedView(this.element, this.view);
|
this.vc.clear();
|
||||||
this.view = null;
|
this.view = null;
|
||||||
}
|
}
|
||||||
if (value) {
|
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);
|
var nodes = this.renderer.getRootNodes(this.view.render);
|
||||||
for (var i = 0; i < nodes.length; i++) {
|
for (var i = 0; i < nodes.length; i++) {
|
||||||
DOM.appendChild(this.anchor, nodes[i]);
|
DOM.appendChild(this.anchor, nodes[i]);
|
||||||
|
@ -1755,3 +1764,7 @@ class SomeImperativeViewport {
|
||||||
@Directive({selector: '[export-dir]', exportAs: 'dir'})
|
@Directive({selector: '[export-dir]', exportAs: 'dir'})
|
||||||
class ExportDir {
|
class ExportDir {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Component({selector: 'comp'})
|
||||||
|
class ComponentWithoutView {
|
||||||
|
}
|
||||||
|
|
|
@ -21,7 +21,8 @@ import {ChangeDetection, ChangeDetectorDefinition} from 'angular2/change_detecti
|
||||||
import {
|
import {
|
||||||
ProtoViewFactory,
|
ProtoViewFactory,
|
||||||
getChangeDetectorDefinitions,
|
getChangeDetectorDefinitions,
|
||||||
createDirectiveVariableBindings
|
createDirectiveVariableBindings,
|
||||||
|
createVariableLocations
|
||||||
} from 'angular2/src/core/compiler/proto_view_factory';
|
} from 'angular2/src/core/compiler/proto_view_factory';
|
||||||
import {Component, Directive} from 'angular2/annotations';
|
import {Component, Directive} from 'angular2/annotations';
|
||||||
import {Key} from 'angular2/di';
|
import {Key} from 'angular2/di';
|
||||||
|
@ -139,6 +140,18 @@ export function main() {
|
||||||
}).not.toThrow();
|
}).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 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()); }
|
function createView() { return new AppView(null, createProtoView(), MapWrapper.create()); }
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ export function main() {
|
||||||
staticChildComponentCount++;
|
staticChildComponentCount++;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var res = new AppProtoView(new MockProtoViewRef(staticChildComponentCount), null, null);
|
var res = new AppProtoView(new MockProtoViewRef(staticChildComponentCount), null, null, null);
|
||||||
res.elementBinders = binders;
|
res.elementBinders = binders;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -155,139 +155,35 @@ export function main() {
|
||||||
viewPool.spy('returnView').andReturn(true);
|
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('static child components', () => {
|
||||||
|
|
||||||
describe('recursively create when not cached', () => {
|
describe('recursively create when not cached', () => {
|
||||||
var hostView, componentProtoView, nestedProtoView;
|
var rootProtoView, hostProtoView, componentProtoView, hostView, componentView;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
hostView = createView(createProtoView([createComponentElBinder(null)]));
|
componentProtoView = createProtoView();
|
||||||
nestedProtoView = createProtoView();
|
hostProtoView = createProtoView([createComponentElBinder(componentProtoView)]);
|
||||||
componentProtoView = createProtoView([createComponentElBinder(nestedProtoView)]);
|
rootProtoView = createProtoView([createComponentElBinder(hostProtoView)]);
|
||||||
|
manager.createRootHostView(wrapPv(rootProtoView), null, null);
|
||||||
|
hostView = createdViews[1];
|
||||||
|
componentView = createdViews[2];
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create the view', () => {
|
it('should create the view', () => {
|
||||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
expect(hostView.proto).toBe(hostProtoView);
|
||||||
wrapPv(componentProtoView), null, null);
|
expect(componentView.proto).toBe(componentProtoView);
|
||||||
expect(createdViews[0].proto).toBe(componentProtoView);
|
|
||||||
expect(createdViews[1].proto).toBe(nestedProtoView);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should hydrate the view', () => {
|
it('should hydrate the view', () => {
|
||||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(hostView, 0);
|
||||||
wrapPv(componentProtoView), null, null);
|
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(componentView.render);
|
||||||
expect(utils.spy('hydrateComponentView')).toHaveBeenCalledWith(createdViews[0], 0);
|
|
||||||
expect(renderer.spy('hydrateView')).toHaveBeenCalledWith(createdViews[0].render);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set the render view', () => {
|
it('should set the render view',
|
||||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
() => {expect(componentView.render).toBe(createdRenderViews[2])});
|
||||||
wrapPv(componentProtoView), null, null);
|
|
||||||
expect(createdViews[1].render).toBe(createdRenderViews[1])
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set the event dispatcher', () => {
|
it('should set the event dispatcher', () => {
|
||||||
manager.createDynamicComponentView(elementRef(wrapView(hostView), 0),
|
expect(renderer.spy('setEventDispatcher'))
|
||||||
wrapPv(componentProtoView), null, null);
|
.toHaveBeenCalledWith(componentView.render, componentView);
|
||||||
var cmpView = createdViews[1];
|
|
||||||
expect(renderer.spy('setEventDispatcher')).toHaveBeenCalledWith(cmpView.render, cmpView);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -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', () => {
|
describe('createRootHostView', () => {
|
||||||
|
|
||||||
var hostProtoView;
|
var hostProtoView;
|
||||||
|
@ -698,7 +400,6 @@ export function main() {
|
||||||
describe('detachViewInContainer', () => {
|
describe('detachViewInContainer', () => {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -61,13 +61,14 @@ export function main() {
|
||||||
if (isBlank(binders)) {
|
if (isBlank(binders)) {
|
||||||
binders = [];
|
binders = [];
|
||||||
}
|
}
|
||||||
var res = new AppProtoView(null, null, null);
|
var res = new AppProtoView(null, null, null, null);
|
||||||
res.elementBinders = binders;
|
res.elementBinders = binders;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
function createElementInjector() {
|
function createElementInjector() {
|
||||||
var host = new SpyElementInjector();
|
var host = new SpyElementInjector();
|
||||||
|
var appInjector = new SpyInjector();
|
||||||
return SpyObject.stub(new SpyElementInjector(),
|
return SpyObject.stub(new SpyElementInjector(),
|
||||||
{
|
{
|
||||||
'isExportingComponent': false,
|
'isExportingComponent': false,
|
||||||
|
@ -75,8 +76,8 @@ export function main() {
|
||||||
'getEventEmitterAccessors': [],
|
'getEventEmitterAccessors': [],
|
||||||
'getHostActionAccessors': [],
|
'getHostActionAccessors': [],
|
||||||
'getComponent': null,
|
'getComponent': null,
|
||||||
'getDynamicallyLoadedComponent': null,
|
'getHost': host,
|
||||||
'getHost': host
|
'getShadowDomAppInjector': appInjector
|
||||||
},
|
},
|
||||||
{});
|
{});
|
||||||
}
|
}
|
||||||
|
@ -99,21 +100,7 @@ export function main() {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
directiveResolver = new DirectiveResolver();
|
directiveResolver = new DirectiveResolver();
|
||||||
utils = new AppViewManagerUtils(directiveResolver);
|
utils = new AppViewManagerUtils();
|
||||||
});
|
|
||||||
|
|
||||||
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');
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("hydrateComponentView", () => {
|
describe("hydrateComponentView", () => {
|
||||||
|
@ -246,6 +233,16 @@ export function main() {
|
||||||
childView.preBuiltObjects[0]);
|
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', () => {
|
describe('hydrateRootHostView', () => {
|
||||||
|
@ -295,3 +292,10 @@ class SpyPreBuiltObjects extends SpyObject {
|
||||||
constructor() { super(PreBuiltObjects); }
|
constructor() { super(PreBuiltObjects); }
|
||||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
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 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()); }
|
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',
|
it('should attach and detach component views',
|
||||||
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
|
||||||
tb.compileAll([
|
tb.compileAll([
|
||||||
|
|
|
@ -59,7 +59,7 @@ export class MdDialog {
|
||||||
var backdropRefPromise = this._openBackdrop(elementRef, contentInjector);
|
var backdropRefPromise = this._openBackdrop(elementRef, contentInjector);
|
||||||
|
|
||||||
// First, load the MdDialogContainer, into which the given component will be loaded.
|
// First, load the MdDialogContainer, into which the given component will be loaded.
|
||||||
return this.componentLoader.loadIntoNewLocation(MdDialogContainer, elementRef)
|
return this.componentLoader.loadNextToLocation(MdDialogContainer, elementRef)
|
||||||
.then(containerRef => {
|
.then(containerRef => {
|
||||||
// TODO(tbosch): clean this up when we have custom renderers
|
// TODO(tbosch): clean this up when we have custom renderers
|
||||||
// (https://github.com/angular/angular/issues/1807)
|
// (https://github.com/angular/angular/issues/1807)
|
||||||
|
@ -86,8 +86,8 @@ export class MdDialog {
|
||||||
dialogRef.containerRef = containerRef;
|
dialogRef.containerRef = containerRef;
|
||||||
|
|
||||||
// Now load the given component into the MdDialogContainer.
|
// Now load the given component into the MdDialogContainer.
|
||||||
return this.componentLoader.loadNextToExistingLocation(
|
return this.componentLoader.loadNextToLocation(type, containerRef.instance.contentRef,
|
||||||
type, containerRef.instance.contentRef, contentInjector)
|
contentInjector)
|
||||||
.then(contentRef => {
|
.then(contentRef => {
|
||||||
|
|
||||||
// Wrap both component refs for the container and the content so that we can return
|
// Wrap both component refs for the container and the content so that we can return
|
||||||
|
@ -107,7 +107,7 @@ export class MdDialog {
|
||||||
|
|
||||||
/** Loads the dialog backdrop (transparent overlay over the rest of the page). */
|
/** Loads the dialog backdrop (transparent overlay over the rest of the page). */
|
||||||
_openBackdrop(elementRef: ElementRef, injector: Injector): Promise<ComponentRef> {
|
_openBackdrop(elementRef: ElementRef, injector: Injector): Promise<ComponentRef> {
|
||||||
return this.componentLoader.loadIntoNewLocation(MdBackdrop, elementRef, injector)
|
return this.componentLoader.loadNextToLocation(MdBackdrop, elementRef, injector)
|
||||||
.then((componentRef) => {
|
.then((componentRef) => {
|
||||||
// TODO(tbosch): clean this up when we have custom renderers
|
// TODO(tbosch): clean this up when we have custom renderers
|
||||||
// (https://github.com/angular/angular/issues/1807)
|
// (https://github.com/angular/angular/issues/1807)
|
||||||
|
|
|
@ -58,12 +58,12 @@ export function main() {
|
||||||
|
|
||||||
function compileNoBindings() {
|
function compileNoBindings() {
|
||||||
cache.clear();
|
cache.clear();
|
||||||
return compiler.compile(BenchmarkComponentNoBindings);
|
return compiler.compileInHost(BenchmarkComponentNoBindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
function compileWithBindings() {
|
function compileWithBindings() {
|
||||||
cache.clear();
|
cache.clear();
|
||||||
return compiler.compile(BenchmarkComponentWithBindings);
|
return compiler.compileInHost(BenchmarkComponentWithBindings);
|
||||||
}
|
}
|
||||||
|
|
||||||
bindAction('#compileNoBindings', measureWrapper(compileNoBindings, 'No Bindings'));
|
bindAction('#compileNoBindings', measureWrapper(compileNoBindings, 'No Bindings'));
|
||||||
|
@ -122,7 +122,7 @@ class MultipleTemplateResolver extends TemplateResolver {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component()
|
@Component({selector: 'cmp-nobind'})
|
||||||
@View({
|
@View({
|
||||||
directives: [Dir0, Dir1, Dir2, Dir3, Dir4],
|
directives: [Dir0, Dir1, Dir2, Dir3, Dir4],
|
||||||
template: `
|
template: `
|
||||||
|
@ -140,7 +140,7 @@ class MultipleTemplateResolver extends TemplateResolver {
|
||||||
class BenchmarkComponentNoBindings {
|
class BenchmarkComponentNoBindings {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component()
|
@Component({selector: 'cmp-withbind'})
|
||||||
@View({
|
@View({
|
||||||
directives: [Dir0, Dir1, Dir2, Dir3, Dir4],
|
directives: [Dir0, Dir1, Dir2, Dir3, Dir4],
|
||||||
template: `
|
template: `
|
||||||
|
|
|
@ -61,10 +61,10 @@ class DummyComponent {
|
||||||
class DummyDirective {
|
class DummyDirective {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Component({selector: 'dynamic-dummy'})
|
@Directive({selector: 'dynamic-dummy'})
|
||||||
class DynamicDummy {
|
class DynamicDummy {
|
||||||
constructor(loader: DynamicComponentLoader, location: ElementRef) {
|
constructor(loader: DynamicComponentLoader, location: ElementRef) {
|
||||||
loader.loadIntoExistingLocation(DummyComponent, location);
|
loader.loadNextToLocation(DummyComponent, location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue