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
This commit is contained in:
Tobias Bosch
2016-04-28 14:00:31 -07:00
parent 96ae348648
commit cacdead96d
28 changed files with 277 additions and 246 deletions

View File

@ -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
@ -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) {
@ -90,7 +102,7 @@ export class NgFor implements DoCheck {
}
}
set ngForTemplate(value: TemplateRef) {
set ngForTemplate(value: TemplateRef<NgForRow>) {
if (isPresent(value)) {
this._templateRef = value;
}
@ -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[] {
@ -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);
@ -169,8 +179,8 @@ 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;
@ -178,9 +188,9 @@ export class NgFor implements DoCheck {
}
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;
}

View File

@ -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)) {

View File

@ -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);
}

View File

@ -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); }
@ -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);
@ -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));
}

View File

@ -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));
}

View File

@ -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');

View File

@ -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,
@ -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');
@ -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);

View File

@ -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) {

View File

@ -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])
@ -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,
@ -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) {

View File

@ -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;
}
@ -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)),
@ -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,
@ -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 {

View File

@ -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[] {
@ -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 {

View File

@ -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';
@ -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,
@ -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);
}
}

View File

@ -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;
}
}

View File

@ -1,8 +1,11 @@
import {CONST_EXPR, isBlank} from 'angular2/src/facade/lang';
import {ElementRef} from './element_ref';
import {AppElement} from './element';
import {AppView} from './view';
import {EmbeddedViewRef} from './view_ref';
const EMPTY_CONTEXT = CONST_EXPR(new Object());
/**
* Represents an Embedded Template that can be used to instantiate Embedded Views.
*
@ -15,7 +18,7 @@ import {EmbeddedViewRef} from './view_ref';
* {@link ViewContainerRef#createEmbeddedView}, which will create the View and attach it to the
* View Container.
*/
export abstract class TemplateRef {
export abstract class TemplateRef<C> {
/**
* The location in the View where the Embedded View logically belongs to.
*
@ -30,16 +33,19 @@ export abstract class TemplateRef {
// TODO(i): rename to anchor or location
get elementRef(): ElementRef { return null; }
abstract createEmbeddedView(): EmbeddedViewRef;
abstract createEmbeddedView(context: C): EmbeddedViewRef<C>;
}
export class TemplateRef_ extends TemplateRef {
export class TemplateRef_<C> extends TemplateRef<C> {
constructor(private _appElement: AppElement, private _viewFactory: Function) { super(); }
createEmbeddedView(): EmbeddedViewRef {
var view: AppView<any> = this._viewFactory(this._appElement.parentView.viewUtils,
this._appElement.parentInjector, this._appElement);
view.create(null, null);
createEmbeddedView(context: C): EmbeddedViewRef<C> {
var view: AppView<C> = this._viewFactory(this._appElement.parentView.viewUtils,
this._appElement.parentInjector, this._appElement);
if (isBlank(context)) {
context = <any>EMPTY_CONTEXT;
}
view.create(context, null, null);
return view.ref;
}

View File

@ -51,8 +51,6 @@ import {
import {StaticNodeDebugInfo, DebugContext} from './debug_context';
import {ElementInjector} from './element_injector';
const EMPTY_CONTEXT = CONST_EXPR(new Object());
var _scope_check: WtfScopeFn = wtfCreateScope(`AppView#check(ascii id)`);
/**
@ -60,7 +58,7 @@ var _scope_check: WtfScopeFn = wtfCreateScope(`AppView#check(ascii id)`);
*
*/
export abstract class AppView<T> {
ref: ViewRef_;
ref: ViewRef_<T>;
rootNodesOrAppElements: any[];
allNodes: any[];
disposables: Function[];
@ -74,12 +72,6 @@ export abstract class AppView<T> {
// change detection will fail.
cdState: ChangeDetectorState = ChangeDetectorState.NeverChecked;
/**
* The context against which data-binding expressions in this view are evaluated against.
* This is always a component instance.
*/
context: T = null;
projectableNodes: Array<any | any[]>;
destroyed: boolean = false;
@ -90,10 +82,11 @@ export abstract class AppView<T> {
private _hasExternalHostElement: boolean;
public context: T;
constructor(public clazz: any, public componentType: RenderComponentType, public type: ViewType,
public locals: {[key: string]: any}, public viewUtils: ViewUtils,
public parentInjector: Injector, public declarationAppElement: AppElement,
public cdMode: ChangeDetectionStrategy,
public viewUtils: ViewUtils, public parentInjector: Injector,
public declarationAppElement: AppElement, public cdMode: ChangeDetectionStrategy,
public staticNodeDebugInfos: StaticNodeDebugInfo[]) {
this.ref = new ViewRef_(this);
if (type === ViewType.COMPONENT || type === ViewType.HOST) {
@ -103,27 +96,24 @@ export abstract class AppView<T> {
}
}
create(givenProjectableNodes: Array<any | any[]>, rootSelectorOrNode: string | any): AppElement {
var context;
create(context: T, givenProjectableNodes: Array<any | any[]>,
rootSelectorOrNode: string | any): AppElement {
this.context = context;
var projectableNodes;
switch (this.type) {
case ViewType.COMPONENT:
context = this.declarationAppElement.component;
projectableNodes = ensureSlotCount(givenProjectableNodes, this.componentType.slotCount);
break;
case ViewType.EMBEDDED:
context = this.declarationAppElement.parentView.context;
projectableNodes = this.declarationAppElement.parentView.projectableNodes;
break;
case ViewType.HOST:
context = EMPTY_CONTEXT;
// Note: Don't ensure the slot count for the projectableNodes as we store
// them only for the contained component view (which will later check the slot count...)
projectableNodes = givenProjectableNodes;
break;
}
this._hasExternalHostElement = isPresent(rootSelectorOrNode);
this.context = context;
this.projectableNodes = projectableNodes;
if (this.debugMode) {
this._resetDebug();
@ -277,12 +267,6 @@ export abstract class AppView<T> {
return _findLastRenderNode(lastNode);
}
hasLocal(contextName: string): boolean {
return StringMapWrapper.contains(this.locals, contextName);
}
setLocal(contextName: string, value: any): void { this.locals[contextName] = value; }
/**
* Overwritten by implementations
*/

View File

@ -62,7 +62,9 @@ export abstract class ViewContainerRef {
*
* Returns the {@link ViewRef} for the newly created View.
*/
abstract createEmbeddedView(templateRef: TemplateRef, index?: number): EmbeddedViewRef;
// TODO(tbosch): Use a generic once ts2dart supports it.
abstract createEmbeddedView(templateRef: TemplateRef<any>, context?: any,
index?: number): EmbeddedViewRef<any>;
/**
* Instantiates a single {@link Component} and inserts its Host View into this container at the
@ -113,7 +115,7 @@ export abstract class ViewContainerRef {
export class ViewContainerRef_ implements ViewContainerRef {
constructor(private _element: AppElement) {}
get(index: number): EmbeddedViewRef { return this._element.nestedViews[index].ref; }
get(index: number): ViewRef { return this._element.nestedViews[index].ref; }
get length(): number {
var views = this._element.nestedViews;
return isPresent(views) ? views.length : 0;
@ -127,8 +129,10 @@ export class ViewContainerRef_ implements ViewContainerRef {
// TODO(rado): profile and decide whether bounds checks should be added
// to the methods below.
createEmbeddedView(templateRef: TemplateRef, index: number = -1): EmbeddedViewRef {
var viewRef: EmbeddedViewRef = templateRef.createEmbeddedView();
// TODO(tbosch): use a generic C once ts2dart supports it.
createEmbeddedView(templateRef: TemplateRef<any>, context: any = null,
index: number = -1): EmbeddedViewRef<any> {
var viewRef: EmbeddedViewRef<any> = templateRef.createEmbeddedView(context);
this.insert(viewRef, index);
return viewRef;
}
@ -153,13 +157,13 @@ export class ViewContainerRef_ implements ViewContainerRef {
insert(viewRef: ViewRef, index: number = -1): ViewRef {
var s = this._insertScope();
if (index == -1) index = this.length;
var viewRef_ = <ViewRef_>viewRef;
var viewRef_ = <ViewRef_<any>>viewRef;
this._element.attachView(viewRef_.internalView, index);
return wtfLeave(s, viewRef_);
}
indexOf(viewRef: ViewRef): number {
return ListWrapper.indexOf(this._element.nestedViews, (<ViewRef_>viewRef).internalView);
return ListWrapper.indexOf(this._element.nestedViews, (<ViewRef_<any>>viewRef).internalView);
}
/** @internal */

View File

@ -38,9 +38,9 @@ export abstract class ViewRef extends ChangeDetectorRef {
* </ul>
* ```
*
* ... we have two {@link ProtoViewRef}s:
* ... we have two {@link TemplateRef}s:
*
* Outer {@link ProtoViewRef}:
* Outer {@link TemplateRef}:
* ```
* Count: {{items.length}}
* <ul>
@ -48,14 +48,14 @@ export abstract class ViewRef extends ChangeDetectorRef {
* </ul>
* ```
*
* Inner {@link ProtoViewRef}:
* Inner {@link TemplateRef}:
* ```
* <li>{{item}}</li>
* ```
*
* Notice that the original template is broken down into two separate {@link ProtoViewRef}s.
* Notice that the original template is broken down into two separate {@link TemplateRef}s.
*
* The outer/inner {@link ProtoViewRef}s are then assembled into views like so:
* The outer/inner {@link TemplateRef}s are then assembled into views like so:
*
* ```
* <!-- ViewRef: outer-0 -->
@ -68,16 +68,8 @@ export abstract class ViewRef extends ChangeDetectorRef {
* <!-- /ViewRef: outer-0 -->
* ```
*/
export abstract class EmbeddedViewRef extends ViewRef {
/**
* Sets `value` of local variable called `variableName` in this View.
*/
abstract setLocal(variableName: string, value: any): void;
/**
* Checks whether this view has a local variable called `variableName`.
*/
abstract hasLocal(variableName: string): boolean;
export abstract class EmbeddedViewRef<C> extends ViewRef {
get context(): C { return unimplemented(); }
get rootNodes(): any[] { return <any[]>unimplemented(); };
@ -87,10 +79,10 @@ export abstract class EmbeddedViewRef extends ViewRef {
abstract destroy();
}
export class ViewRef_ implements EmbeddedViewRef {
constructor(private _view: AppView<any>) { this._view = _view; }
export class ViewRef_<C> implements EmbeddedViewRef<C> {
constructor(private _view: AppView<C>) { this._view = _view; }
get internalView(): AppView<any> { return this._view; }
get internalView(): AppView<C> { return this._view; }
/**
* Return `ChangeDetectorRef`
@ -99,9 +91,7 @@ export class ViewRef_ implements EmbeddedViewRef {
get rootNodes(): any[] { return this._view.flatRootNodes; }
setLocal(variableName: string, value: any): void { this._view.setLocal(variableName, value); }
hasLocal(variableName: string): boolean { return this._view.hasLocal(variableName); }
get context() { return this._view.context; }
get destroyed(): boolean { return this._view.destroyed; }

View File

@ -8,10 +8,28 @@ import 'package:angular2/src/facade/lang.dart';
import 'platform_reflection_capabilities.dart';
import 'types.dart';
import '../linker/template_ref.dart';
var DOT_REGEX = new RegExp('\\.');
class ReflectionCapabilities implements PlatformReflectionCapabilities {
ReflectionCapabilities([metadataReader]) {}
Map<Symbol, Type> parameterizedTypeMapping = new Map<Symbol, Type>();
ReflectionCapabilities([metadataReader]) {
// In Dart, there is no way of getting from a parameterized Type to
// the underlying non parameterized type.
// So we need to have a separate Map for the types that are generic
// and used in our DI...
parameterizedTypeMapping[reflectType(TemplateRef).qualifiedName] = TemplateRef;
}
_typeFromMirror(TypeMirror typeMirror) {
var result = parameterizedTypeMapping[typeMirror.qualifiedName];
if (result == null && typeMirror.hasReflectedType && typeMirror.reflectedType != dynamic) {
result = typeMirror.reflectedType;
}
return result;
}
bool isReflectionEnabled() {
return true;
@ -245,9 +263,8 @@ class ReflectionCapabilities implements PlatformReflectionCapabilities {
List _convertParameter(ParameterMirror p) {
var t = p.type;
var res = (!t.hasReflectedType || t.reflectedType == dynamic)
? <Object>[]
: <Object>[t.reflectedType];
var type = _typeFromMirror(t);
var res = type != null ? [type] : [];
res.addAll(p.metadata.map((m) => m.reflectee));
return res;
}

View File

@ -11,7 +11,8 @@ export abstract class RenderDebugInfo {
get injector(): Injector { return unimplemented(); }
get component(): any { return unimplemented(); }
get providerTokens(): any[] { return unimplemented(); }
get locals(): {[key: string]: string} { return unimplemented(); }
get references(): {[key: string]: any} { return unimplemented(); }
get context(): any { return unimplemented(); }
get source(): string { return unimplemented(); }
}