Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WIP: Template context #8321

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
44 changes: 27 additions & 17 deletions modules/angular2/src/common/directives/ng_for.ts
Expand Up @@ -16,6 +16,18 @@ import {
} from "../../core/change_detection/differs/default_iterable_differ";
import {BaseException} from "../../facade/exceptions";

export class NgForRow {
constructor(public $implicit: any, public index: number, public count: number) {}

get first(): boolean { return this.index === 0; }

get last(): boolean { return this.index === this.count - 1; }

get even(): boolean { return this.index % 2 === 0; }

get odd(): boolean { return !this.even; }
}

/**
* The `NgFor` directive instantiates a template once per item from an iterable. The context for
* each instantiated template inherits from the outer context with the given loop variable set
Expand Down Expand Up @@ -75,7 +87,7 @@ export class NgFor implements DoCheck {
_ngForTrackBy: TrackByFn;
private _differ: IterableDiffer;

constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef,
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<NgForRow>,
private _iterableDiffers: IterableDiffers, private _cdr: ChangeDetectorRef) {}

set ngForOf(value: any) {
Expand All @@ -90,7 +102,7 @@ export class NgFor implements DoCheck {
}
}

set ngForTemplate(value: TemplateRef) {
set ngForTemplate(value: TemplateRef<NgForRow>) {
if (isPresent(value)) {
this._templateRef = value;
}
Expand Down Expand Up @@ -127,22 +139,19 @@ export class NgFor implements DoCheck {
}

for (var i = 0, ilen = this._viewContainer.length; i < ilen; i++) {
var viewRef = <EmbeddedViewRef>this._viewContainer.get(i);
viewRef.setLocal('first', i === 0);
viewRef.setLocal('last', i === ilen - 1);
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(i);
viewRef.context.index = i;
viewRef.context.count = ilen;
}

changes.forEachIdentityChange((record) => {
var viewRef = <EmbeddedViewRef>this._viewContainer.get(record.currentIndex);
viewRef.setLocal('\$implicit', record.item);
var viewRef = <EmbeddedViewRef<NgForRow>>this._viewContainer.get(record.currentIndex);
viewRef.context.$implicit = record.item;
});
}

private _perViewChange(view: EmbeddedViewRef, record: CollectionChangeRecord) {
view.setLocal('\$implicit', record.item);
view.setLocal('index', record.currentIndex);
view.setLocal('even', (record.currentIndex % 2 == 0));
view.setLocal('odd', (record.currentIndex % 2 == 1));
private _perViewChange(view: EmbeddedViewRef<NgForRow>, record: CollectionChangeRecord) {
view.context.$implicit = record.item;
}

private _bulkRemove(tuples: RecordViewTuple[]): RecordViewTuple[] {
Expand All @@ -153,7 +162,8 @@ export class NgFor implements DoCheck {
var tuple = tuples[i];
// separate moved views from removed views.
if (isPresent(tuple.record.currentIndex)) {
tuple.view = <EmbeddedViewRef>this._viewContainer.detach(tuple.record.previousIndex);
tuple.view =
<EmbeddedViewRef<NgForRow>>this._viewContainer.detach(tuple.record.previousIndex);
movedTuples.push(tuple);
} else {
this._viewContainer.remove(tuple.record.previousIndex);
Expand All @@ -169,18 +179,18 @@ export class NgFor implements DoCheck {
if (isPresent(tuple.view)) {
this._viewContainer.insert(tuple.view, tuple.record.currentIndex);
} else {
tuple.view =
this._viewContainer.createEmbeddedView(this._templateRef, tuple.record.currentIndex);
tuple.view = this._viewContainer.createEmbeddedView(
this._templateRef, new NgForRow(null, null, null), tuple.record.currentIndex);
}
}
return tuples;
}
}

class RecordViewTuple {
view: EmbeddedViewRef;
view: EmbeddedViewRef<NgForRow>;
record: any;
constructor(record: any, view: EmbeddedViewRef) {
constructor(record: any, view: EmbeddedViewRef<NgForRow>) {
this.record = record;
this.view = view;
}
Expand Down
3 changes: 2 additions & 1 deletion modules/angular2/src/common/directives/ng_if.ts
Expand Up @@ -27,7 +27,8 @@ import {isBlank} from 'angular2/src/facade/lang';
export class NgIf {
private _prevCondition: boolean = null;

constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef) {}
constructor(private _viewContainer: ViewContainerRef, private _templateRef: TemplateRef<Object>) {
}

set ngIf(newCondition: any /* boolean */) {
if (newCondition && (isBlank(this._prevCondition) || !this._prevCondition)) {
Expand Down
2 changes: 1 addition & 1 deletion modules/angular2/src/common/directives/ng_plural.ts
Expand Up @@ -76,7 +76,7 @@ export abstract class NgLocalization { abstract getPluralCategory(value: any): s
export class NgPluralCase {
/** @internal */
_view: SwitchView;
constructor(@Attribute('ngPluralCase') public value: string, template: TemplateRef,
constructor(@Attribute('ngPluralCase') public value: string, template: TemplateRef<Object>,
viewContainer: ViewContainerRef) {
this._view = new SwitchView(viewContainer, template);
}
Expand Down
7 changes: 4 additions & 3 deletions modules/angular2/src/common/directives/ng_switch.ts
Expand Up @@ -5,7 +5,8 @@ import {ListWrapper, Map} from 'angular2/src/facade/collection';
const _WHEN_DEFAULT = CONST_EXPR(new Object());

export class SwitchView {
constructor(private _viewContainerRef: ViewContainerRef, private _templateRef: TemplateRef) {}
constructor(private _viewContainerRef: ViewContainerRef,
private _templateRef: TemplateRef<Object>) {}

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

Expand Down Expand Up @@ -175,7 +176,7 @@ export class NgSwitchWhen {
_view: SwitchView;
private _switch: NgSwitch;

constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef,
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
@Host() ngSwitch: NgSwitch) {
this._switch = ngSwitch;
this._view = new SwitchView(viewContainer, templateRef);
Expand All @@ -195,7 +196,7 @@ export class NgSwitchWhen {
*/
@Directive({selector: '[ngSwitchDefault]'})
export class NgSwitchDefault {
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef,
constructor(viewContainer: ViewContainerRef, templateRef: TemplateRef<Object>,
@Host() sswitch: NgSwitch) {
sswitch._registerView(_WHEN_DEFAULT, new SwitchView(viewContainer, templateRef));
}
Expand Down
Expand Up @@ -14,7 +14,7 @@ export class NgTemplateOutlet {
constructor(private _viewContainerRef: ViewContainerRef) {}

@Input()
set ngTemplateOutlet(templateRef: TemplateRef) {
set ngTemplateOutlet(templateRef: TemplateRef<Object>) {
if (isPresent(this._insertedViewRef)) {
this._viewContainerRef.remove(this._viewContainerRef.indexOf(this._insertedViewRef));
}
Expand Down
2 changes: 1 addition & 1 deletion modules/angular2/src/compiler/output/interpretive_view.ts
Expand Up @@ -17,7 +17,7 @@ export class InterpretiveAppViewInstanceFactory implements InstanceFactory {
class _InterpretiveAppView extends AppView<any> implements DynamicInstance {
constructor(args: any[], public props: Map<string, any>, public getters: Map<string, Function>,
public methods: Map<string, Function>) {
super(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7], args[8]);
super(args[0], args[1], args[2], args[3], args[4], args[5], args[6], args[7]);
}
createInternal(rootSelector: string | any): AppElement {
var m = this.methods.get('createInternal');
Expand Down
10 changes: 7 additions & 3 deletions modules/angular2/src/compiler/view_compiler/compile_view.ts
Expand Up @@ -65,6 +65,8 @@ export class CompileView implements NameResolver {
public literalMapCount = 0;
public pipeCount = 0;

public componentContext: o.Expression;

constructor(public component: CompileDirectiveMetadata, public genConfig: CompilerConfig,
public pipeMetas: CompilePipeMetadata[], public styles: o.Expression,
public viewIndex: number, public declarationElement: CompileElement,
Expand All @@ -90,6 +92,9 @@ export class CompileView implements NameResolver {
} else {
this.componentView = this.declarationElement.view.componentView;
}
this.componentContext =
getPropertyInView(o.THIS_EXPR.prop('context'), this, this.componentView);

var viewQueries = new CompileTokenMap<CompileQuery[]>();
if (this.viewType === ViewType.COMPONENT) {
var directiveInstance = o.THIS_EXPR.prop('context');
Expand All @@ -111,9 +116,8 @@ export class CompileView implements NameResolver {
});
}
this.viewQueries = viewQueries;
templateVariableBindings.forEach((entry) => {
this.locals.set(entry[1], o.THIS_EXPR.prop('locals').key(o.literal(entry[0])));
});
templateVariableBindings.forEach(
(entry) => { this.locals.set(entry[1], o.THIS_EXPR.prop('context').prop(entry[0])); });

if (!this.declarationElement.isNull()) {
this.declarationElement.setEmbeddedView(this);
Expand Down
3 changes: 2 additions & 1 deletion modules/angular2/src/compiler/view_compiler/event_binder.ts
Expand Up @@ -47,7 +47,8 @@ export class CompileEventListener {
this._hasComponentHostListener = true;
}
this._method.resetDebugInfo(this.compileElement.nodeIndex, hostEvent);
var context = isPresent(directiveInstance) ? directiveInstance : o.THIS_EXPR.prop('context');
var context = isPresent(directiveInstance) ? directiveInstance :
this.compileElement.view.componentContext;
var actionStmts = convertCdStatementToIr(this.compileElement.view, context, hostEvent.handler);
var lastIndex = actionStmts.length - 1;
if (lastIndex >= 0) {
Expand Down
Expand Up @@ -73,7 +73,7 @@ export function bindRenderText(boundText: BoundTextAst, compileNode: CompileNode
var valueField = createBindFieldExpr(bindingIndex);
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);

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

export function bindRenderInputs(boundProps: BoundElementPropertyAst[],
compileElement: CompileElement): void {
bindAndWriteToRenderer(boundProps, o.THIS_EXPR.prop('context'), compileElement);
bindAndWriteToRenderer(boundProps, compileElement.view.componentContext, compileElement);
}

export function bindDirectiveHostProps(directiveAst: DirectiveAst, directiveInstance: o.Expression,
Expand Down Expand Up @@ -184,7 +184,7 @@ export function bindDirectiveInputs(directiveAst: DirectiveAst, directiveInstanc
statements.push(
logBindingUpdateStmt(compileElement.renderNode, input.directiveName, currValExpr));
}
bind(view, currValExpr, fieldExpr, input.value, o.THIS_EXPR.prop('context'), statements,
bind(view, currValExpr, fieldExpr, input.value, view.componentContext, statements,
detectChangesInInputsMethod);
});
if (isOnPushComp) {
Expand Down
13 changes: 7 additions & 6 deletions modules/angular2/src/compiler/view_compiler/view_builder.ts
Expand Up @@ -246,7 +246,9 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
compileElement.contentNodesByNgContentIndex.map(nodes => createFlatArray(nodes)));
}
this.view.createMethod.addStmt(
compViewExpr.callMethod('create', [codeGenContentNodes, o.NULL_EXPR]).toStmt());
compViewExpr.callMethod('create',
[compileElement.getComponent(), codeGenContentNodes, o.NULL_EXPR])
.toStmt());
}
return null;
}
Expand Down Expand Up @@ -391,8 +393,6 @@ function createStaticNodeDebugInfo(node: CompileNode): o.Expression {

function createViewClass(view: CompileView, renderCompTypeVar: o.ReadVarExpr,
nodeDebugInfosVar: o.Expression): o.ClassStmt {
var emptyTemplateVariableBindings =
view.templateVariableBindings.map((entry) => [entry[0], o.NULL_EXPR]);
var viewConstructorArgs = [
new o.FnParam(ViewConstructorVars.viewUtils.name, o.importType(Identifiers.ViewUtils)),
new o.FnParam(ViewConstructorVars.parentInjector.name, o.importType(Identifiers.Injector)),
Expand All @@ -403,7 +403,6 @@ function createViewClass(view: CompileView, renderCompTypeVar: o.ReadVarExpr,
o.variable(view.className),
renderCompTypeVar,
ViewTypeEnum.fromValue(view.viewType),
o.literalMap(emptyTemplateVariableBindings),
ViewConstructorVars.viewUtils,
ViewConstructorVars.parentInjector,
ViewConstructorVars.declarationEl,
Expand Down Expand Up @@ -563,8 +562,10 @@ function addReturnValuefNotEmpty(statements: o.Statement[], value: o.Expression)
}

function getContextType(view: CompileView): o.Type {
var typeMeta = view.component.type;
return typeMeta.isHost ? o.DYNAMIC_TYPE : o.importType(typeMeta);
if (view.viewType === ViewType.COMPONENT) {
return o.importType(view.component.type);
}
return o.DYNAMIC_TYPE;
}

function getChangeDetectionMode(view: CompileView): ChangeDetectionStrategy {
Expand Down
8 changes: 4 additions & 4 deletions modules/angular2/src/core/debug/debug_node.ts
Expand Up @@ -26,8 +26,10 @@ export class DebugNode {
return isPresent(this._debugInfo) ? this._debugInfo.component : null;
}

get locals(): {[key: string]: any} {
return isPresent(this._debugInfo) ? this._debugInfo.locals : null;
get context(): any { return isPresent(this._debugInfo) ? this._debugInfo.context : null; }

get references(): {[key: string]: any} {
return isPresent(this._debugInfo) ? this._debugInfo.references : null;
}

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

inject(token: any): any { return this.injector.get(token); }

getLocal(name: string): any { return this.locals[name]; }
}

export class DebugElement extends DebugNode {
Expand Down
6 changes: 4 additions & 2 deletions modules/angular2/src/core/linker/component_factory.ts
@@ -1,5 +1,5 @@
import {Injector} from 'angular2/src/core/di';
import {Type, CONST, isPresent, isBlank} from 'angular2/src/facade/lang';
import {Type, CONST, CONST_EXPR, isPresent, isBlank} from 'angular2/src/facade/lang';
import {unimplemented} from 'angular2/src/facade/exceptions';
import {ElementRef} from './element_ref';
import {ViewRef, ViewRef_} from './view_ref';
Expand Down Expand Up @@ -69,6 +69,8 @@ export class ComponentRef_ extends ComponentRef {
onDestroy(callback: Function): void { this.hostView.onDestroy(callback); }
}

const EMPTY_CONTEXT = CONST_EXPR(new Object());

@CONST()
export class ComponentFactory {
constructor(public selector: string, private _viewFactory: Function,
Expand All @@ -87,7 +89,7 @@ export class ComponentFactory {
}
// Note: Host views don't need a declarationAppElement!
var hostView = this._viewFactory(vu, injector, null);
var hostElement = hostView.create(projectableNodes, rootSelectorOrNode);
var hostElement = hostView.create(EMPTY_CONTEXT, projectableNodes, rootSelectorOrNode);
return new ComponentRef_(hostElement, this._componentType);
}
}
35 changes: 14 additions & 21 deletions modules/angular2/src/core/linker/debug_context.ts
Expand Up @@ -52,28 +52,21 @@ export class DebugContext implements RenderDebugInfo {
get source(): string {
return `${this._view.componentType.templateUrl}:${this._tplRow}:${this._tplCol}`;
}
get locals(): {[key: string]: string} {
get references(): {[key: string]: any} {
var varValues: {[key: string]: string} = {};
// TODO(tbosch): right now, the semantics of debugNode.locals are
// that it contains the variables of all elements, not just
// the given one. We preserve this for now to not have a breaking
// change, but should change this later!
ListWrapper.forEachWithIndex(
this._view.staticNodeDebugInfos,
(staticNodeInfo: StaticNodeDebugInfo, nodeIndex: number) => {
var refs = staticNodeInfo.refTokens;
StringMapWrapper.forEach(refs, (refToken, refName) => {
var varValue;
if (isBlank(refToken)) {
varValue = isPresent(this._view.allNodes) ? this._view.allNodes[nodeIndex] : null;
} else {
varValue = this._view.injectorGet(refToken, nodeIndex, null);
}
varValues[refName] = varValue;
});
});
StringMapWrapper.forEach(this._view.locals,
(localValue, localName) => { varValues[localName] = localValue; });
var staticNodeInfo = this._staticNodeInfo;
if (isPresent(staticNodeInfo)) {
var refs = staticNodeInfo.refTokens;
StringMapWrapper.forEach(refs, (refToken, refName) => {
var varValue;
if (isBlank(refToken)) {
varValue = isPresent(this._view.allNodes) ? this._view.allNodes[this._nodeIndex] : null;
} else {
varValue = this._view.injectorGet(refToken, this._nodeIndex, null);
}
varValues[refName] = varValue;
});
}
return varValues;
}
}