refactor(views): split ViewManager/ViewContainerRef.createView
into 2 methods
BREAKING CHANGES: `ViewManager.createView` / `ViewContainerRef.create` have been split into 2 methods: - `createHostView` which takes dynamically created bindings - `createEmbeddedView` which takes the newly introduced `TemplateRef` The new type `TemplateRef` is the combination of a `ProtoViewRef` and and `ElementRef` from the same place. Use `TemplateRef` when working with embedded views in `ng-if`, `ng-for`, ... instead of `ProtoViewRef`. Also, `ProtoViewRef` is no more injectable, but `TemplateRef` is. First part of #1989 to clean up manual content projection. Closes #3114
This commit is contained in:
@ -360,12 +360,12 @@ import {DEFAULT} from 'angular2/change_detection';
|
||||
* })
|
||||
* export class Unless {
|
||||
* viewContainer: ViewContainerRef;
|
||||
* protoViewRef: ProtoViewRef;
|
||||
* templateRef: TemplateRef;
|
||||
* prevCondition: boolean;
|
||||
*
|
||||
* constructor(viewContainer: ViewContainerRef, protoViewRef: ProtoViewRef) {
|
||||
* constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef) {
|
||||
* this.viewContainer = viewContainer;
|
||||
* this.protoViewRef = protoViewRef;
|
||||
* this.templateRef = templateRef;
|
||||
* this.prevCondition = null;
|
||||
* }
|
||||
*
|
||||
@ -375,7 +375,7 @@ import {DEFAULT} from 'angular2/change_detection';
|
||||
* this.viewContainer.clear();
|
||||
* } else if (!newCondition && (isBlank(this.prevCondition) || this.prevCondition)) {
|
||||
* this.prevCondition = false;
|
||||
* this.viewContainer.create(this.protoViewRef);
|
||||
* this.viewContainer.create(this.templateRef);
|
||||
* }
|
||||
* }
|
||||
* }
|
||||
|
@ -146,8 +146,8 @@ export class Compiler {
|
||||
return this._compileNestedProtoViews(hostRenderPv, protoView, componentType);
|
||||
});
|
||||
}
|
||||
return hostPvPromise.then(hostAppProtoView => this._mergeCyclicEmbeddedProtoViews().then(
|
||||
_ => new ProtoViewRef(hostAppProtoView)));
|
||||
return hostPvPromise.then(
|
||||
hostAppProtoView => this._mergeCyclicEmbeddedProtoViews().then(_ => hostAppProtoView.ref));
|
||||
}
|
||||
|
||||
private _compile(componentBinding: DirectiveBinding): Promise<AppProtoView>| AppProtoView {
|
||||
|
@ -63,7 +63,7 @@ export class DynamicComponentLoader {
|
||||
.then(hostProtoViewRef => {
|
||||
var viewContainer = this._viewManager.getViewContainer(location);
|
||||
var hostViewRef =
|
||||
viewContainer.create(hostProtoViewRef, viewContainer.length, null, bindings);
|
||||
viewContainer.createHostView(hostProtoViewRef, viewContainer.length, bindings);
|
||||
var newLocation = this._viewManager.getHostElement(hostViewRef);
|
||||
var component = this._viewManager.getComponent(newLocation);
|
||||
|
||||
|
@ -40,7 +40,7 @@ import * as viewModule from './view';
|
||||
import * as avmModule from './view_manager';
|
||||
import {ViewContainerRef} from './view_container_ref';
|
||||
import {ElementRef} from './element_ref';
|
||||
import {ProtoViewRef, ViewRef} from './view_ref';
|
||||
import {TemplateRef} from './template_ref';
|
||||
import {Directive, Component, LifecycleEvent} from 'angular2/src/core/annotations_impl/annotations';
|
||||
import {hasLifecycleHook} from './directive_lifecycle_reflector';
|
||||
import {ChangeDetector, ChangeDetectorRef, Pipes} from 'angular2/change_detection';
|
||||
@ -52,7 +52,7 @@ var _staticKeys;
|
||||
|
||||
export class StaticKeys {
|
||||
viewManagerId: number;
|
||||
protoViewId: number;
|
||||
templateRefId: number;
|
||||
viewContainerId: number;
|
||||
changeDetectorRefId: number;
|
||||
elementRefId: number;
|
||||
@ -60,7 +60,7 @@ export class StaticKeys {
|
||||
|
||||
constructor() {
|
||||
this.viewManagerId = Key.get(avmModule.AppViewManager).id;
|
||||
this.protoViewId = Key.get(ProtoViewRef).id;
|
||||
this.templateRefId = Key.get(TemplateRef).id;
|
||||
this.viewContainerId = Key.get(ViewContainerRef).id;
|
||||
this.changeDetectorRefId = Key.get(ChangeDetectorRef).id;
|
||||
this.elementRefId = Key.get(ElementRef).id;
|
||||
@ -278,7 +278,7 @@ export class DirectiveBinding extends ResolvedBinding {
|
||||
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
|
||||
export class PreBuiltObjects {
|
||||
constructor(public viewManager: avmModule.AppViewManager, public view: viewModule.AppView,
|
||||
public elementRef: ElementRef, public protoView: viewModule.AppProtoView) {}
|
||||
public elementRef: ElementRef, public templateRef: TemplateRef) {}
|
||||
}
|
||||
|
||||
export class EventEmitterAccessor {
|
||||
@ -622,15 +622,15 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
|
||||
return this.getViewContainerRef();
|
||||
}
|
||||
|
||||
if (dirDep.key.id === StaticKeys.instance().protoViewId) {
|
||||
if (isBlank(this._preBuiltObjects.protoView)) {
|
||||
if (dirDep.key.id === StaticKeys.instance().templateRefId) {
|
||||
if (isBlank(this._preBuiltObjects.templateRef)) {
|
||||
if (dirDep.optional) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new NoBindingError(dirDep.key);
|
||||
}
|
||||
return new ProtoViewRef(this._preBuiltObjects.protoView);
|
||||
return this._preBuiltObjects.templateRef;
|
||||
}
|
||||
|
||||
return undefinedValue;
|
||||
|
32
modules/angular2/src/core/compiler/template_ref.ts
Normal file
32
modules/angular2/src/core/compiler/template_ref.ts
Normal file
@ -0,0 +1,32 @@
|
||||
import {internalView, ProtoViewRef} from './view_ref';
|
||||
import {ElementRef} from './element_ref';
|
||||
import * as viewModule from './view';
|
||||
|
||||
/**
|
||||
* Reference to a template within a component.
|
||||
*
|
||||
* Represents an opaque reference to the underlying template that can
|
||||
* be instantiated using the {@Link ViewContainerRef}.
|
||||
*/
|
||||
export class TemplateRef {
|
||||
/**
|
||||
* The location of the template
|
||||
*/
|
||||
elementRef: ElementRef;
|
||||
|
||||
constructor(elementRef: ElementRef) { this.elementRef = elementRef; }
|
||||
|
||||
private _getProtoView(): viewModule.AppProtoView {
|
||||
var parentView = internalView(this.elementRef.parentView);
|
||||
return parentView.proto
|
||||
.elementBinders[this.elementRef.boundElementIndex - parentView.elementOffset]
|
||||
.nestedProtoView;
|
||||
}
|
||||
|
||||
get protoViewRef(): ProtoViewRef { return this._getProtoView().ref; }
|
||||
|
||||
/**
|
||||
* Whether this template has a local variable with the given name
|
||||
*/
|
||||
hasLocal(name: string): boolean { return this._getProtoView().protoLocals.has(name); }
|
||||
}
|
@ -21,7 +21,7 @@ import {ElementBinder} from './element_binder';
|
||||
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
|
||||
import * as renderApi from 'angular2/src/render/api';
|
||||
import {RenderEventDispatcher} from 'angular2/src/render/api';
|
||||
import {ViewRef, internalView} from './view_ref';
|
||||
import {ViewRef, ProtoViewRef, internalView} from './view_ref';
|
||||
import {ElementRef} from './element_ref';
|
||||
|
||||
export class AppProtoViewMergeMapping {
|
||||
@ -256,10 +256,12 @@ export class AppProtoView {
|
||||
elementBinders: List<ElementBinder> = [];
|
||||
protoLocals: Map<string, any> = new Map();
|
||||
mergeMapping: AppProtoViewMergeMapping;
|
||||
ref: ProtoViewRef;
|
||||
|
||||
constructor(public type: renderApi.ViewType, public protoChangeDetector: ProtoChangeDetector,
|
||||
public variableBindings: Map<string, string>,
|
||||
public variableLocations: Map<string, number>, public textBindingCount: number) {
|
||||
this.ref = new ProtoViewRef(this);
|
||||
if (isPresent(variableBindings)) {
|
||||
MapWrapper.forEach(variableBindings,
|
||||
(templateName, _) => { this.protoLocals.set(templateName, null); });
|
||||
|
@ -6,6 +6,7 @@ import * as avmModule from './view_manager';
|
||||
import * as viewModule from './view';
|
||||
|
||||
import {ElementRef} from './element_ref';
|
||||
import {TemplateRef} from './template_ref';
|
||||
import {ViewRef, ProtoViewRef, internalView} from './view_ref';
|
||||
|
||||
export class ViewContainerRef {
|
||||
@ -28,11 +29,16 @@ export class ViewContainerRef {
|
||||
|
||||
// TODO(rado): profile and decide whether bounds checks should be added
|
||||
// to the methods below.
|
||||
create(protoViewRef: ProtoViewRef = null, atIndex: number = -1, context: ElementRef = null,
|
||||
bindings: ResolvedBinding[] = null): ViewRef {
|
||||
createEmbeddedView(templateRef: TemplateRef, atIndex: number = -1): ViewRef {
|
||||
if (atIndex == -1) atIndex = this.length;
|
||||
return this.viewManager.createViewInContainer(this.element, atIndex, protoViewRef, context,
|
||||
bindings);
|
||||
return this.viewManager.createEmbeddedViewInContainer(this.element, atIndex, templateRef);
|
||||
}
|
||||
|
||||
createHostView(protoViewRef: ProtoViewRef = null, atIndex: number = -1,
|
||||
dynamicallyCreatedBindings: ResolvedBinding[] = null): ViewRef {
|
||||
if (atIndex == -1) atIndex = this.length;
|
||||
return this.viewManager.createHostViewInContainer(this.element, atIndex, protoViewRef,
|
||||
dynamicallyCreatedBindings);
|
||||
}
|
||||
|
||||
insert(viewRef: ViewRef, atIndex: number = -1): ViewRef {
|
||||
|
@ -4,6 +4,7 @@ import * as viewModule from './view';
|
||||
import {ElementRef} from './element_ref';
|
||||
import {ProtoViewRef, ViewRef, internalView, internalProtoView} from './view_ref';
|
||||
import {ViewContainerRef} from './view_container_ref';
|
||||
import {TemplateRef} from './template_ref';
|
||||
import {
|
||||
Renderer,
|
||||
RenderViewRef,
|
||||
@ -39,9 +40,11 @@ export class AppViewManager {
|
||||
/**
|
||||
* Return the first child element of the host element view.
|
||||
*/
|
||||
// TODO(misko): remove https://github.com/angular/angular/issues/2891
|
||||
getHostElement(hostViewRef: ViewRef): ElementRef {
|
||||
var hostView = internalView(hostViewRef);
|
||||
if (hostView.proto.type !== ViewType.HOST) {
|
||||
throw new BaseException('This operation is only allowed on host views');
|
||||
}
|
||||
return hostView.elementRefs[hostView.elementOffset];
|
||||
}
|
||||
|
||||
@ -170,22 +173,42 @@ export class AppViewManager {
|
||||
*
|
||||
* See {@link AppViewManager#destroyViewInContainer}.
|
||||
*/
|
||||
createViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
|
||||
protoViewRef: ProtoViewRef, context: ElementRef = null,
|
||||
bindings: ResolvedBinding[] = null): ViewRef {
|
||||
createEmbeddedViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
|
||||
templateRef: TemplateRef): ViewRef {
|
||||
var protoView = internalProtoView(templateRef.protoViewRef);
|
||||
if (protoView.type !== ViewType.EMBEDDED) {
|
||||
throw new BaseException('This method can only be called with embedded ProtoViews!');
|
||||
}
|
||||
return this._createViewInContainer(viewContainerLocation, atIndex, protoView,
|
||||
templateRef.elementRef, null);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* See {@link AppViewManager#destroyViewInContainer}.
|
||||
*/
|
||||
createHostViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
|
||||
protoViewRef: ProtoViewRef,
|
||||
imperativelyCreatedInjector: ResolvedBinding[]): ViewRef {
|
||||
var protoView = internalProtoView(protoViewRef);
|
||||
if (protoView.type !== ViewType.HOST) {
|
||||
throw new BaseException('This method can only be called with host ProtoViews!');
|
||||
}
|
||||
return this._createViewInContainer(viewContainerLocation, atIndex, protoView,
|
||||
viewContainerLocation, imperativelyCreatedInjector);
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* See {@link AppViewManager#destroyViewInContainer}.
|
||||
*/
|
||||
_createViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
|
||||
protoView: viewModule.AppProtoView, context: ElementRef,
|
||||
imperativelyCreatedInjector: ResolvedBinding[]): ViewRef {
|
||||
var parentView = internalView(viewContainerLocation.parentView);
|
||||
var boundElementIndex = viewContainerLocation.boundElementIndex;
|
||||
var contextView = null;
|
||||
var contextBoundElementIndex = null;
|
||||
if (isPresent(context)) {
|
||||
contextView = internalView(context.parentView);
|
||||
contextBoundElementIndex = context.boundElementIndex;
|
||||
} else {
|
||||
contextView = parentView;
|
||||
contextBoundElementIndex = boundElementIndex;
|
||||
}
|
||||
|
||||
var contextView = internalView(context.parentView);
|
||||
var contextBoundElementIndex = context.boundElementIndex;
|
||||
var embeddedFragmentView = contextView.getNestedView(contextBoundElementIndex);
|
||||
var view;
|
||||
if (protoView.type === ViewType.EMBEDDED && isPresent(embeddedFragmentView) &&
|
||||
@ -194,7 +217,8 @@ export class AppViewManager {
|
||||
view = embeddedFragmentView;
|
||||
this._attachRenderView(parentView, boundElementIndex, atIndex, view);
|
||||
} else {
|
||||
// Case 2: instantiate another copy of the template. This is a separate case
|
||||
// Case 2: instantiate another copy of the template or a host ProtoView.
|
||||
// This is a separate case
|
||||
// as we only inline one copy of the template into the parent view.
|
||||
view = this._createPooledView(protoView);
|
||||
this._attachRenderView(parentView, boundElementIndex, atIndex, view);
|
||||
@ -203,7 +227,8 @@ export class AppViewManager {
|
||||
this._utils.attachViewInContainer(parentView, boundElementIndex, contextView,
|
||||
contextBoundElementIndex, atIndex, view);
|
||||
this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView,
|
||||
contextBoundElementIndex, atIndex, bindings);
|
||||
contextBoundElementIndex, atIndex,
|
||||
imperativelyCreatedInjector);
|
||||
return view.ref;
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ import * as viewModule from './view';
|
||||
import {internalView} from './view_ref';
|
||||
import * as avmModule from './view_manager';
|
||||
import {ElementRef} from './element_ref';
|
||||
import {TemplateRef} from './template_ref';
|
||||
import {Renderer, RenderViewWithFragments} from 'angular2/src/render/api';
|
||||
import {Locals} from 'angular2/change_detection';
|
||||
import {RenderViewRef, RenderFragmentRef, ViewType} from 'angular2/src/render/api';
|
||||
@ -83,9 +84,9 @@ export class AppViewManagerUtils {
|
||||
|
||||
// preBuiltObjects
|
||||
if (isPresent(elementInjector)) {
|
||||
var embeddedProtoView = binder.hasEmbeddedProtoView() ? binder.nestedProtoView : null;
|
||||
var templateRef = binder.hasEmbeddedProtoView() ? new TemplateRef(el) : null;
|
||||
preBuiltObjects[boundElementIndex] =
|
||||
new eli.PreBuiltObjects(viewManager, currentView, el, embeddedProtoView);
|
||||
new eli.PreBuiltObjects(viewManager, currentView, el, templateRef);
|
||||
}
|
||||
}
|
||||
currentView.init(protoView.protoChangeDetector.instantiate(currentView), elementInjectors,
|
||||
@ -155,7 +156,7 @@ export class AppViewManagerUtils {
|
||||
|
||||
hydrateViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
|
||||
contextView: viewModule.AppView, contextBoundElementIndex: number,
|
||||
atIndex: number, bindings: ResolvedBinding[]) {
|
||||
atIndex: number, imperativelyCreatedBindings: ResolvedBinding[]) {
|
||||
if (isBlank(contextView)) {
|
||||
contextView = parentView;
|
||||
contextBoundElementIndex = boundElementIndex;
|
||||
@ -164,7 +165,9 @@ export class AppViewManagerUtils {
|
||||
var view = viewContainer.views[atIndex];
|
||||
var elementInjector = contextView.elementInjectors[contextBoundElementIndex];
|
||||
|
||||
var injector = isPresent(bindings) ? Injector.fromResolvedBindings(bindings) : null;
|
||||
var injector = isPresent(imperativelyCreatedBindings) ?
|
||||
Injector.fromResolvedBindings(imperativelyCreatedBindings) :
|
||||
null;
|
||||
this._hydrateView(view, injector, elementInjector.getHost(), contextView.context,
|
||||
contextView.locals);
|
||||
}
|
||||
|
Reference in New Issue
Block a user