Skip to content

Commit f42382d

Browse files
committed
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 angular#1989 to clean up manual content projection. Closes angular#3114
1 parent 762a94f commit f42382d

File tree

17 files changed

+198
-126
lines changed

17 files changed

+198
-126
lines changed

modules/angular2/core.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export {AppViewManager} from 'angular2/src/core/compiler/view_manager';
1818
export {IQueryList} from 'angular2/src/core/compiler/interface_query';
1919
export {QueryList} from 'angular2/src/core/compiler/query_list';
2020
export {ElementRef} from 'angular2/src/core/compiler/element_ref';
21+
export {TemplateRef} from 'angular2/src/core/compiler/template_ref';
2122
export {RenderElementRef} from 'angular2/src/render/api';
2223
export {ViewRef, ProtoViewRef} from 'angular2/src/core/compiler/view_ref';
2324
export {ViewContainerRef} from 'angular2/src/core/compiler/view_container_ref';

modules/angular2/src/core/annotations_impl/annotations.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -360,12 +360,12 @@ import {DEFAULT} from 'angular2/change_detection';
360360
* })
361361
* export class Unless {
362362
* viewContainer: ViewContainerRef;
363-
* protoViewRef: ProtoViewRef;
363+
* templateRef: TemplateRef;
364364
* prevCondition: boolean;
365365
*
366-
* constructor(viewContainer: ViewContainerRef, protoViewRef: ProtoViewRef) {
366+
* constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef) {
367367
* this.viewContainer = viewContainer;
368-
* this.protoViewRef = protoViewRef;
368+
* this.templateRef = templateRef;
369369
* this.prevCondition = null;
370370
* }
371371
*
@@ -375,7 +375,7 @@ import {DEFAULT} from 'angular2/change_detection';
375375
* this.viewContainer.clear();
376376
* } else if (!newCondition && (isBlank(this.prevCondition) || this.prevCondition)) {
377377
* this.prevCondition = false;
378-
* this.viewContainer.create(this.protoViewRef);
378+
* this.viewContainer.create(this.templateRef);
379379
* }
380380
* }
381381
* }

modules/angular2/src/core/compiler/compiler.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,8 @@ export class Compiler {
146146
return this._compileNestedProtoViews(hostRenderPv, protoView, componentType);
147147
});
148148
}
149-
return hostPvPromise.then(hostAppProtoView => this._mergeCyclicEmbeddedProtoViews().then(
150-
_ => new ProtoViewRef(hostAppProtoView)));
149+
return hostPvPromise.then(
150+
hostAppProtoView => this._mergeCyclicEmbeddedProtoViews().then(_ => hostAppProtoView.ref));
151151
}
152152

153153
private _compile(componentBinding: DirectiveBinding): Promise<AppProtoView>| AppProtoView {

modules/angular2/src/core/compiler/dynamic_component_loader.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ export class DynamicComponentLoader {
6363
.then(hostProtoViewRef => {
6464
var viewContainer = this._viewManager.getViewContainer(location);
6565
var hostViewRef =
66-
viewContainer.create(hostProtoViewRef, viewContainer.length, null, bindings);
66+
viewContainer.createHostView(hostProtoViewRef, viewContainer.length, bindings);
6767
var newLocation = this._viewManager.getHostElement(hostViewRef);
6868
var component = this._viewManager.getComponent(newLocation);
6969

modules/angular2/src/core/compiler/element_injector.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import * as viewModule from './view';
4040
import * as avmModule from './view_manager';
4141
import {ViewContainerRef} from './view_container_ref';
4242
import {ElementRef} from './element_ref';
43-
import {ProtoViewRef, ViewRef} from './view_ref';
43+
import {TemplateRef} from './template_ref';
4444
import {Directive, Component, LifecycleEvent} from 'angular2/src/core/annotations_impl/annotations';
4545
import {hasLifecycleHook} from './directive_lifecycle_reflector';
4646
import {ChangeDetector, ChangeDetectorRef, Pipes} from 'angular2/change_detection';
@@ -52,15 +52,15 @@ var _staticKeys;
5252

5353
export class StaticKeys {
5454
viewManagerId: number;
55-
protoViewId: number;
55+
templateRefId: number;
5656
viewContainerId: number;
5757
changeDetectorRefId: number;
5858
elementRefId: number;
5959
pipesKey: Key;
6060

6161
constructor() {
6262
this.viewManagerId = Key.get(avmModule.AppViewManager).id;
63-
this.protoViewId = Key.get(ProtoViewRef).id;
63+
this.templateRefId = Key.get(TemplateRef).id;
6464
this.viewContainerId = Key.get(ViewContainerRef).id;
6565
this.changeDetectorRefId = Key.get(ChangeDetectorRef).id;
6666
this.elementRefId = Key.get(ElementRef).id;
@@ -278,7 +278,7 @@ export class DirectiveBinding extends ResolvedBinding {
278278
// TODO(rado): benchmark and consider rolling in as ElementInjector fields.
279279
export class PreBuiltObjects {
280280
constructor(public viewManager: avmModule.AppViewManager, public view: viewModule.AppView,
281-
public elementRef: ElementRef, public protoView: viewModule.AppProtoView) {}
281+
public elementRef: ElementRef, public templateRef: TemplateRef) {}
282282
}
283283

284284
export class EventEmitterAccessor {
@@ -622,15 +622,15 @@ export class ElementInjector extends TreeNode<ElementInjector> implements Depend
622622
return this.getViewContainerRef();
623623
}
624624

625-
if (dirDep.key.id === StaticKeys.instance().protoViewId) {
626-
if (isBlank(this._preBuiltObjects.protoView)) {
625+
if (dirDep.key.id === StaticKeys.instance().templateRefId) {
626+
if (isBlank(this._preBuiltObjects.templateRef)) {
627627
if (dirDep.optional) {
628628
return null;
629629
}
630630

631631
throw new NoBindingError(dirDep.key);
632632
}
633-
return new ProtoViewRef(this._preBuiltObjects.protoView);
633+
return this._preBuiltObjects.templateRef;
634634
}
635635

636636
return undefinedValue;
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import {internalView, ProtoViewRef} from './view_ref';
2+
import {ElementRef} from './element_ref';
3+
import * as viewModule from './view';
4+
5+
/**
6+
* Reference to a template within a component.
7+
*
8+
* Represents an opaque reference to the underlying template that can
9+
* be instantiated using the {@Link ViewContainerRef}.
10+
*/
11+
export class TemplateRef {
12+
/**
13+
* The location of the template
14+
*/
15+
elementRef: ElementRef;
16+
17+
constructor(elementRef: ElementRef) { this.elementRef = elementRef; }
18+
19+
private _getProtoView(): viewModule.AppProtoView {
20+
var parentView = internalView(this.elementRef.parentView);
21+
return parentView.proto
22+
.elementBinders[this.elementRef.boundElementIndex - parentView.elementOffset]
23+
.nestedProtoView;
24+
}
25+
26+
get protoViewRef(): ProtoViewRef { return this._getProtoView().ref; }
27+
28+
/**
29+
* Whether this template has a local variable with the given name
30+
*/
31+
hasLocal(name: string): boolean { return this._getProtoView().protoLocals.has(name); }
32+
}

modules/angular2/src/core/compiler/view.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {ElementBinder} from './element_binder';
2121
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
2222
import * as renderApi from 'angular2/src/render/api';
2323
import {RenderEventDispatcher} from 'angular2/src/render/api';
24-
import {ViewRef, internalView} from './view_ref';
24+
import {ViewRef, ProtoViewRef, internalView} from './view_ref';
2525
import {ElementRef} from './element_ref';
2626

2727
export class AppProtoViewMergeMapping {
@@ -256,10 +256,12 @@ export class AppProtoView {
256256
elementBinders: List<ElementBinder> = [];
257257
protoLocals: Map<string, any> = new Map();
258258
mergeMapping: AppProtoViewMergeMapping;
259+
ref: ProtoViewRef;
259260

260261
constructor(public type: renderApi.ViewType, public protoChangeDetector: ProtoChangeDetector,
261262
public variableBindings: Map<string, string>,
262263
public variableLocations: Map<string, number>, public textBindingCount: number) {
264+
this.ref = new ProtoViewRef(this);
263265
if (isPresent(variableBindings)) {
264266
MapWrapper.forEach(variableBindings,
265267
(templateName, _) => { this.protoLocals.set(templateName, null); });

modules/angular2/src/core/compiler/view_container_ref.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as avmModule from './view_manager';
66
import * as viewModule from './view';
77

88
import {ElementRef} from './element_ref';
9+
import {TemplateRef} from './template_ref';
910
import {ViewRef, ProtoViewRef, internalView} from './view_ref';
1011

1112
export class ViewContainerRef {
@@ -28,11 +29,16 @@ export class ViewContainerRef {
2829

2930
// TODO(rado): profile and decide whether bounds checks should be added
3031
// to the methods below.
31-
create(protoViewRef: ProtoViewRef = null, atIndex: number = -1, context: ElementRef = null,
32-
bindings: ResolvedBinding[] = null): ViewRef {
32+
createEmbeddedView(templateRef: TemplateRef, atIndex: number = -1): ViewRef {
3333
if (atIndex == -1) atIndex = this.length;
34-
return this.viewManager.createViewInContainer(this.element, atIndex, protoViewRef, context,
35-
bindings);
34+
return this.viewManager.createEmbeddedViewInContainer(this.element, atIndex, templateRef);
35+
}
36+
37+
createHostView(protoViewRef: ProtoViewRef = null, atIndex: number = -1,
38+
dynamicallyCreatedBindings: ResolvedBinding[] = null): ViewRef {
39+
if (atIndex == -1) atIndex = this.length;
40+
return this.viewManager.createHostViewInContainer(this.element, atIndex, protoViewRef,
41+
dynamicallyCreatedBindings);
3642
}
3743

3844
insert(viewRef: ViewRef, atIndex: number = -1): ViewRef {

modules/angular2/src/core/compiler/view_manager.ts

Lines changed: 41 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import * as viewModule from './view';
44
import {ElementRef} from './element_ref';
55
import {ProtoViewRef, ViewRef, internalView, internalProtoView} from './view_ref';
66
import {ViewContainerRef} from './view_container_ref';
7+
import {TemplateRef} from './template_ref';
78
import {
89
Renderer,
910
RenderViewRef,
@@ -39,9 +40,11 @@ export class AppViewManager {
3940
/**
4041
* Return the first child element of the host element view.
4142
*/
42-
// TODO(misko): remove https://github.com/angular/angular/issues/2891
4343
getHostElement(hostViewRef: ViewRef): ElementRef {
4444
var hostView = internalView(hostViewRef);
45+
if (hostView.proto.type !== ViewType.HOST) {
46+
throw new BaseException('This operation is only allowed on host views');
47+
}
4548
return hostView.elementRefs[hostView.elementOffset];
4649
}
4750

@@ -170,22 +173,42 @@ export class AppViewManager {
170173
*
171174
* See {@link AppViewManager#destroyViewInContainer}.
172175
*/
173-
createViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
174-
protoViewRef: ProtoViewRef, context: ElementRef = null,
175-
bindings: ResolvedBinding[] = null): ViewRef {
176+
createEmbeddedViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
177+
templateRef: TemplateRef): ViewRef {
178+
var protoView = internalProtoView(templateRef.protoViewRef);
179+
if (protoView.type !== ViewType.EMBEDDED) {
180+
throw new BaseException('This method can only be called with embedded ProtoViews!');
181+
}
182+
return this._createViewInContainer(viewContainerLocation, atIndex, protoView,
183+
templateRef.elementRef, null);
184+
}
185+
186+
/**
187+
*
188+
* See {@link AppViewManager#destroyViewInContainer}.
189+
*/
190+
createHostViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
191+
protoViewRef: ProtoViewRef,
192+
imperativelyCreatedInjector: ResolvedBinding[]): ViewRef {
176193
var protoView = internalProtoView(protoViewRef);
177-
var parentView = internalView(viewContainerLocation.parentView);
178-
var boundElementIndex = viewContainerLocation.boundElementIndex;
179-
var contextView = null;
180-
var contextBoundElementIndex = null;
181-
if (isPresent(context)) {
182-
contextView = internalView(context.parentView);
183-
contextBoundElementIndex = context.boundElementIndex;
184-
} else {
185-
contextView = parentView;
186-
contextBoundElementIndex = boundElementIndex;
194+
if (protoView.type !== ViewType.HOST) {
195+
throw new BaseException('This method can only be called with host ProtoViews!');
187196
}
197+
return this._createViewInContainer(viewContainerLocation, atIndex, protoView,
198+
viewContainerLocation, imperativelyCreatedInjector);
199+
}
188200

201+
/**
202+
*
203+
* See {@link AppViewManager#destroyViewInContainer}.
204+
*/
205+
_createViewInContainer(viewContainerLocation: ElementRef, atIndex: number,
206+
protoView: viewModule.AppProtoView, context: ElementRef,
207+
imperativelyCreatedInjector: ResolvedBinding[]): ViewRef {
208+
var parentView = internalView(viewContainerLocation.parentView);
209+
var boundElementIndex = viewContainerLocation.boundElementIndex;
210+
var contextView = internalView(context.parentView);
211+
var contextBoundElementIndex = context.boundElementIndex;
189212
var embeddedFragmentView = contextView.getNestedView(contextBoundElementIndex);
190213
var view;
191214
if (protoView.type === ViewType.EMBEDDED && isPresent(embeddedFragmentView) &&
@@ -194,7 +217,8 @@ export class AppViewManager {
194217
view = embeddedFragmentView;
195218
this._attachRenderView(parentView, boundElementIndex, atIndex, view);
196219
} else {
197-
// Case 2: instantiate another copy of the template. This is a separate case
220+
// Case 2: instantiate another copy of the template or a host ProtoView.
221+
// This is a separate case
198222
// as we only inline one copy of the template into the parent view.
199223
view = this._createPooledView(protoView);
200224
this._attachRenderView(parentView, boundElementIndex, atIndex, view);
@@ -203,7 +227,8 @@ export class AppViewManager {
203227
this._utils.attachViewInContainer(parentView, boundElementIndex, contextView,
204228
contextBoundElementIndex, atIndex, view);
205229
this._utils.hydrateViewInContainer(parentView, boundElementIndex, contextView,
206-
contextBoundElementIndex, atIndex, bindings);
230+
contextBoundElementIndex, atIndex,
231+
imperativelyCreatedInjector);
207232
return view.ref;
208233
}
209234

modules/angular2/src/core/compiler/view_manager_utils.ts

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import * as viewModule from './view';
66
import {internalView} from './view_ref';
77
import * as avmModule from './view_manager';
88
import {ElementRef} from './element_ref';
9+
import {TemplateRef} from './template_ref';
910
import {Renderer, RenderViewWithFragments} from 'angular2/src/render/api';
1011
import {Locals} from 'angular2/change_detection';
1112
import {RenderViewRef, RenderFragmentRef, ViewType} from 'angular2/src/render/api';
@@ -83,9 +84,9 @@ export class AppViewManagerUtils {
8384

8485
// preBuiltObjects
8586
if (isPresent(elementInjector)) {
86-
var embeddedProtoView = binder.hasEmbeddedProtoView() ? binder.nestedProtoView : null;
87+
var templateRef = binder.hasEmbeddedProtoView() ? new TemplateRef(el) : null;
8788
preBuiltObjects[boundElementIndex] =
88-
new eli.PreBuiltObjects(viewManager, currentView, el, embeddedProtoView);
89+
new eli.PreBuiltObjects(viewManager, currentView, el, templateRef);
8990
}
9091
}
9192
currentView.init(protoView.protoChangeDetector.instantiate(currentView), elementInjectors,
@@ -155,7 +156,7 @@ export class AppViewManagerUtils {
155156

156157
hydrateViewInContainer(parentView: viewModule.AppView, boundElementIndex: number,
157158
contextView: viewModule.AppView, contextBoundElementIndex: number,
158-
atIndex: number, bindings: ResolvedBinding[]) {
159+
atIndex: number, imperativelyCreatedBindings: ResolvedBinding[]) {
159160
if (isBlank(contextView)) {
160161
contextView = parentView;
161162
contextBoundElementIndex = boundElementIndex;
@@ -164,7 +165,9 @@ export class AppViewManagerUtils {
164165
var view = viewContainer.views[atIndex];
165166
var elementInjector = contextView.elementInjectors[contextBoundElementIndex];
166167

167-
var injector = isPresent(bindings) ? Injector.fromResolvedBindings(bindings) : null;
168+
var injector = isPresent(imperativelyCreatedBindings) ?
169+
Injector.fromResolvedBindings(imperativelyCreatedBindings) :
170+
null;
168171
this._hydrateView(view, injector, elementInjector.getHost(), contextView.context,
169172
contextView.locals);
170173
}

0 commit comments

Comments
 (0)