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:
@ -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
|
||||
*/
|
||||
@CONST()
|
||||
|
@ -33,6 +33,7 @@ import * as renderApi from 'angular2/src/render/api';
|
||||
@Injectable()
|
||||
export class CompilerCache {
|
||||
_cache: Map<Type, AppProtoView> = MapWrapper.create();
|
||||
_hostCache: Map<Type, AppProtoView> = MapWrapper.create();
|
||||
|
||||
set(component: Type, protoView: AppProtoView): void {
|
||||
MapWrapper.set(this._cache, component, protoView);
|
||||
@ -43,7 +44,19 @@ export class CompilerCache {
|
||||
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);
|
||||
|
||||
var directiveMetadata = componentBinding.metadata;
|
||||
return this._render.compileHost(directiveMetadata)
|
||||
.then((hostRenderPv) => {
|
||||
return this._compileNestedProtoViews(componentBinding, hostRenderPv, [componentBinding]);
|
||||
})
|
||||
.then((appProtoView) => { return new ProtoViewRef(appProtoView); });
|
||||
}
|
||||
|
||||
compile(component: Type): Promise<ProtoViewRef> {
|
||||
var componentBinding = this._bindDirective(component);
|
||||
Compiler._assertTypeIsComponent(componentBinding);
|
||||
var pvOrPromise = this._compile(componentBinding);
|
||||
var pvPromise = isPromise(pvOrPromise) ? <Promise<AppProtoView>>pvOrPromise :
|
||||
PromiseWrapper.resolve(pvOrPromise);
|
||||
return pvPromise.then((appProtoView) => { return new ProtoViewRef(appProtoView); });
|
||||
var hostPvPromise;
|
||||
var component = <Type>componentBinding.key.token;
|
||||
var hostAppProtoView = this._compilerCache.getHost(component);
|
||||
if (isPresent(hostAppProtoView)) {
|
||||
hostPvPromise = PromiseWrapper.resolve(hostAppProtoView);
|
||||
} else {
|
||||
hostPvPromise = this._render.compileHost(directiveMetadata)
|
||||
.then((hostRenderPv) => {
|
||||
return this._compileNestedProtoViews(componentBinding, hostRenderPv,
|
||||
[componentBinding]);
|
||||
});
|
||||
}
|
||||
return hostPvPromise.then((hostAppProtoView) => { return new ProtoViewRef(hostAppProtoView); });
|
||||
}
|
||||
|
||||
private _compile(componentBinding: DirectiveBinding): Promise<AppProtoView>| AppProtoView {
|
||||
@ -128,9 +140,6 @@ export class Compiler {
|
||||
return pvPromise;
|
||||
}
|
||||
var template = this._templateResolver.resolve(component);
|
||||
if (isBlank(template)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var directives = this._flattenDirectives(template);
|
||||
|
||||
@ -160,16 +169,17 @@ export class Compiler {
|
||||
var protoViews =
|
||||
this._protoViewFactory.createAppProtoViews(componentBinding, renderPv, directives);
|
||||
var protoView = protoViews[0];
|
||||
// TODO(tbosch): we should be caching host protoViews as well!
|
||||
// -> 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.
|
||||
if (isPresent(componentBinding)) {
|
||||
var component = componentBinding.key.token;
|
||||
this._compilerCache.set(component, protoView);
|
||||
MapWrapper.delete(this._compiling, component);
|
||||
if (renderPv.type === renderApi.ViewType.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 = [];
|
||||
ListWrapper.forEach(this._collectComponentElementBinders(protoViews), (elementBinder) => {
|
||||
var nestedComponent = elementBinder.componentDirective;
|
||||
@ -179,7 +189,7 @@ export class Compiler {
|
||||
if (isPromise(nestedCall)) {
|
||||
ListWrapper.push(nestedPVPromises,
|
||||
(<Promise<AppProtoView>>nestedCall).then(elementBinderDone));
|
||||
} else if (isPresent(nestedCall)) {
|
||||
} else {
|
||||
elementBinderDone(<AppProtoView>nestedCall);
|
||||
}
|
||||
});
|
||||
|
@ -25,31 +25,14 @@ export class ComponentRef {
|
||||
export class DynamicComponentLoader {
|
||||
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
|
||||
* component's selector.
|
||||
* 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> {
|
||||
return this._compiler.compileInHost(this._getBinding(typeOrBinding))
|
||||
return this._compiler.compileInHost(typeOrBinding)
|
||||
.then(hostProtoViewRef => {
|
||||
var hostViewRef =
|
||||
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
|
||||
* a parent on the render side, although it is attached to a parent in the injector hierarchy.
|
||||
* The loaded component receives injection normally as a hosted view.
|
||||
* Loads a component into the component view of the provided ElementRef
|
||||
* next to the element with the given name
|
||||
* The loaded component receives
|
||||
* injection normally as a hosted view.
|
||||
*/
|
||||
loadIntoNewLocation(typeOrBinding, parentComponentLocation: ElementRef,
|
||||
injector: Injector = null): Promise<ComponentRef> {
|
||||
return this._compiler.compileInHost(this._getBinding(typeOrBinding))
|
||||
.then(hostProtoViewRef => {
|
||||
var hostViewRef = this._viewManager.createFreeHostView(parentComponentLocation,
|
||||
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);
|
||||
});
|
||||
loadIntoLocation(typeOrBinding: Type | Binding, hostLocation: ElementRef, anchorName: string,
|
||||
injector: Injector = null): Promise<ComponentRef> {
|
||||
return this.loadNextToLocation(
|
||||
typeOrBinding, this._viewManager.getNamedElementInComponentView(hostLocation, anchorName),
|
||||
injector);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a component next to the provided ElementRef. The loaded component receives
|
||||
* injection normally as a hosted view.
|
||||
*/
|
||||
loadNextToExistingLocation(typeOrBinding, location: ElementRef,
|
||||
injector: Injector = null): Promise<ComponentRef> {
|
||||
var binding = this._getBinding(typeOrBinding);
|
||||
return this._compiler.compileInHost(binding).then(hostProtoViewRef => {
|
||||
var viewContainer = this._viewManager.getViewContainer(location);
|
||||
var hostViewRef =
|
||||
viewContainer.create(hostProtoViewRef, viewContainer.length, null, injector);
|
||||
var newLocation = new ElementRef(hostViewRef, 0);
|
||||
var component = this._viewManager.getComponent(newLocation);
|
||||
loadNextToLocation(typeOrBinding: Type | Binding, location: ElementRef,
|
||||
injector: Injector = null): Promise<ComponentRef> {
|
||||
return this._compiler.compileInHost(typeOrBinding)
|
||||
.then(hostProtoViewRef => {
|
||||
var viewContainer = this._viewManager.getViewContainer(location);
|
||||
var hostViewRef =
|
||||
viewContainer.create(hostProtoViewRef, viewContainer.length, null, injector);
|
||||
var newLocation = new ElementRef(hostViewRef, 0);
|
||||
var component = this._viewManager.getComponent(newLocation);
|
||||
|
||||
var dispose = () => {
|
||||
var index = viewContainer.indexOf(hostViewRef);
|
||||
viewContainer.remove(index);
|
||||
};
|
||||
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;
|
||||
var dispose = () => {
|
||||
var index = viewContainer.indexOf(hostViewRef);
|
||||
viewContainer.remove(index);
|
||||
};
|
||||
return new ComponentRef(newLocation, component, dispose);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -24,10 +24,6 @@ export class ElementBinder {
|
||||
return isPresent(this.componentDirective) && isPresent(this.nestedProtoView);
|
||||
}
|
||||
|
||||
hasDynamicComponent() {
|
||||
return isPresent(this.componentDirective) && isBlank(this.nestedProtoView);
|
||||
}
|
||||
|
||||
hasEmbeddedProtoView() {
|
||||
return !isPresent(this.componentDirective) && isPresent(this.nestedProtoView);
|
||||
}
|
||||
|
@ -662,9 +662,6 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||
private _preBuiltObjects = null;
|
||||
private _constructionCounter: number = 0;
|
||||
|
||||
private _dynamicallyCreatedComponent: any;
|
||||
private _dynamicallyCreatedComponentBinding: DirectiveBinding;
|
||||
|
||||
// Queries are added during construction or linking with a new parent.
|
||||
// They are never removed.
|
||||
private _query0: QueryRef;
|
||||
@ -693,20 +690,10 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||
this._lightDomAppInjector = null;
|
||||
this._shadowDomAppInjector = null;
|
||||
this._strategy.callOnDestroy();
|
||||
this.destroyDynamicComponent();
|
||||
this._strategy.clearInstances();
|
||||
this._constructionCounter = 0;
|
||||
}
|
||||
|
||||
destroyDynamicComponent(): void {
|
||||
if (isPresent(this._dynamicallyCreatedComponentBinding) &&
|
||||
this._dynamicallyCreatedComponentBinding.callOnDestroy) {
|
||||
this._dynamicallyCreatedComponent.onDestroy();
|
||||
this._dynamicallyCreatedComponentBinding = null;
|
||||
this._dynamicallyCreatedComponent = null;
|
||||
}
|
||||
}
|
||||
|
||||
onAllChangesDone(): void {
|
||||
if (isPresent(this._query0) && this._query0.originator === this)
|
||||
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 {
|
||||
if (this._proto._firstBindingIsComponent && isBlank(shadowDomAppInjector)) {
|
||||
throw new BaseException(
|
||||
@ -761,18 +740,7 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||
}
|
||||
}
|
||||
|
||||
get(token): any {
|
||||
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;
|
||||
}
|
||||
get(token): any { return this._getByKey(Key.get(token), self, false, null); }
|
||||
|
||||
hasDirective(type: Type): boolean {
|
||||
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());
|
||||
}
|
||||
|
||||
getDynamicallyLoadedComponent(): any { return this._dynamicallyCreatedComponent; }
|
||||
|
||||
directParent(): ElementInjector { return this._proto.distanceToParent < 2 ? this.parent : null; }
|
||||
|
||||
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 {
|
||||
if (this._constructionCounter++ > this._strategy.getMaxDirectives()) {
|
||||
throw new CyclicDependencyError(binding.key);
|
||||
@ -1113,8 +1074,6 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||
|
||||
if (isPresent(this._host) && this._host._isComponentKey(key)) {
|
||||
return this._host.getComponent();
|
||||
} else if (isPresent(this._host) && this._host._isDynamicallyLoadedComponentKey(key)) {
|
||||
return this._host.getDynamicallyLoadedComponent();
|
||||
} else if (optional) {
|
||||
return this._appInjector(requestor).getOptional(key);
|
||||
} else {
|
||||
@ -1123,8 +1082,7 @@ export class ElementInjector extends TreeNode<ElementInjector> {
|
||||
}
|
||||
|
||||
private _appInjector(requestor: Key): Injector {
|
||||
if (isPresent(requestor) &&
|
||||
(this._isComponentKey(requestor) || this._isDynamicallyLoadedComponentKey(requestor))) {
|
||||
if (isPresent(requestor) && this._isComponentKey(requestor)) {
|
||||
return this._shadowDomAppInjector;
|
||||
} else {
|
||||
return this._lightDomAppInjector;
|
||||
|
@ -231,7 +231,8 @@ function _createAppProtoView(
|
||||
renderProtoView: renderApi.ProtoViewDto, protoChangeDetector: ProtoChangeDetector,
|
||||
variableBindings: Map<string, string>, allDirectives: List<DirectiveBinding>): AppProtoView {
|
||||
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);
|
||||
_bindDirectiveEvents(protoView, elementBinders);
|
||||
|
||||
@ -276,6 +277,18 @@ function _createVariableNames(parentVariableNames, renderProtoView): List<string
|
||||
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) {
|
||||
for (var i = 0; i < elementBinders.length; i++) {
|
||||
var renderElementBinder = elementBinders[i];
|
||||
|
@ -30,7 +30,6 @@ export class TemplateResolver {
|
||||
return annotation;
|
||||
}
|
||||
}
|
||||
// No annotation = dynamic component!
|
||||
return null;
|
||||
throw new BaseException(`No View annotation found on component ${stringify(component)}`);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ import {EventDispatcher} from 'angular2/src/render/api';
|
||||
export class AppViewContainer {
|
||||
// The order in this list matches the DOM order.
|
||||
views: List<AppView> = [];
|
||||
freeViews: List<AppView> = [];
|
||||
}
|
||||
|
||||
/**
|
||||
@ -39,9 +38,6 @@ export class AppView implements ChangeDispatcher, EventDispatcher {
|
||||
elementInjectors: List<ElementInjector> = null;
|
||||
changeDetector: ChangeDetector = 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>;
|
||||
preBuiltObjects: List<PreBuiltObjects> = null;
|
||||
|
||||
@ -170,7 +166,8 @@ export class AppProtoView {
|
||||
|
||||
constructor(public render: renderApi.RenderProtoViewRef,
|
||||
public protoChangeDetector: ProtoChangeDetector,
|
||||
public variableBindings: Map<string, string>) {
|
||||
public variableBindings: Map<string, string>,
|
||||
public variableLocations: Map<string, number>) {
|
||||
if (isPresent(variableBindings)) {
|
||||
MapWrapper.forEach(variableBindings, (templateName, _) => {
|
||||
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 {AppViewPool} from './view_pool';
|
||||
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.
|
||||
@ -30,33 +31,30 @@ export class AppViewManager {
|
||||
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 {
|
||||
var hostView = internalView(hostLocation.parentView);
|
||||
var boundElementIndex = hostLocation.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,
|
||||
injector: Injector): ViewRef {
|
||||
var hostProtoView = internalProtoView(hostProtoViewRef);
|
||||
@ -86,51 +84,6 @@ export class AppViewManager {
|
||||
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,
|
||||
protoViewRef: ProtoViewRef, context: ElementRef = null,
|
||||
injector: Injector = null): ViewRef {
|
||||
@ -239,20 +192,6 @@ export class AppViewManager {
|
||||
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) {
|
||||
this._renderer.hydrateView(view.render);
|
||||
|
||||
@ -272,7 +211,7 @@ export class AppViewManager {
|
||||
for (var i = 0; i < binders.length; i++) {
|
||||
var componentView = view.componentChildViews[i];
|
||||
if (isPresent(componentView)) {
|
||||
if (binders[i].hasDynamicComponent() || forceDestroyComponents) {
|
||||
if (forceDestroyComponents) {
|
||||
this._destroyComponentView(view, i, componentView);
|
||||
} else {
|
||||
this._viewDehydrateRecurse(componentView, false);
|
||||
@ -283,16 +222,7 @@ export class AppViewManager {
|
||||
for (var j = vc.views.length - 1; j >= 0; 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 {Renderer} from 'angular2/src/render/api';
|
||||
import {Locals} from 'angular2/change_detection';
|
||||
import {DirectiveResolver} from './directive_resolver';
|
||||
import {RenderViewRef} from 'angular2/src/render/api';
|
||||
|
||||
@Injectable()
|
||||
export class AppViewManagerUtils {
|
||||
constructor(public _directiveResolver: DirectiveResolver) {}
|
||||
constructor() {}
|
||||
|
||||
getComponentInstance(parentView: viewModule.AppView, boundElementIndex: number): any {
|
||||
var binder = parentView.proto.elementBinders[boundElementIndex];
|
||||
var eli = parentView.elementInjectors[boundElementIndex];
|
||||
if (binder.hasDynamicComponent()) {
|
||||
return eli.getDynamicallyLoadedComponent();
|
||||
} else {
|
||||
return eli.getComponent();
|
||||
}
|
||||
return eli.getComponent();
|
||||
}
|
||||
|
||||
createView(protoView: viewModule.AppProtoView, renderView: RenderViewRef,
|
||||
@ -92,44 +86,6 @@ export class AppViewManagerUtils {
|
||||
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,
|
||||
contextView: viewModule.AppView, contextBoundElementIndex: number,
|
||||
atIndex: number, view: viewModule.AppView) {
|
||||
@ -172,23 +128,12 @@ export class AppViewManagerUtils {
|
||||
}
|
||||
var viewContainer = parentView.viewContainers[boundElementIndex];
|
||||
var view = viewContainer.views[atIndex];
|
||||
var elementInjector = contextView.elementInjectors[contextBoundElementIndex].getHost();
|
||||
this._hydrateView(view, injector, elementInjector, contextView.context, contextView.locals);
|
||||
}
|
||||
|
||||
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}`);
|
||||
var elementInjector = contextView.elementInjectors[contextBoundElementIndex];
|
||||
if (isBlank(elementInjector.getHost()) && isBlank(injector)) {
|
||||
injector = elementInjector.getShadowDomAppInjector();
|
||||
}
|
||||
if (isBlank(injector)) {
|
||||
injector = elementInjector.getLightDomAppInjector();
|
||||
}
|
||||
var annotation = this._directiveResolver.resolve(componentBinding.token);
|
||||
var componentDirective = eli.DirectiveBinding.createFromBinding(componentBinding, annotation);
|
||||
elementInjector.dynamicallyCreateComponent(componentDirective, injector);
|
||||
this._hydrateView(view, injector, elementInjector.getHost(), contextView.context,
|
||||
contextView.locals);
|
||||
}
|
||||
|
||||
_hydrateView(view: viewModule.AppView, appInjector: Injector,
|
||||
|
Reference in New Issue
Block a user