8000 feat(core): introduce template context · angular/angular@cacdead · GitHub
[go: up one dir, main page]

Skip to content

Commit cacdead

Browse files
committed
feat(core): introduce template context
BREAKING CHANGE: - Before, a `EmbeddedViewRef` used to have methods for setting variables. Now, a user has to pass in a context object that represents all variables when an `EmbeddedViewRef` should be created. - `ViewContainerRef.createEmbeddedViewRef` now takes a context object as 2nd argument. - `EmbeddedViewRef.setLocal` and `getLocal` have been removed. Use `EmbeddedViewRef.context` to access the context. - `DebugNode.locals` has been removed. Use the new methods `DebugElement.references` to get the references that are present on this element, or `DebugElement.context` to get the context of the `EmbeddedViewRef` or the component to which the element belongs. Closes #8321
1 parent 96ae348 commit cacdead

28 files changed

+277
-246
lines changed

modules/angular2/src/common/directives/ng_for.ts

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,18 @@ import {
1616
} from "../../core/change_detection/differs/default_iterable_differ";
1717
import {BaseException} from "../../facade/exceptions";
1818

19+
export class NgForRow {
20+
constructor(public $implicit: any, public index: number, public count: number) {}
21+
22+
get first(): boolean { return this.index === 0; }
23+
24+
get last(): boolean { return this.index === this.count - 1; }
25+
26+
get even(): boolean { return this.index % 2 === 0; }
27+
28+
get odd(): boolean { return !this.even; }
29+
}
30+
1931
/**
2032
* The `NgFor` directive instantiates a template once per item from an iterable. The context for
2133
* each instantiated template inherits from the outer context with the given loop variable set
@@ -75,7 +87,7 @@ export class NgFor implements DoCheck {
7587
_ngForTrackBy: TrackByFn;
7688
private _differ: IterableDiffer;
7789

78-
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef,
90+
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<NgForRow>,
7991
private _iterableDiffers: IterableDiffers, private _cdr: ChangeDetectorRef) {}
8092

8193
set ngForOf(value: any) {
@@ -90,7 +102,7 @@ export class NgFor implements DoCheck {
90102
}
91103
}
92104

93-
set ngForTemplate(value: TemplateRef) {
105+
set ngForTemplate(value: TemplateRef<NgForRow>) {
94106
if (isPresent(value)) {
95107
this._templateRef = value;
96108
}
@@ -127,22 +139,19 @@ export class NgFor implements DoCheck {
127139
}
128140

129141
for (var i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
130-
var viewRef = <EmbeddedViewRef>this._viewContainer.get(i);
131-
viewRef.setLocal('first', i === 0);
132-
viewRef.setLocal('last', i === ilen - 1);
142+
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
143+
viewRef.context.index = i;
144+
viewRef.context.count = ilen;
133145
}
134146

135147
changes.forEachIdentityChange((record) => {
136-
var viewRef = <EmbeddedViewRef>this._viewContainer.get(record.currentIndex);
137-
viewRef.setLocal('\$implicit', record.item);
148+
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
149+
viewRef.context.$implicit = record.item;
138150
});
139151
}
140152

141-
private _perViewChange(view: EmbeddedViewRef, record: CollectionChangeRecord) {
142-
view.setLocal('\$implicit', record.item);
143-
view.setLocal('index', record.currentIndex);
144-
view.setLocal('even', (record.currentIndex % 2 == 0));
145-
view.setLocal('odd', (record.currentIndex % 2 == 1));
153+
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: CollectionChangeRecord) {
154+
view.context.$implicit = record.item;
146155
}
147156

148157
private _bulkRemove(tuples: RecordViewTuple[]): RecordViewTuple[] {
@@ -153,7 +162,8 @@ export class NgFor implements DoCheck {
153162
var tuple = tuples[i];
154163
// separate moved views from removed views.
155164
if (isPresent(tuple.record.currentIndex)) {
156-
tuple.view = <EmbeddedViewRef>this._viewContainer.detach(tuple.record.previousIndex);
165+
tuple.view =
166+
<EmbeddedViewRef<NgForRow>>this._viewContainer.detach(tuple.record.previousIndex);
157167
movedTuples.push(tuple);
158168
} else {
159169
this._viewContainer.remove(tuple.record.previousIndex);
@@ -169,18 +179,18 @@ export class NgFor implements DoCheck {
169179
if (isPresent(tuple.view)) {
170180
this._viewContainer.insert(tuple.view, tuple.record.currentIndex);
171181
} else {
172-
tuple.view =
173-
this._viewContainer.createEmbeddedView(this._templateRef, tuple.record.currentIndex);
182+
tuple.view = this._viewContainer.createEmbeddedView(
183+
this._templateRef, new NgForRow(null, null, null), tuple.record.currentIndex);
174184
}
175185
}
176186
return tuples;
177187
}
178188
}
179189

180190
class RecordViewTuple {
181-
view: EmbeddedViewRef;
191+
view: EmbeddedViewRef<NgForRow>;
182192
record: any;
183-
constructor(record: any, view: EmbeddedViewRef) {
193+
constructor(record: any, view: EmbeddedViewRef<NgForRow>) {
184194
this.record = record;
185195
this.view = view;
186196
}

modules/angular2/src/common/directives/ng_if.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,8 @@ import {isBlank} from 'angular2/src/facade/lang';
2727
export class NgIf {
2828
private _prevCondition: boolean = null;
2929

30-
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef) {}
30+
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<Object>) {
31+
}
3132

3233
set ngIf(newCondition: any /* boolean */) {
3334
if (newCondition && (isBlank(this._prevCondition) || !this._prevCondition)) {

modules/angular2/src/common/directives/ng_plural.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export abstract class NgLocalization { abstract getPluralCategory(value: any): s
7676
export class NgPluralCase {
7777
/** @internal */
7878
_view: SwitchView;
79-
constructor(@Attribute('ngPluralCase') public value: string, template: TemplateRef,
79+
constructor(@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
8080
viewContainer: ViewContainerRef) {
8181
this._view = new SwitchView(viewContainer, template);
8282
}

modules/angular2/src/common/directives/ng_switch.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import {ListWrapper, Map} from 'angular2/src/facade/collection';
55
const _WHEN_DEFAULT = CONST_EXPR(new Object());
66

77
export class SwitchView {
8-
constructor(private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef) {}
8+
constructor(private _viewContainerRef: ViewContainerRef,
9+
private _templateRef: TemplateRef<Object>) {}
910

1011
create(): void { this._viewContainerRef.createEmbeddedView(this._templateRef); }
1112

@@ -175,7 +176,7 @@ export class NgSwitchWhen {
175176
_view: SwitchView;
176177
private _switch: NgSwitch;
177178

178-
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef,
179+
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
179180
@Host() ngSwitch: NgSwitch) {
180181
this._switch = ngSwitch;
181182
this._view = new SwitchView(viewContainer, templateRef);
@@ -195,7 +196,7 @@ export class NgSwitchWhen {
195196
*/
196197
@Directive({selector: '[ngSwitchDefault]'})
197198
export class NgSwitchDefault {
198-
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef,
199+
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
199200
@Host() sswitch: NgSwitch) {
200201
sswitch._registerView(_WHEN_DEFAULT, new SwitchView(viewContainer, templateRef));
201202
}

modules/angular2/src/common/directives/ng_template_outlet.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class NgTemplateOutlet {
1414
constructor(private _viewContainerRef: ViewContainerRef) {}
1515

1616
@Input()
17-
set ngTemplateOutlet(templateRef: TemplateRef) {
17+
set ngTemplateOutlet(templateRef: TemplateRef<Object>) {
1818
if (isPresent(this._insertedViewRef)) {
1919
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._insertedViewRef));
2020
}

modules/angular2/src/compiler/output/interpretive_view.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export class InterpretiveAppViewInstanceFactory implements InstanceFactory {
1717
class _InterpretiveAppView extends AppView<any> implements DynamicInstance {
1818
constructor(args: any[], public props: Map<string, any>, public getters: Map<string, Function>,
1919
public methods: Map<string, Function>) {
20-
super(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
20+
super(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
2121
}
2222
createInternal(rootSelector: string | any): AppElement {
2323
var m = this.methods.get('createInternal');

modules/angular2/src/compiler/view_compiler/compile_view.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,8 @@ export class CompileView implements NameResolver {
6565
public literalMapCount = 0;
6666
public pipeCount = 0;
6767

68+
public componentContext: o.Expression;
69+
6870
constructor(public component: CompileDirectiveMetadata, public genConfig: CompilerConfig,
6971
public pipeMetas: CompilePipeMetadata[], public styles: o.Expression,
7072
public viewIndex: number, public declarationElement: CompileElement,
@@ -90,6 +92,9 @@ export class CompileView implements NameResolver {
9092
} else {
9193
this.componentView = this.declarationElement.view.componentView;
9294
}
95+
this.componentContext =
96+
getPropertyInView(o.THIS_EXPR.prop('context'), this, this.componentView);
97+
9398
var viewQueries = new CompileTokenMap<CompileQuery[]>();
9499
if (this.viewType === ViewType.COMPONENT) {
95100
var directiveInstance = o.THIS_EXPR.prop('context');
@@ -111,9 +116,8 @@ export class CompileView implements NameResolver {
111116
});
112117
}
113118
this.viewQueries = viewQueries;
114-
templateVariableBindings.forEach((entry) => {
115-
this.locals.set(entry[1], o.THIS_EXPR.prop('locals').key(o.literal(entry[0])));
116-
});
119+
templateVariableBindings.forEach(
120+
(entry) => { this.locals.set(entry[1], o.THIS_EXPR.prop('context').prop(entry[0])); });
117121

118122
if (!this.declarationElement.isNull()) {
119123
this.declarationElement.setEmbeddedView(this);

modules/angular2/src/compiler/view_compiler/event_binder.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ export class CompileEventListener {
4747
this._hasComponentHostListener = true;
4848
}
4949
this._method.resetDebugInfo(this.compileElement.nodeIndex, hostEvent);
50-
var context = isPresent(directiveInstance) ? directiveInstance : o.THIS_EXPR.prop('context');
50+
var context = isPresent(directiveInstance) ? directiveInstance :
51+
this.compileElement.view.componentContext;
5152
var actionStmts = convertCdStatementToIr(this.compileElement.view, context, hostEvent.handler);
5253
var lastIndex = actionStmts.length - 1;
5354
if (lastIndex >= 0) {

modules/angular2/src/compiler/view_compiler/property_binder.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export function bindRenderText(boundText: BoundTextAst, compileNode: CompileNode
7373
var valueField = createBindFieldExpr(bindingIndex);
7474
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
7575

76-
bind(view, currValExpr, valueField, boundText.value, o.THIS_EXPR.prop('context'),
76+
bind(view, currValExpr, valueField, boundText.value, view.componentContext,
7777
[
7878
o.THIS_EXPR.prop('renderer')
7979
.callMethod('setText', [compileNode.renderNode, currValExpr])
@@ -131,7 +131,7 @@ function bindAndWriteToRenderer(boundProps: BoundElementPropertyAst[], context:
131131

132132
export function bindRenderInputs(boundProps: BoundElementPropertyAst[],
133133
compileElement: CompileElement): void {
134-
bindAndWriteToRenderer(boundProps, o.THIS_EXPR.prop('context'), compileElement);
134+
bindAndWriteToRenderer(boundProps, compileElement.view.componentContext, compileElement);
135135
}
136136

137137
export function bindDirectiveHostProps(directiveAst: DirectiveAst, directiveInstance: o.Expression,
@@ -184,7 +184,7 @@ export function bindDirectiveInputs(directiveAst: DirectiveAst, directiveInstanc
184184
statements.push(
185185
logBindingUpdateStmt(compileElement.renderNode, input.directiveName, currValExpr));
186186
}
187-
bind(view, currValExpr, fieldExpr, input.value, o.THIS_EXPR.prop('context'), statements,
187+
bind(view, currValExpr, fieldExpr, input.value, view.componentContext, statements,
188188
detectChangesInInputsMethod);
189189
});
190190
if (isOnPushComp) {

modules/angular2/src/compiler/view_compiler/view_builder.ts

+7Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -246,7 +246,9 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
246246
compileElement.contentNodesByNgContentIndex.map(nodes => createFlatArray(nodes)));
247247
}
248248
this.view.createMethod.addStmt(
249-
compViewExpr.callMethod('create', [codeGenContentNodes, o.NULL_EXPR]).toStmt());
249+
compViewExpr.callMethod('create',
250+
[compileElement.getComponent(), codeGenContentNodes, o.NULL_EXPR])
251+
.toStmt());
250252
}
251253
return null;
252254
}
@@ -391,8 +393,6 @@ function createStaticNodeDebugInfo(node: CompileNode): o.Expression {
391393

392394
function createViewClass(view: CompileView, renderCompTypeVar: o.ReadVarExpr,
393395
nodeDebugInfosVar: o.Expression): o.ClassStmt {
394-
var emptyTemplateVariableBindings =
395-
view.templateVariableBindings.map((entry) => [entry[0], o.NULL_EXPR]);
396396
var viewConstructorArgs = [
397397
new o.FnParam(ViewConstructorVars.viewUtils.name, o.importType(Identifiers.ViewUtils)),
398398
new o.FnParam(ViewConstructorVars.parentInjector.name, o.importType(Identifiers.Injector)),
@@ -403,7 +403,6 @@ function createViewClass(view: CompileView, renderCompTypeVar: o.ReadVarExpr,
403403
o.variable(view.className),
404404
renderCompTypeVar,
405405
ViewTypeEnum.fromValue(view.viewType),
406-
o.literalMap(emptyTemplateVariableBindings),
407406
ViewConstructorVars.viewUtils,
408407
ViewConstructorVars.parentInjector,
409408
ViewConstructorVars.declarationEl,
@@ -563,8 +562,10 @@ function addReturnValuefNotEmpty(statements: o.Statement[], value: o.Expression)
563562
}
564563

565564
function getContextType(view: CompileView): o.Type {
566-
var typeMeta = view.component.type;
567-
return typeMeta.isHost ? o.DYNAMIC_TYPE : o.importType(typeMeta);
565+
if (view.viewType === ViewType.COMPONENT) {
566+
return o.importType(view.component.type);
567+
}
568+
return o.DYNAMIC_TYPE;
568569
}
569570

570571
function getChangeDetectionMode(view: CompileView): ChangeDetectionStrategy {

modules/angular2/src/core/debug/debug_node.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,10 @@ export class DebugNode {
2626
return isPresent(this._debugInfo) ? this._debugInfo.component : null;
2727
}
2828

29-
get locals(): {[key: string]: any} {
30-
return isPresent(this._debugInfo) ? this._debugInfo.locals : null;
29+
get context(): any { return isPresent(this._debugInfo) ? this._debugInfo.context : null; }
30+
31+
get references(): {[key: string]: any} {
32+
return isPresent(this._debugInfo) ? this._debugInfo.references : null;
3133
}
3234

3335
get providerTokens(): any[] {
@@ -37,8 +39,6 @@ export class DebugNode {
3739
get source(): string { return isPresent(this._debugInfo) ? this._debugInfo.source : null; }
3840

3941
inject(token: any): any { return this.injector.get(token); }
40-
41-
getLocal(name: string): any { return this.locals[name]; }
4242
}
4343

4444
export class DebugElement extends DebugNode {

modules/angular2/src/core/linker/component_factory.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {Injector} from 'angular2/src/core/di';
2-
import {Type, CONST, isPresent, isBlank} from 'angular2/src/facade/lang';
2+
import {Type, CONST, CONST_EXPR, isPresent, isBlank} from 'angular2/src/facade/lang';
33
import {unimplemented} from 'angular2/src/facade/exceptions';
44
import {ElementRef} from './element_ref';
55
import {ViewRef, ViewRef_} from './view_ref';
@@ -69,6 +69,8 @@ export class ComponentRef_ extends ComponentRef {
6969
onDestroy(callback: Function): void { this.hostView.onDestroy(callback); }
7070
}
7171

72+
const EMPTY_CONTEXT = CONST_EXPR(new Object());
73+
7274
@CONST()
7375
export class ComponentFactory {
7476
constructor(public selector: string, private _viewFactory: Function,
@@ -87,7 +89,7 @@ export class ComponentFactory {
8789
}
8890
// Note: Host views don't need a declarationAppElement!
8991
var hostView = this._viewFactory(vu, injector, null);
90-
var hostElement = hostView.create(projectableNodes, rootSelectorOrNode);
92+
var hostElement = hostView.create(EMPTY_CONTEXT, projectableNodes, rootSelectorOrNode);
9193
return new ComponentRef_(hostElement, this._componentType);
9294
}
9395
}

modules/angular2/src/core/linker/debug_context.ts

Lines changed: 14 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -52,28 +52,21 @@ export class DebugContext implements RenderDebugInfo {
5252
get source(): string {
5353
return `${this._view.componentType.templateUrl}:${this._tplRow}:${this._tplCol}`;
5454
}
55-
get locals(): {[key: string]: string} {
55+
get references(): {[key: string]: any} {
5656
var varValues: {[key: string]: string} = {};
57-
// TODO(tbosch): right now, the semantics of debugNode.locals are
58-
// that it contains the variables of all elements, not just
59-
// the given one. We preserve this for now to not have a breaking
60-
// change, but should change this later!
61-
ListWrapper.forEachWithIndex(
62-
this._view.staticNodeDebugInfos,
63-
(staticNodeInfo: StaticNodeDebugInfo, nodeIndex: number) => {
64-
var refs = staticNodeInfo.refTokens;
65-
StringMapWrapper.forEach(refs, (refToken, refName) => {
66-
var varValue;
67-
if (isBlank(refToken)) {
68-
varValue = isPresent(this._view.allNodes) ? this._view.allNodes[nodeIndex] : null;
69-
} else {
70-
varValue = this._view.injectorGet(refToken, nodeIndex, null);
71-
}
72-
varValues[refName] = varValue;
73-
});
74-
});
75-
StringMapWrapper.forEach(this._view.locals,
76-
(localValue, localName) => { varValues[localName] = localValue; });
57+
var staticNodeInfo = this._staticNodeInfo;
58+
if (isPresent(staticNodeInfo)) {
59+
var refs = staticNodeInfo.refTokens;
60+
StringMapWrapper.forEach(refs, (refToken, refName) => {
61+
var varValue;
62+
if (isBlank(refToken)) {
63+
varValue = isPresent(this._view.allNodes) ? this._view.allNodes[this._nodeIndex] : null;
64+
} else {
65+
varValue = this._view.injectorGet(refToken, this._nodeIndex, null);
66+
}
67+
varValues[refName] = varValue;
68+
});
69+
}
7770
return varValues;
7871
}
7972
}

0 commit comments

Comments
 (0)
0