repackaging: all the file moves
This commit is contained in:
@ -0,0 +1,6 @@
|
||||
import {CompileNode} from './compile_element';
|
||||
import {TemplateAst} from '../template_ast';
|
||||
|
||||
export class CompileBinding {
|
||||
constructor(public node: CompileNode, public sourceAst: TemplateAst) {}
|
||||
}
|
427
modules/@angular/compiler/src/view_compiler/compile_element.ts
Normal file
427
modules/@angular/compiler/src/view_compiler/compile_element.ts
Normal file
@ -0,0 +1,427 @@
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import * as o from '../output/output_ast';
|
||||
import {Identifiers, identifierToken} from '../identifiers';
|
||||
import {InjectMethodVars} from './constants';
|
||||
import {CompileView} from './compile_view';
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {TemplateAst, ProviderAst, ProviderAstType, ReferenceAst} from '../template_ast';
|
||||
import {
|
||||
CompileTokenMap,
|
||||
CompileDirectiveMetadata,
|
||||
CompileTokenMetadata,
|
||||
CompileQueryMetadata,
|
||||
CompileProviderMetadata,
|
||||
CompileDiDependencyMetadata,
|
||||
CompileIdentifierMetadata,
|
||||
CompileTypeMetadata,
|
||||
} from '../compile_metadata';
|
||||
import {getPropertyInView, createDiTokenExpression, injectFromViewParentInjector} from './util';
|
||||
import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query';
|
||||
import {CompileMethod} from './compile_method';
|
||||
import {ValueTransformer, visitValue} from '../util';
|
||||
|
||||
export class CompileNode {
|
||||
constructor(public parent: CompileElement, public view: CompileView, public nodeIndex: number,
|
||||
public renderNode: o.Expression, public sourceAst: TemplateAst) {}
|
||||
|
||||
isNull(): boolean { return isBlank(this.renderNode); }
|
||||
|
||||
isRootElement(): boolean { return this.view != this.parent.view; }
|
||||
}
|
||||
|
||||
export class CompileElement extends CompileNode {
|
||||
static createNull(): CompileElement {
|
||||
return new CompileElement(null, null, null, null, null, null, [], [], false, false, []);
|
||||
}
|
||||
|
||||
private _compViewExpr: o.Expression = null;
|
||||
public appElement: o.ReadPropExpr;
|
||||
public elementRef: o.Expression;
|
||||
public injector: o.Expression;
|
||||
private _instances = new CompileTokenMap<o.Expression>();
|
||||
private _resolvedProviders: CompileTokenMap<ProviderAst>;
|
||||
|
||||
private _queryCount = 0;
|
||||
private _queries = new CompileTokenMap<CompileQuery[]>();
|
||||
private _componentConstructorViewQueryLists: o.Expression[] = [];
|
||||
|
||||
public contentNodesByNgContentIndex: Array<o.Expression>[] = null;
|
||||
public embeddedView: CompileView;
|
||||
public directiveInstances: o.Expression[];
|
||||
public referenceTokens: {[key: string]: CompileTokenMetadata};
|
||||
|
||||
constructor(parent: CompileElement, view: CompileView, nodeIndex: number,
|
||||
renderNode: o.Expression, sourceAst: TemplateAst,
|
||||
public component: CompileDirectiveMetadata,
|
||||
private _directives: CompileDirectiveMetadata[],
|
||||
private _resolvedProvidersArray: ProviderAst[], public hasViewContainer: boolean,
|
||||
public hasEmbeddedView: boolean, references: ReferenceAst[]) {
|
||||
super(parent, view, nodeIndex, renderNode, sourceAst);
|
||||
this.referenceTokens = {};
|
||||
references.forEach(ref => this.referenceTokens[ref.name] = ref.value);
|
||||
|
||||
this.elementRef = o.importExpr(Identifiers.ElementRef).instantiate([this.renderNode]);
|
||||
this._instances.add(identifierToken(Identifiers.ElementRef), this.elementRef);
|
||||
this.injector = o.THIS_EXPR.callMethod('injector', [o.literal(this.nodeIndex)]);
|
||||
this._instances.add(identifierToken(Identifiers.Injector), this.injector);
|
||||
this._instances.add(identifierToken(Identifiers.Renderer), o.THIS_EXPR.prop('renderer'));
|
||||
if (this.hasViewContainer || this.hasEmbeddedView || isPresent(this.component)) {
|
||||
this._createAppElement();
|
||||
}
|
||||
}
|
||||
|
||||
private _createAppElement() {
|
||||
var fieldName = `_appEl_${this.nodeIndex}`;
|
||||
var parentNodeIndex = this.isRootElement() ? null : this.parent.nodeIndex;
|
||||
// private is fine here as no child view will reference an AppElement
|
||||
this.view.fields.push(new o.ClassField(fieldName, o.importType(Identifiers.AppElement),
|
||||
[o.StmtModifier.Private]));
|
||||
var statement = o.THIS_EXPR.prop(fieldName)
|
||||
.set(o.importExpr(Identifiers.AppElement)
|
||||
.instantiate([
|
||||
o.literal(this.nodeIndex),
|
||||
o.literal(parentNodeIndex),
|
||||
o.THIS_EXPR,
|
||||
this.renderNode
|
||||
]))
|
||||
.toStmt();
|
||||
this.view.createMethod.addStmt(statement);
|
||||
this.appElement = o.THIS_EXPR.prop(fieldName);
|
||||
this._instances.add(identifierToken(Identifiers.AppElement), this.appElement);
|
||||
}
|
||||
|
||||
setComponentView(compViewExpr: o.Expression) {
|
||||
this._compViewExpr = compViewExpr;
|
||||
this.contentNodesByNgContentIndex =
|
||||
ListWrapper.createFixedSize(this.component.template.ngContentSelectors.length);
|
||||
for (var i = 0; i < this.contentNodesByNgContentIndex.length; i++) {
|
||||
this.contentNodesByNgContentIndex[i] = [];
|
||||
}
|
||||
}
|
||||
|
||||
setEmbeddedView(embeddedView: CompileView) {
|
||||
this.embeddedView = embeddedView;
|
||||
if (isPresent(embeddedView)) {
|
||||
var createTemplateRefExpr =
|
||||
o.importExpr(Identifiers.TemplateRef_)
|
||||
.instantiate([this.appElement, this.embeddedView.viewFactory]);
|
||||
var provider = new CompileProviderMetadata(
|
||||
{token: identifierToken(Identifiers.TemplateRef), useValue: createTemplateRefExpr});
|
||||
// Add TemplateRef as first provider as it does not have deps on other providers
|
||||
this._resolvedProvidersArray.unshift(new ProviderAst(provider.token, false, true, [provider],
|
||||
ProviderAstType.Builtin,
|
||||
this.sourceAst.sourceSpan));
|
||||
}
|
||||
}
|
||||
|
||||
beforeChildren(): void {
|
||||
if (this.hasViewContainer) {
|
||||
this._instances.add(identifierToken(Identifiers.ViewContainerRef),
|
||||
this.appElement.prop('vcRef'));
|
||||
}
|
||||
|
||||
this._resolvedProviders = new CompileTokenMap<ProviderAst>();
|
||||
this._resolvedProvidersArray.forEach(provider =>
|
||||
this._resolvedProviders.add(provider.token, provider));
|
||||
|
||||
// create all the provider instances, some in the view constructor,
|
||||
// some as getters. We rely on the fact that they are already sorted topologically.
|
||||
this._resolvedProviders.values().forEach((resolvedProvider) => {
|
||||
var providerValueExpressions = resolvedProvider.providers.map((provider) => {
|
||||
if (isPresent(provider.useExisting)) {
|
||||
return this._getDependency(
|
||||
resolvedProvider.providerType,
|
||||
new CompileDiDependencyMetadata({token: provider.useExisting}));
|
||||
} else if (isPresent(provider.useFactory)) {
|
||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useFactory.diDeps;
|
||||
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
||||
return o.importExpr(provider.useFactory).callFn(depsExpr);
|
||||
} else if (isPresent(provider.useClass)) {
|
||||
var deps = isPresent(provider.deps) ? provider.deps : provider.useClass.diDeps;
|
||||
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
||||
return o.importExpr(provider.useClass)
|
||||
.instantiate(depsExpr, o.importType(provider.useClass));
|
||||
} else {
|
||||
return _convertValueToOutputAst(provider.useValue);
|
||||
}
|
||||
});
|
||||
var propName = `_${resolvedProvider.token.name}_${this.nodeIndex}_${this._instances.size}`;
|
||||
var instance =
|
||||
createProviderProperty(propName, resolvedProvider, providerValueExpressions,
|
||||
resolvedProvider.multiProvider, resolvedProvider.eager, this);
|
||||
this._instances.add(resolvedProvider.token, instance);
|
||||
});
|
||||
|
||||
this.directiveInstances =
|
||||
this._directives.map((directive) => this._instances.get(identifierToken(directive.type)));
|
||||
for (var i = 0; i < this.directiveInstances.length; i++) {
|
||||
var directiveInstance = this.directiveInstances[i];
|
||||
var directive = this._directives[i];
|
||||
directive.queries.forEach((queryMeta) => { this._addQuery(queryMeta, directiveInstance); });
|
||||
}
|
||||
var queriesWithReads: _QueryWithRead[] = [];
|
||||
this._resolvedProviders.values().forEach((resolvedProvider) => {
|
||||
var queriesForProvider = this._getQueriesFor(resolvedProvider.token);
|
||||
ListWrapper.addAll(
|
||||
queriesWithReads,
|
||||
queriesForProvider.map(query => new _QueryWithRead(query, resolvedProvider.token)));
|
||||
});
|
||||
StringMapWrapper.forEach(this.referenceTokens, (_, varName) => {
|
||||
var token = this.referenceTokens[varName];
|
||||
var varValue;
|
||||
if (isPresent(token)) {
|
||||
varValue = this._instances.get(token);
|
||||
} else {
|
||||
varValue = this.renderNode;
|
||||
}
|
||||
this.view.locals.set(varName, varValue);
|
||||
var varToken = new CompileTokenMetadata({value: varName});
|
||||
ListWrapper.addAll(queriesWithReads, this._getQueriesFor(varToken)
|
||||
.map(query => new _QueryWithRead(query, varToken)));
|
||||
});
|
||||
queriesWithReads.forEach((queryWithRead) => {
|
||||
var value: o.Expression;
|
||||
if (isPresent(queryWithRead.read.identifier)) {
|
||||
// query for an identifier
|
||||
value = this._instances.get(queryWithRead.read);
|
||||
} else {
|
||||
// query for a reference
|
||||
var token = this.referenceTokens[queryWithRead.read.value];
|
||||
if (isPresent(token)) {
|
||||
value = this._instances.get(token);
|
||||
} else {
|
||||
value = this.elementRef;
|
||||
}
|
||||
}
|
||||
if (isPresent(value)) {
|
||||
queryWithRead.query.addValue(value, this.view);
|
||||
}
|
||||
});
|
||||
|
||||
if (isPresent(this.component)) {
|
||||
var componentConstructorViewQueryList =
|
||||
isPresent(this.component) ? o.literalArr(this._componentConstructorViewQueryLists) :
|
||||
o.NULL_EXPR;
|
||||
var compExpr = isPresent(this.getComponent()) ? this.getComponent() : o.NULL_EXPR;
|
||||
this.view.createMethod.addStmt(
|
||||
this.appElement.callMethod(
|
||||
'initComponent',
|
||||
[compExpr, componentConstructorViewQueryList, this._compViewExpr])
|
||||
.toStmt());
|
||||
}
|
||||
}
|
||||
|
||||
afterChildren(childNodeCount: number) {
|
||||
this._resolvedProviders.values().forEach((resolvedProvider) => {
|
||||
// Note: afterChildren is called after recursing into children.
|
||||
// This is good so that an injector match in an element that is closer to a requesting element
|
||||
// matches first.
|
||||
var providerExpr = this._instances.get(resolvedProvider.token);
|
||||
// Note: view providers are only visible on the injector of that element.
|
||||
// This is not fully correct as the rules during codegen don't allow a directive
|
||||
// to get hold of a view provdier on the same element. We still do this semantic
|
||||
// as it simplifies our model to having only one runtime injector per element.
|
||||
var providerChildNodeCount =
|
||||
resolvedProvider.providerType === ProviderAstType.PrivateService ? 0 : childNodeCount;
|
||||
this.view.injectorGetMethod.addStmt(createInjectInternalCondition(
|
||||
this.nodeIndex, providerChildNodeCount, resolvedProvider, providerExpr));
|
||||
});
|
||||
|
||||
this._queries.values().forEach(
|
||||
(queries) =>
|
||||
queries.forEach((query) => query.afterChildren(this.view.updateContentQueriesMethod)));
|
||||
}
|
||||
|
||||
addContentNode(ngContentIndex: number, nodeExpr: o.Expression) {
|
||||
this.contentNodesByNgContentIndex[ngContentIndex].push(nodeExpr);
|
||||
}
|
||||
|
||||
getComponent(): o.Expression {
|
||||
return isPresent(this.component) ? this._instances.get(identifierToken(this.component.type)) :
|
||||
null;
|
||||
}
|
||||
|
||||
getProviderTokens(): o.Expression[] {
|
||||
return this._resolvedProviders.values().map(
|
||||
(resolvedProvider) => createDiTokenExpression(resolvedProvider.token));
|
||||
}
|
||||
|
||||
private _getQueriesFor(token: CompileTokenMetadata): CompileQuery[] {
|
||||
var result: CompileQuery[] = [];
|
||||
var currentEl: CompileElement = this;
|
||||
var distance = 0;
|
||||
var queries: CompileQuery[];
|
||||
while (!currentEl.isNull()) {
|
||||
queries = currentEl._queries.get(token);
|
||||
if (isPresent(queries)) {
|
||||
ListWrapper.addAll(result,
|
||||
queries.filter((query) => query.meta.descendants || distance <= 1));
|
||||
}
|
||||
if (currentEl._directives.length > 0) {
|
||||
distance++;
|
||||
}
|
||||
currentEl = currentEl.parent;
|
||||
}
|
||||
queries = this.view.componentView.viewQueries.get(token);
|
||||
if (isPresent(queries)) {
|
||||
ListWrapper.addAll(result, queries);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _addQuery(queryMeta: CompileQueryMetadata,
|
||||
directiveInstance: o.Expression): CompileQuery {
|
||||
var propName = `_query_${queryMeta.selectors[0].name}_${this.nodeIndex}_${this._queryCount++}`;
|
||||
var queryList = createQueryList(queryMeta, directiveInstance, propName, this.view);
|
||||
var query = new CompileQuery(queryMeta, queryList, directiveInstance, this.view);
|
||||
addQueryToTokenMap(this._queries, query);
|
||||
return query;
|
||||
}
|
||||
|
||||
private _getLocalDependency(requestingProviderType: ProviderAstType,
|
||||
dep: CompileDiDependencyMetadata): o.Expression {
|
||||
var result = null;
|
||||
// constructor content query
|
||||
if (isBlank(result) && isPresent(dep.query)) {
|
||||
result = this._addQuery(dep.query, null).queryList;
|
||||
}
|
||||
|
||||
// constructor view query
|
||||
if (isBlank(result) && isPresent(dep.viewQuery)) {
|
||||
result = createQueryList(
|
||||
dep.viewQuery, null,
|
||||
`_viewQuery_${dep.viewQuery.selectors[0].name}_${this.nodeIndex}_${this._componentConstructorViewQueryLists.length}`,
|
||||
this.view);
|
||||
this._componentConstructorViewQueryLists.push(result);
|
||||
}
|
||||
|
||||
if (isPresent(dep.token)) {
|
||||
// access builtins with special visibility
|
||||
if (isBlank(result)) {
|
||||
if (dep.token.equalsTo(identifierToken(Identifiers.ChangeDetectorRef))) {
|
||||
if (requestingProviderType === ProviderAstType.Component) {
|
||||
return this._compViewExpr.prop('ref');
|
||||
} else {
|
||||
return getPropertyInView(o.THIS_EXPR.prop('ref'), this.view, this.view.componentView);
|
||||
}
|
||||
}
|
||||
}
|
||||
// access regular providers on the element
|
||||
if (isBlank(result)) {
|
||||
result = this._instances.get(dep.token);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _getDependency(requestingProviderType: ProviderAstType,
|
||||
dep: CompileDiDependencyMetadata): o.Expression {
|
||||
var currElement: CompileElement = this;
|
||||
var result = null;
|
||||
if (dep.isValue) {
|
||||
result = o.literal(dep.value);
|
||||
}
|
||||
if (isBlank(result) && !dep.isSkipSelf) {
|
||||
result = this._getLocalDependency(requestingProviderType, dep);
|
||||
}
|
||||
// check parent elements
|
||||
while (isBlank(result) && !currElement.parent.isNull()) {
|
||||
currElement = currElement.parent;
|
||||
result = currElement._getLocalDependency(ProviderAstType.PublicService,
|
||||
new CompileDiDependencyMetadata({token: dep.token}));
|
||||
}
|
||||
|
||||
if (isBlank(result)) {
|
||||
result = injectFromViewParentInjector(dep.token, dep.isOptional);
|
||||
}
|
||||
if (isBlank(result)) {
|
||||
result = o.NULL_EXPR;
|
||||
}
|
||||
return getPropertyInView(result, this.view, currElement.view);
|
||||
}
|
||||
}
|
||||
|
||||
function createInjectInternalCondition(nodeIndex: number, childNodeCount: number,
|
||||
provider: ProviderAst,
|
||||
providerExpr: o.Expression): o.Statement {
|
||||
var indexCondition;
|
||||
if (childNodeCount > 0) {
|
||||
indexCondition = o.literal(nodeIndex)
|
||||
.lowerEquals(InjectMethodVars.requestNodeIndex)
|
||||
.and(InjectMethodVars.requestNodeIndex.lowerEquals(
|
||||
o.literal(nodeIndex + childNodeCount)));
|
||||
} else {
|
||||
indexCondition = o.literal(nodeIndex).identical(InjectMethodVars.requestNodeIndex);
|
||||
}
|
||||
return new o.IfStmt(
|
||||
InjectMethodVars.token.identical(createDiTokenExpression(provider.token)).and(indexCondition),
|
||||
[new o.ReturnStatement(providerExpr)]);
|
||||
}
|
||||
|
||||
function createProviderProperty(propName: string, provider: ProviderAst,
|
||||
providerValueExpressions: o.Expression[], isMulti: boolean,
|
||||
isEager: boolean, compileElement: CompileElement): o.Expression {
|
||||
var view = compileElement.view;
|
||||
var resolvedProviderValueExpr;
|
||||
var type;
|
||||
if (isMulti) {
|
||||
resolvedProviderValueExpr = o.literalArr(providerValueExpressions);
|
||||
type = new o.ArrayType(o.DYNAMIC_TYPE);
|
||||
} else {
|
||||
resolvedProviderValueExpr = providerValueExpressions[0];
|
||||
type = providerValueExpressions[0].type;
|
||||
}
|
||||
if (isBlank(type)) {
|
||||
type = o.DYNAMIC_TYPE;
|
||||
}
|
||||
if (isEager) {
|
||||
view.fields.push(new o.ClassField(propName, type));
|
||||
view.createMethod.addStmt(o.THIS_EXPR.prop(propName).set(resolvedProviderValueExpr).toStmt());
|
||||
} else {
|
||||
var internalField = `_${propName}`;
|
||||
view.fields.push(new o.ClassField(internalField, type));
|
||||
var getter = new CompileMethod(view);
|
||||
getter.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||
// Note: Equals is important for JS so that it also checks the undefined case!
|
||||
getter.addStmt(
|
||||
new o.IfStmt(o.THIS_EXPR.prop(internalField).isBlank(),
|
||||
[o.THIS_EXPR.prop(internalField).set(resolvedProviderValueExpr).toStmt()]));
|
||||
getter.addStmt(new o.ReturnStatement(o.THIS_EXPR.prop(internalField)));
|
||||
view.getters.push(new o.ClassGetter(propName, getter.finish(), type));
|
||||
}
|
||||
return o.THIS_EXPR.prop(propName);
|
||||
}
|
||||
|
||||
class _QueryWithRead {
|
||||
public read: CompileTokenMetadata;
|
||||
constructor(public query: CompileQuery, match: CompileTokenMetadata) {
|
||||
this.read = isPresent(query.meta.read) ? query.meta.read : match;
|
||||
}
|
||||
}
|
||||
|
||||
function _convertValueToOutputAst(value: any): o.Expression {
|
||||
return visitValue(value, new _ValueOutputAstTransformer(), null);
|
||||
}
|
||||
|
||||
class _ValueOutputAstTransformer extends ValueTransformer {
|
||||
visitArray(arr: any[], context: any): o.Expression {
|
||||
return o.literalArr(arr.map(value => visitValue(value, this, context)));
|
||||
}
|
||||
visitStringMap(map: {[key: string]: any}, context: any): o.Expression {
|
||||
var entries = [];
|
||||
StringMapWrapper.forEach(
|
||||
map, (value, key) => { entries.push([key, visitValue(value, this, context)]); });
|
||||
return o.literalMap(entries);
|
||||
}
|
||||
visitPrimitive(value: any, context: any): o.Expression { return o.literal(value); }
|
||||
visitOther(value: any, context: any): o.Expression {
|
||||
if (value instanceof CompileIdentifierMetadata) {
|
||||
return o.importExpr(value);
|
||||
} else if (value instanceof o.Expression) {
|
||||
return value;
|
||||
} else {
|
||||
throw new BaseException(`Illegal state: Don't now how to compile value ${value}`);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,75 @@
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {MapWrapper, ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import * as o from '../output/output_ast';
|
||||
import {TemplateAst} from '../template_ast';
|
||||
|
||||
import {CompileView} from './compile_view';
|
||||
|
||||
class _DebugState {
|
||||
constructor(public nodeIndex: number, public sourceAst: TemplateAst) {}
|
||||
}
|
||||
|
||||
var NULL_DEBUG_STATE = new _DebugState(null, null);
|
||||
|
||||
export class CompileMethod {
|
||||
private _newState: _DebugState = NULL_DEBUG_STATE;
|
||||
private _currState: _DebugState = NULL_DEBUG_STATE;
|
||||
|
||||
private _debugEnabled: boolean;
|
||||
|
||||
private _bodyStatements: o.Statement[] = [];
|
||||
|
||||
constructor(private _view: CompileView) {
|
||||
this._debugEnabled = this._view.genConfig.genDebugInfo;
|
||||
}
|
||||
|
||||
private _updateDebugContextIfNeeded() {
|
||||
if (this._newState.nodeIndex !== this._currState.nodeIndex ||
|
||||
this._newState.sourceAst !== this._currState.sourceAst) {
|
||||
var expr = this._updateDebugContext(this._newState);
|
||||
if (isPresent(expr)) {
|
||||
this._bodyStatements.push(expr.toStmt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _updateDebugContext(newState: _DebugState): o.Expression {
|
||||
this._currState = this._newState = newState;
|
||||
if (this._debugEnabled) {
|
||||
var sourceLocation =
|
||||
isPresent(newState.sourceAst) ? newState.sourceAst.sourceSpan.start : null;
|
||||
|
||||
return o.THIS_EXPR.callMethod('debug', [
|
||||
o.literal(newState.nodeIndex),
|
||||
isPresent(sourceLocation) ? o.literal(sourceLocation.line) : o.NULL_EXPR,
|
||||
isPresent(sourceLocation) ? o.literal(sourceLocation.col) : o.NULL_EXPR
|
||||
]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
resetDebugInfoExpr(nodeIndex: number, templateAst: TemplateAst): o.Expression {
|
||||
var res = this._updateDebugContext(new _DebugState(nodeIndex, templateAst));
|
||||
return isPresent(res) ? res : o.NULL_EXPR;
|
||||
}
|
||||
|
||||
resetDebugInfo(nodeIndex: number, templateAst: TemplateAst) {
|
||||
this._newState = new _DebugState(nodeIndex, templateAst);
|
||||
}
|
||||
|
||||
addStmt(stmt: o.Statement) {
|
||||
this._updateDebugContextIfNeeded();
|
||||
this._bodyStatements.push(stmt);
|
||||
}
|
||||
|
||||
addStmts(stmts: o.Statement[]) {
|
||||
this._updateDebugContextIfNeeded();
|
||||
ListWrapper.addAll(this._bodyStatements, stmts);
|
||||
}
|
||||
|
||||
finish(): o.Statement[] { return this._bodyStatements; }
|
||||
|
||||
isEmpty(): boolean { return this._bodyStatements.length === 0; }
|
||||
}
|
75
modules/@angular/compiler/src/view_compiler/compile_pipe.ts
Normal file
75
modules/@angular/compiler/src/view_compiler/compile_pipe.ts
Normal file
@ -0,0 +1,75 @@
|
||||
import {isBlank, isPresent} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import * as o from '../output/output_ast';
|
||||
import {CompileView} from './compile_view';
|
||||
import {CompilePipeMetadata} from '../compile_metadata';
|
||||
import {Identifiers, identifierToken} from '../identifiers';
|
||||
import {injectFromViewParentInjector, createPureProxy, getPropertyInView} from './util';
|
||||
|
||||
class _PurePipeProxy {
|
||||
constructor(public instance: o.ReadPropExpr, public argCount: number) {}
|
||||
}
|
||||
|
||||
export class CompilePipe {
|
||||
meta: CompilePipeMetadata;
|
||||
instance: o.ReadPropExpr;
|
||||
private _purePipeProxies: _PurePipeProxy[] = [];
|
||||
|
||||
constructor(public view: CompileView, name: string) {
|
||||
this.meta = _findPipeMeta(view, name);
|
||||
this.instance = o.THIS_EXPR.prop(`_pipe_${name}_${view.pipeCount++}`);
|
||||
}
|
||||
|
||||
get pure(): boolean { return this.meta.pure; }
|
||||
|
||||
create(): void {
|
||||
var deps = this.meta.type.diDeps.map((diDep) => {
|
||||
if (diDep.token.equalsTo(identifierToken(Identifiers.ChangeDetectorRef))) {
|
||||
return getPropertyInView(o.THIS_EXPR.prop('ref'), this.view, this.view.componentView);
|
||||
}
|
||||
return injectFromViewParentInjector(diDep.token, false);
|
||||
});
|
||||
this.view.fields.push(new o.ClassField(this.instance.name, o.importType(this.meta.type)));
|
||||
this.view.createMethod.resetDebugInfo(null, null);
|
||||
this.view.createMethod.addStmt(o.THIS_EXPR.prop(this.instance.name)
|
||||
.set(o.importExpr(this.meta.type).instantiate(deps))
|
||||
.toStmt());
|
||||
this._purePipeProxies.forEach((purePipeProxy) => {
|
||||
createPureProxy(
|
||||
this.instance.prop('transform').callMethod(o.BuiltinMethod.bind, [this.instance]),
|
||||
purePipeProxy.argCount, purePipeProxy.instance, this.view);
|
||||
});
|
||||
}
|
||||
|
||||
call(callingView: CompileView, args: o.Expression[]): o.Expression {
|
||||
if (this.meta.pure) {
|
||||
var purePipeProxy = new _PurePipeProxy(
|
||||
o.THIS_EXPR.prop(`${this.instance.name}_${this._purePipeProxies.length}`), args.length);
|
||||
this._purePipeProxies.push(purePipeProxy);
|
||||
return getPropertyInView(
|
||||
o.importExpr(Identifiers.castByValue)
|
||||
.callFn([purePipeProxy.instance, this.instance.prop('transform')]),
|
||||
callingView, this.view)
|
||||
.callFn(args);
|
||||
} else {
|
||||
return getPropertyInView(this.instance, callingView, this.view).callMethod('transform', args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function _findPipeMeta(view: CompileView, name: string): CompilePipeMetadata {
|
||||
var pipeMeta: CompilePipeMetadata = null;
|
||||
for (var i = view.pipeMetas.length - 1; i >= 0; i--) {
|
||||
var localPipeMeta = view.pipeMetas[i];
|
||||
if (localPipeMeta.name == name) {
|
||||
pipeMeta = localPipeMeta;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isBlank(pipeMeta)) {
|
||||
throw new BaseException(
|
||||
`Illegal state: Could not find pipe ${name} although the parser should have detected this error!`);
|
||||
}
|
||||
return pipeMeta;
|
||||
}
|
117
modules/@angular/compiler/src/view_compiler/compile_query.ts
Normal file
117
modules/@angular/compiler/src/view_compiler/compile_query.ts
Normal file
@ -0,0 +1,117 @@
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import * as o from '../output/output_ast';
|
||||
import {Identifiers} from '../identifiers';
|
||||
|
||||
import {
|
||||
CompileQueryMetadata,
|
||||
CompileIdentifierMetadata,
|
||||
CompileTokenMap
|
||||
} from '../compile_metadata';
|
||||
|
||||
import {CompileView} from './compile_view';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileMethod} from './compile_method';
|
||||
import {getPropertyInView} from './util';
|
||||
|
||||
class ViewQueryValues {
|
||||
constructor(public view: CompileView, public values: Array<o.Expression | ViewQueryValues>) {}
|
||||
}
|
||||
|
||||
export class CompileQuery {
|
||||
private _values: ViewQueryValues;
|
||||
|
||||
constructor(public meta: CompileQueryMetadata, public queryList: o.Expression,
|
||||
public ownerDirectiveExpression: o.Expression, public view: CompileView) {
|
||||
this._values = new ViewQueryValues(view, []);
|
||||
}
|
||||
|
||||
addValue(value: o.Expression, view: CompileView) {
|
||||
var currentView = view;
|
||||
var elPath: CompileElement[] = [];
|
||||
while (isPresent(currentView) && currentView !== this.view) {
|
||||
var parentEl = currentView.declarationElement;
|
||||
elPath.unshift(parentEl);
|
||||
currentView = parentEl.view;
|
||||
}
|
||||
var queryListForDirtyExpr = getPropertyInView(this.queryList, view, this.view);
|
||||
|
||||
var viewValues = this._values;
|
||||
elPath.forEach((el) => {
|
||||
var last =
|
||||
viewValues.values.length > 0 ? viewValues.values[viewValues.values.length - 1] : null;
|
||||
if (last instanceof ViewQueryValues && last.view === el.embeddedView) {
|
||||
viewValues = last;
|
||||
} else {
|
||||
var newViewValues = new ViewQueryValues(el.embeddedView, []);
|
||||
viewValues.values.push(newViewValues);
|
||||
viewValues = newViewValues;
|
||||
}
|
||||
});
|
||||
viewValues.values.push(value);
|
||||
|
||||
if (elPath.length > 0) {
|
||||
view.dirtyParentQueriesMethod.addStmt(
|
||||
queryListForDirtyExpr.callMethod('setDirty', []).toStmt());
|
||||
}
|
||||
}
|
||||
|
||||
afterChildren(targetMethod: CompileMethod) {
|
||||
var values = createQueryValues(this._values);
|
||||
var updateStmts = [this.queryList.callMethod('reset', [o.literalArr(values)]).toStmt()];
|
||||
if (isPresent(this.ownerDirectiveExpression)) {
|
||||
var valueExpr = this.meta.first ? this.queryList.prop('first') : this.queryList;
|
||||
updateStmts.push(
|
||||
this.ownerDirectiveExpression.prop(this.meta.propertyName).set(valueExpr).toStmt());
|
||||
}
|
||||
if (!this.meta.first) {
|
||||
updateStmts.push(this.queryList.callMethod('notifyOnChanges', []).toStmt());
|
||||
}
|
||||
targetMethod.addStmt(new o.IfStmt(this.queryList.prop('dirty'), updateStmts));
|
||||
}
|
||||
}
|
||||
|
||||
function createQueryValues(viewValues: ViewQueryValues): o.Expression[] {
|
||||
return ListWrapper.flatten(viewValues.values.map((entry) => {
|
||||
if (entry instanceof ViewQueryValues) {
|
||||
return mapNestedViews(entry.view.declarationElement.appElement, entry.view,
|
||||
createQueryValues(entry));
|
||||
} else {
|
||||
return <o.Expression>entry;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
function mapNestedViews(declarationAppElement: o.Expression, view: CompileView,
|
||||
expressions: o.Expression[]): o.Expression {
|
||||
var adjustedExpressions: o.Expression[] = expressions.map((expr) => {
|
||||
return o.replaceVarInExpression(o.THIS_EXPR.name, o.variable('nestedView'), expr);
|
||||
});
|
||||
return declarationAppElement.callMethod('mapNestedViews', [
|
||||
o.variable(view.className),
|
||||
o.fn([new o.FnParam('nestedView', view.classType)],
|
||||
[new o.ReturnStatement(o.literalArr(adjustedExpressions))])
|
||||
]);
|
||||
}
|
||||
|
||||
export function createQueryList(query: CompileQueryMetadata, directiveInstance: o.Expression,
|
||||
propertyName: string, compileView: CompileView): o.Expression {
|
||||
compileView.fields.push(new o.ClassField(propertyName, o.importType(Identifiers.QueryList)));
|
||||
var expr = o.THIS_EXPR.prop(propertyName);
|
||||
compileView.createMethod.addStmt(o.THIS_EXPR.prop(propertyName)
|
||||
.set(o.importExpr(Identifiers.QueryList).instantiate([]))
|
||||
.toStmt());
|
||||
return expr;
|
||||
}
|
||||
|
||||
export function addQueryToTokenMap(map: CompileTokenMap<CompileQuery[]>, query: CompileQuery) {
|
||||
query.meta.selectors.forEach((selector) => {
|
||||
var entry = map.get(selector);
|
||||
if (isBlank(entry)) {
|
||||
entry = [];
|
||||
map.add(selector, entry);
|
||||
}
|
||||
entry.push(query);
|
||||
});
|
||||
}
|
209
modules/@angular/compiler/src/view_compiler/compile_view.ts
Normal file
209
modules/@angular/compiler/src/view_compiler/compile_view.ts
Normal file
@ -0,0 +1,209 @@
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, StringMapWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import * as o from '../output/output_ast';
|
||||
import {EventHandlerVars} from './constants';
|
||||
import {CompileQuery, createQueryList, addQueryToTokenMap} from './compile_query';
|
||||
import {NameResolver} from './expression_converter';
|
||||
import {CompileElement, CompileNode} from './compile_element';
|
||||
import {CompileMethod} from './compile_method';
|
||||
import {CompilePipe} from './compile_pipe';
|
||||
import {ViewType} from 'angular2/src/core/linker/view_type';
|
||||
import {
|
||||
CompileDirectiveMetadata,
|
||||
CompilePipeMetadata,
|
||||
CompileIdentifierMetadata,
|
||||
CompileTokenMap
|
||||
} from '../compile_metadata';
|
||||
import {
|
||||
getViewFactoryName,
|
||||
injectFromViewParentInjector,
|
||||
createDiTokenExpression,
|
||||
getPropertyInView,
|
||||
createPureProxy
|
||||
} from './util';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {CompileBinding} from './compile_binding';
|
||||
import {Identifiers} from '../identifiers';
|
||||
|
||||
export class CompileView implements NameResolver {
|
||||
public viewType: ViewType;
|
||||
public viewQueries: CompileTokenMap<CompileQuery[]>;
|
||||
|
||||
public nodes: CompileNode[] = [];
|
||||
// root nodes or AppElements for ViewContainers
|
||||
public rootNodesOrAppElements: o.Expression[] = [];
|
||||
|
||||
public bindings: CompileBinding[] = [];
|
||||
|
||||
public classStatements: o.Statement[] = [];
|
||||
public createMethod: CompileMethod;
|
||||
public injectorGetMethod: CompileMethod;
|
||||
public updateContentQueriesMethod: CompileMethod;
|
||||
public dirtyParentQueriesMethod: CompileMethod;
|
||||
public updateViewQueriesMethod: CompileMethod;
|
||||
public detectChangesInInputsMethod: CompileMethod;
|
||||
public detectChangesRenderPropertiesMethod: CompileMethod;
|
||||
public afterContentLifecycleCallbacksMethod: CompileMethod;
|
||||
public afterViewLifecycleCallbacksMethod: CompileMethod;
|
||||
public destroyMethod: CompileMethod;
|
||||
public eventHandlerMethods: o.ClassMethod[] = [];
|
||||
|
||||
public fields: o.ClassField[] = [];
|
||||
public getters: o.ClassGetter[] = [];
|
||||
public disposables: o.Expression[] = [];
|
||||
public subscriptions: o.Expression[] = [];
|
||||
|
||||
public componentView: CompileView;
|
||||
public purePipes = new Map<string, CompilePipe>();
|
||||
public pipes: CompilePipe[] = [];
|
||||
public locals = new Map<string, o.Expression>();
|
||||
public className: string;
|
||||
public classType: o.Type;
|
||||
public viewFactory: o.ReadVarExpr;
|
||||
|
||||
public literalArrayCount = 0;
|
||||
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,
|
||||
public templateVariableBindings: string[][]) {
|
||||
this.createMethod = new CompileMethod(this);
|
||||
this.injectorGetMethod = new CompileMethod(this);
|
||||
this.updateContentQueriesMethod = new CompileMethod(this);
|
||||
this.dirtyParentQueriesMethod = new CompileMethod(this);
|
||||
this.updateViewQueriesMethod = new CompileMethod(this);
|
||||
this.detectChangesInInputsMethod = new CompileMethod(this);
|
||||
this.detectChangesRenderPropertiesMethod = new CompileMethod(this);
|
||||
|
||||
this.afterContentLifecycleCallbacksMethod = new CompileMethod(this);
|
||||
this.afterViewLifecycleCallbacksMethod = new CompileMethod(this);
|
||||
this.destroyMethod = new CompileMethod(this);
|
||||
|
||||
this.viewType = getViewType(component, viewIndex);
|
||||
this.className = `_View_${component.type.name}${viewIndex}`;
|
||||
this.classType = o.importType(new CompileIdentifierMetadata({name: this.className}));
|
||||
this.viewFactory = o.variable(getViewFactoryName(component, viewIndex));
|
||||
if (this.viewType === ViewType.COMPONENT || this.viewType === ViewType.HOST) {
|
||||
this.componentView = this;
|
||||
} 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');
|
||||
ListWrapper.forEachWithIndex(this.component.viewQueries, (queryMeta, queryIndex) => {
|
||||
var propName = `_viewQuery_${queryMeta.selectors[0].name}_${queryIndex}`;
|
||||
var queryList = createQueryList(queryMeta, directiveInstance, propName, this);
|
||||
var query = new CompileQuery(queryMeta, queryList, directiveInstance, this);
|
||||
addQueryToTokenMap(viewQueries, query);
|
||||
});
|
||||
var constructorViewQueryCount = 0;
|
||||
this.component.type.diDeps.forEach((dep) => {
|
||||
if (isPresent(dep.viewQuery)) {
|
||||
var queryList = o.THIS_EXPR.prop('declarationAppElement')
|
||||
.prop('componentConstructorViewQueries')
|
||||
.key(o.literal(constructorViewQueryCount++));
|
||||
var query = new CompileQuery(dep.viewQuery, queryList, null, this);
|
||||
addQueryToTokenMap(viewQueries, query);
|
||||
}
|
||||
});
|
||||
}
|
||||
this.viewQueries = viewQueries;
|
||||
templateVariableBindings.forEach(
|
||||
(entry) => { this.locals.set(entry[1], o.THIS_EXPR.prop('context').prop(entry[0])); });
|
||||
|
||||
if (!this.declarationElement.isNull()) {
|
||||
this.declarationElement.setEmbeddedView(this);
|
||||
}
|
||||
}
|
||||
|
||||
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression {
|
||||
var compView = this.componentView;
|
||||
var pipe = compView.purePipes.get(name);
|
||||
if (isBlank(pipe)) {
|
||||
pipe = new CompilePipe(compView, name);
|
||||
if (pipe.pure) {
|
||||
compView.purePipes.set(name, pipe);
|
||||
}
|
||||
compView.pipes.push(pipe);
|
||||
}
|
||||
return pipe.call(this, [input].concat(args));
|
||||
}
|
||||
|
||||
getLocal(name: string): o.Expression {
|
||||
if (name == EventHandlerVars.event.name) {
|
||||
return EventHandlerVars.event;
|
||||
}
|
||||
var currView: CompileView = this;
|
||||
var result = currView.locals.get(name);
|
||||
while (isBlank(result) && isPresent(currView.declarationElement.view)) {
|
||||
currView = currView.declarationElement.view;
|
||||
result = currView.locals.get(name);
|
||||
}
|
||||
if (isPresent(result)) {
|
||||
return getPropertyInView(result, this, currView);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
createLiteralArray(values: o.Expression[]): o.Expression {
|
||||
if (values.length === 0) {
|
||||
return o.importExpr(Identifiers.EMPTY_ARRAY);
|
||||
}
|
||||
var proxyExpr = o.THIS_EXPR.prop(`_arr_${this.literalArrayCount++}`);
|
||||
var proxyParams: o.FnParam[] = [];
|
||||
var proxyReturnEntries: o.Expression[] = [];
|
||||
for (var i = 0; i < values.length; i++) {
|
||||
var paramName = `p${i}`;
|
||||
proxyParams.push(new o.FnParam(paramName));
|
||||
proxyReturnEntries.push(o.variable(paramName));
|
||||
}
|
||||
createPureProxy(o.fn(proxyParams, [new o.ReturnStatement(o.literalArr(proxyReturnEntries))]),
|
||||
values.length, proxyExpr, this);
|
||||
return proxyExpr.callFn(values);
|
||||
}
|
||||
|
||||
createLiteralMap(entries: Array<Array<string | o.Expression>>): o.Expression {
|
||||
if (entries.length === 0) {
|
||||
return o.importExpr(Identifiers.EMPTY_MAP);
|
||||
}
|
||||
var proxyExpr = o.THIS_EXPR.prop(`_map_${this.literalMapCount++}`);
|
||||
var proxyParams: o.FnParam[] = [];
|
||||
var proxyReturnEntries: Array<Array<string | o.Expression>> = [];
|
||||
var values: o.Expression[] = [];
|
||||
for (var i = 0; i < entries.length; i++) {
|
||||
var paramName = `p${i}`;
|
||||
proxyParams.push(new o.FnParam(paramName));
|
||||
proxyReturnEntries.push([entries[i][0], o.variable(paramName)]);
|
||||
values.push(<o.Expression>entries[i][1]);
|
||||
}
|
||||
createPureProxy(o.fn(proxyParams, [new o.ReturnStatement(o.literalMap(proxyReturnEntries))]),
|
||||
entries.length, proxyExpr, this);
|
||||
return proxyExpr.callFn(values);
|
||||
}
|
||||
|
||||
afterNodes() {
|
||||
this.pipes.forEach((pipe) => pipe.create());
|
||||
this.viewQueries.values().forEach(
|
||||
(queries) => queries.forEach((query) => query.afterChildren(this.updateViewQueriesMethod)));
|
||||
}
|
||||
}
|
||||
|
||||
function getViewType(component: CompileDirectiveMetadata, embeddedTemplateIndex: number): ViewType {
|
||||
if (embeddedTemplateIndex > 0) {
|
||||
return ViewType.EMBEDDED;
|
||||
} else if (component.type.isHost) {
|
||||
return ViewType.HOST;
|
||||
} else {
|
||||
return ViewType.COMPONENT;
|
||||
}
|
||||
}
|
86
modules/@angular/compiler/src/view_compiler/constants.ts
Normal file
86
modules/@angular/compiler/src/view_compiler/constants.ts
Normal file
@ -0,0 +1,86 @@
|
||||
import {serializeEnum, isBlank, resolveEnumToken} from 'angular2/src/facade/lang';
|
||||
import {CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||
import {
|
||||
ChangeDetectorState,
|
||||
ChangeDetectionStrategy
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
import {ViewType} from 'angular2/src/core/linker/view_type';
|
||||
import * as o from '../output/output_ast';
|
||||
import {Identifiers} from '../identifiers';
|
||||
|
||||
function _enumExpression(classIdentifier: CompileIdentifierMetadata, value: any): o.Expression {
|
||||
if (isBlank(value)) return o.NULL_EXPR;
|
||||
var name = resolveEnumToken(classIdentifier.runtime, value);
|
||||
return o.importExpr(new CompileIdentifierMetadata({
|
||||
name: `${classIdentifier.name}.${name}`,
|
||||
moduleUrl: classIdentifier.moduleUrl,
|
||||
runtime: value
|
||||
}));
|
||||
}
|
||||
|
||||
export class ViewTypeEnum {
|
||||
static fromValue(value: ViewType): o.Expression {
|
||||
return _enumExpression(Identifiers.ViewType, value);
|
||||
}
|
||||
static HOST = ViewTypeEnum.fromValue(ViewType.HOST);
|
||||
static COMPONENT = ViewTypeEnum.fromValue(ViewType.COMPONENT);
|
||||
static EMBEDDED = ViewTypeEnum.fromValue(ViewType.EMBEDDED);
|
||||
}
|
||||
|
||||
export class ViewEncapsulationEnum {
|
||||
static fromValue(value: ViewEncapsulation): o.Expression {
|
||||
return _enumExpression(Identifiers.ViewEncapsulation, value);
|
||||
}
|
||||
static Emulated = ViewEncapsulationEnum.fromValue(ViewEncapsulation.Emulated);
|
||||
static Native = ViewEncapsulationEnum.fromValue(ViewEncapsulation.Native);
|
||||
static None = ViewEncapsulationEnum.fromValue(ViewEncapsulation.None);
|
||||
}
|
||||
|
||||
export class ChangeDetectorStateEnum {
|
||||
static fromValue(value: ChangeDetectorState): o.Expression {
|
||||
return _enumExpression(Identifiers.ChangeDetectorState, value);
|
||||
}
|
||||
static NeverChecked = ChangeDetectorStateEnum.fromValue(ChangeDetectorState.NeverChecked);
|
||||
static CheckedBefore = ChangeDetectorStateEnum.fromValue(ChangeDetectorState.CheckedBefore);
|
||||
static Errored = ChangeDetectorStateEnum.fromValue(ChangeDetectorState.Errored);
|
||||
}
|
||||
|
||||
export class ChangeDetectionStrategyEnum {
|
||||
static fromValue(value: ChangeDetectionStrategy): o.Expression {
|
||||
return _enumExpression(Identifiers.ChangeDetectionStrategy, value);
|
||||
}
|
||||
static CheckOnce = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.CheckOnce);
|
||||
static Checked = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.Checked);
|
||||
static CheckAlways = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.CheckAlways);
|
||||
static Detached = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.Detached);
|
||||
static OnPush = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.OnPush);
|
||||
static Default = ChangeDetectionStrategyEnum.fromValue(ChangeDetectionStrategy.Default);
|
||||
}
|
||||
|
||||
export class ViewConstructorVars {
|
||||
static viewUtils = o.variable('viewUtils');
|
||||
static parentInjector = o.variable('parentInjector');
|
||||
static declarationEl = o.variable('declarationEl');
|
||||
}
|
||||
|
||||
export class ViewProperties {
|
||||
static renderer = o.THIS_EXPR.prop('renderer');
|
||||
static projectableNodes = o.THIS_EXPR.prop('projectableNodes');
|
||||
static viewUtils = o.THIS_EXPR.prop('viewUtils');
|
||||
}
|
||||
|
||||
export class EventHandlerVars { static event = o.variable('$event'); }
|
||||
|
||||
export class InjectMethodVars {
|
||||
static token = o.variable('token');
|
||||
static requestNodeIndex = o.variable('requestNodeIndex');
|
||||
static notFoundResult = o.variable('notFoundResult');
|
||||
}
|
||||
|
||||
export class DetectChangesVars {
|
||||
static throwOnChange = o.variable(`throwOnChange`);
|
||||
static changes = o.variable(`changes`);
|
||||
static changed = o.variable(`changed`);
|
||||
static valUnwrapper = o.variable(`valUnwrapper`);
|
||||
}
|
163
modules/@angular/compiler/src/view_compiler/event_binder.ts
Normal file
163
modules/@angular/compiler/src/view_compiler/event_binder.ts
Normal file
@ -0,0 +1,163 @@
|
||||
import {isBlank, isPresent, StringWrapper} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||
import {EventHandlerVars, ViewProperties} from './constants';
|
||||
|
||||
import * as o from '../output/output_ast';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileMethod} from './compile_method';
|
||||
|
||||
import {BoundEventAst, DirectiveAst} from '../template_ast';
|
||||
import {CompileDirectiveMetadata} from '../compile_metadata';
|
||||
|
||||
import {convertCdStatementToIr} from './expression_converter';
|
||||
import {CompileBinding} from './compile_binding';
|
||||
|
||||
export class CompileEventListener {
|
||||
private _method: CompileMethod;
|
||||
private _hasComponentHostListener: boolean = false;
|
||||
private _methodName: string;
|
||||
private _eventParam: o.FnParam;
|
||||
private _actionResultExprs: o.Expression[] = [];
|
||||
|
||||
static getOrCreate(compileElement: CompileElement, eventTarget: string, eventName: string,
|
||||
targetEventListeners: CompileEventListener[]): CompileEventListener {
|
||||
var listener = targetEventListeners.find(listener => listener.eventTarget == eventTarget &&
|
||||
listener.eventName == eventName);
|
||||
if (isBlank(listener)) {
|
||||
listener = new CompileEventListener(compileElement, eventTarget, eventName,
|
||||
targetEventListeners.length);
|
||||
targetEventListeners.push(listener);
|
||||
}
|
||||
return listener;
|
||||
}
|
||||
|
||||
constructor(public compileElement: CompileElement, public eventTarget: string,
|
||||
public eventName: string, listenerIndex: number) {
|
||||
this._method = new CompileMethod(compileElement.view);
|
||||
this._methodName =
|
||||
`_handle_${santitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`;
|
||||
this._eventParam =
|
||||
new o.FnParam(EventHandlerVars.event.name,
|
||||
o.importType(this.compileElement.view.genConfig.renderTypes.renderEvent));
|
||||
}
|
||||
|
||||
addAction(hostEvent: BoundEventAst, directive: CompileDirectiveMetadata,
|
||||
directiveInstance: o.Expression) {
|
||||
if (isPresent(directive) && directive.isComponent) {
|
||||
this._hasComponentHostListener = true;
|
||||
}
|
||||
this._method.resetDebugInfo(this.compileElement.nodeIndex, hostEvent);
|
||||
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) {
|
||||
var lastStatement = actionStmts[lastIndex];
|
||||
var returnExpr = convertStmtIntoExpression(lastStatement);
|
||||
var preventDefaultVar = o.variable(`pd_${this._actionResultExprs.length}`);
|
||||
this._actionResultExprs.push(preventDefaultVar);
|
||||
if (isPresent(returnExpr)) {
|
||||
// Note: We need to cast the result of the method call to dynamic,
|
||||
// as it might be a void method!
|
||||
actionStmts[lastIndex] =
|
||||
preventDefaultVar.set(returnExpr.cast(o.DYNAMIC_TYPE).notIdentical(o.literal(false)))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]);
|
||||
}
|
||||
}
|
||||
this._method.addStmts(actionStmts);
|
||||
}
|
||||
|
||||
finishMethod() {
|
||||
var markPathToRootStart = this._hasComponentHostListener ?
|
||||
this.compileElement.appElement.prop('componentView') :
|
||||
o.THIS_EXPR;
|
||||
var resultExpr: o.Expression = o.literal(true);
|
||||
this._actionResultExprs.forEach((expr) => { resultExpr = resultExpr.and(expr); });
|
||||
var stmts =
|
||||
(<o.Statement[]>[markPathToRootStart.callMethod('markPathToRootAsCheckOnce', []).toStmt()])
|
||||
.concat(this._method.finish())
|
||||
.concat([new o.ReturnStatement(resultExpr)]);
|
||||
// private is fine here as no child view will reference the event handler...
|
||||
this.compileElement.view.eventHandlerMethods.push(new o.ClassMethod(
|
||||
this._methodName, [this._eventParam], stmts, o.BOOL_TYPE, [o.StmtModifier.Private]));
|
||||
}
|
||||
|
||||
listenToRenderer() {
|
||||
var listenExpr;
|
||||
var eventListener = o.THIS_EXPR.callMethod(
|
||||
'eventHandler',
|
||||
[o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.bind, [o.THIS_EXPR])]);
|
||||
if (isPresent(this.eventTarget)) {
|
||||
listenExpr = ViewProperties.renderer.callMethod(
|
||||
'listenGlobal', [o.literal(this.eventTarget), o.literal(this.eventName), eventListener]);
|
||||
} else {
|
||||
listenExpr = ViewProperties.renderer.callMethod(
|
||||
'listen', [this.compileElement.renderNode, o.literal(this.eventName), eventListener]);
|
||||
}
|
||||
var disposable = o.variable(`disposable_${this.compileElement.view.disposables.length}`);
|
||||
this.compileElement.view.disposables.push(disposable);
|
||||
// private is fine here as no child view will reference the event handler...
|
||||
this.compileElement.view.createMethod.addStmt(
|
||||
disposable.set(listenExpr).toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
|
||||
}
|
||||
|
||||
listenToDirective(directiveInstance: o.Expression, observablePropName: string) {
|
||||
var subscription = o.variable(`subscription_${this.compileElement.view.subscriptions.length}`);
|
||||
this.compileElement.view.subscriptions.push(subscription);
|
||||
var eventListener = o.THIS_EXPR.callMethod(
|
||||
'eventHandler',
|
||||
[o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.bind, [o.THIS_EXPR])]);
|
||||
this.compileElement.view.createMethod.addStmt(
|
||||
subscription.set(directiveInstance.prop(observablePropName)
|
||||
.callMethod(o.BuiltinMethod.SubscribeObservable, [eventListener]))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
}
|
||||
}
|
||||
|
||||
export function collectEventListeners(hostEvents: BoundEventAst[], dirs: DirectiveAst[],
|
||||
compileElement: CompileElement): CompileEventListener[] {
|
||||
var eventListeners: CompileEventListener[] = [];
|
||||
hostEvents.forEach((hostEvent) => {
|
||||
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
|
||||
var listener = CompileEventListener.getOrCreate(compileElement, hostEvent.target,
|
||||
hostEvent.name, eventListeners);
|
||||
listener.addAction(hostEvent, null, null);
|
||||
});
|
||||
ListWrapper.forEachWithIndex(dirs, (directiveAst, i) => {
|
||||
var directiveInstance = compileElement.directiveInstances[i];
|
||||
directiveAst.hostEvents.forEach((hostEvent) => {
|
||||
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
|
||||
var listener = CompileEventListener.getOrCreate(compileElement, hostEvent.target,
|
||||
hostEvent.name, eventListeners);
|
||||
listener.addAction(hostEvent, directiveAst.directive, directiveInstance);
|
||||
});
|
||||
});
|
||||
eventListeners.forEach((listener) => listener.finishMethod());
|
||||
return eventListeners;
|
||||
}
|
||||
|
||||
export function bindDirectiveOutputs(directiveAst: DirectiveAst, directiveInstance: o.Expression,
|
||||
eventListeners: CompileEventListener[]) {
|
||||
StringMapWrapper.forEach(directiveAst.directive.outputs, (eventName, observablePropName) => {
|
||||
eventListeners.filter(listener => listener.eventName == eventName)
|
||||
.forEach(
|
||||
(listener) => { listener.listenToDirective(directiveInstance, observablePropName); });
|
||||
});
|
||||
}
|
||||
|
||||
export function bindRenderOutputs(eventListeners: CompileEventListener[]) {
|
||||
eventListeners.forEach(listener => listener.listenToRenderer());
|
||||
}
|
||||
|
||||
function convertStmtIntoExpression(stmt: o.Statement): o.Expression {
|
||||
if (stmt instanceof o.ExpressionStatement) {
|
||||
return stmt.expr;
|
||||
} else if (stmt instanceof o.ReturnStatement) {
|
||||
return stmt.value;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function santitizeEventName(name: string): string {
|
||||
return StringWrapper.replaceAll(name, /[^a-zA-Z_]/g, '_');
|
||||
}
|
@ -0,0 +1,252 @@
|
||||
import * as cdAst from '../expression_parser/ast';
|
||||
import * as o from '../output/output_ast';
|
||||
import {Identifiers} from '../identifiers';
|
||||
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
import {isBlank, isPresent, isArray} from 'angular2/src/facade/lang';
|
||||
|
||||
var IMPLICIT_RECEIVER = o.variable('#implicit');
|
||||
|
||||
export interface NameResolver {
|
||||
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression;
|
||||
getLocal(name: string): o.Expression;
|
||||
createLiteralArray(values: o.Expression[]): o.Expression;
|
||||
createLiteralMap(values: Array<Array<string | o.Expression>>): o.Expression;
|
||||
}
|
||||
|
||||
export class ExpressionWithWrappedValueInfo {
|
||||
constructor(public expression: o.Expression, public needsValueUnwrapper: boolean) {}
|
||||
}
|
||||
|
||||
export function convertCdExpressionToIr(
|
||||
nameResolver: NameResolver, implicitReceiver: o.Expression, expression: cdAst.AST,
|
||||
valueUnwrapper: o.ReadVarExpr): ExpressionWithWrappedValueInfo {
|
||||
var visitor = new _AstToIrVisitor(nameResolver, implicitReceiver, valueUnwrapper);
|
||||
var irAst: o.Expression = expression.visit(visitor, _Mode.Expression);
|
||||
return new ExpressionWithWrappedValueInfo(irAst, visitor.needsValueUnwrapper);
|
||||
}
|
||||
|
||||
export function convertCdStatementToIr(nameResolver: NameResolver, implicitReceiver: o.Expression,
|
||||
stmt: cdAst.AST): o.Statement[] {
|
||||
var visitor = new _AstToIrVisitor(nameResolver, implicitReceiver, null);
|
||||
var statements = [];
|
||||
flattenStatements(stmt.visit(visitor, _Mode.Statement), statements);
|
||||
return statements;
|
||||
}
|
||||
|
||||
enum _Mode {
|
||||
Statement,
|
||||
Expression
|
||||
}
|
||||
|
||||
function ensureStatementMode(mode: _Mode, ast: cdAst.AST) {
|
||||
if (mode !== _Mode.Statement) {
|
||||
throw new BaseException(`Expected a statement, but saw ${ast}`);
|
||||
}
|
||||
}
|
||||
|
||||
function ensureExpressionMode(mode: _Mode, ast: cdAst.AST) {
|
||||
if (mode !== _Mode.Expression) {
|
||||
throw new BaseException(`Expected an expression, but saw ${ast}`);
|
||||
}
|
||||
}
|
||||
|
||||
function convertToStatementIfNeeded(mode: _Mode, expr: o.Expression): o.Expression | o.Statement {
|
||||
if (mode === _Mode.Statement) {
|
||||
return expr.toStmt();
|
||||
} else {
|
||||
return expr;
|
||||
}
|
||||
}
|
||||
|
||||
class _AstToIrVisitor implements cdAst.AstVisitor {
|
||||
public needsValueUnwrapper: boolean = false;
|
||||
|
||||
constructor(private _nameResolver: NameResolver, private _implicitReceiver: o.Expression,
|
||||
private _valueUnwrapper: o.ReadVarExpr) {}
|
||||
|
||||
visitBinary(ast: cdAst.Binary, mode: _Mode): any {
|
||||
var op;
|
||||
switch (ast.operation) {
|
||||
case '+':
|
||||
op = o.BinaryOperator.Plus;
|
||||
break;
|
||||
case '-':
|
||||
op = o.BinaryOperator.Minus;
|
||||
break;
|
||||
case '*':
|
||||
op = o.BinaryOperator.Multiply;
|
||||
break;
|
||||
case '/':
|
||||
op = o.BinaryOperator.Divide;
|
||||
break;
|
||||
case '%':
|
||||
op = o.BinaryOperator.Modulo;
|
||||
break;
|
||||
case '&&':
|
||||
op = o.BinaryOperator.And;
|
||||
break;
|
||||
case '||':
|
||||
op = o.BinaryOperator.Or;
|
||||
break;
|
||||
case '==':
|
||||
op = o.BinaryOperator.Equals;
|
||||
break;
|
||||
case '!=':
|
||||
op = o.BinaryOperator.NotEquals;
|
||||
break;
|
||||
case '===':
|
||||
op = o.BinaryOperator.Identical;
|
||||
break;
|
||||
case '!==':
|
||||
op = o.BinaryOperator.NotIdentical;
|
||||
break;
|
||||
case '<':
|
||||
op = o.BinaryOperator.Lower;
|
||||
break;
|
||||
case '>':
|
||||
op = o.BinaryOperator.Bigger;
|
||||
break;
|
||||
case '<=':
|
||||
op = o.BinaryOperator.LowerEquals;
|
||||
break;
|
||||
case '>=':
|
||||
op = o.BinaryOperator.BiggerEquals;
|
||||
break;
|
||||
default:
|
||||
throw new BaseException(`Unsupported operation ${ast.operation}`);
|
||||
}
|
||||
|
||||
return convertToStatementIfNeeded(
|
||||
mode, new o.BinaryOperatorExpr(op, ast.left.visit(this, _Mode.Expression),
|
||||
ast.right.visit(this, _Mode.Expression)));
|
||||
}
|
||||
visitChain(ast: cdAst.Chain, mode: _Mode): any {
|
||||
ensureStatementMode(mode, ast);
|
||||
return this.visitAll(ast.expressions, mode);
|
||||
}
|
||||
visitConditional(ast: cdAst.Conditional, mode: _Mode): any {
|
||||
var value: o.Expression = ast.condition.visit(this, _Mode.Expression);
|
||||
return convertToStatementIfNeeded(
|
||||
mode, value.conditional(ast.trueExp.visit(this, _Mode.Expression),
|
||||
ast.falseExp.visit(this, _Mode.Expression)));
|
||||
}
|
||||
visitPipe(ast: cdAst.BindingPipe, mode: _Mode): any {
|
||||
var input = ast.exp.visit(this, _Mode.Expression);
|
||||
var args = this.visitAll(ast.args, _Mode.Expression);
|
||||
var value = this._nameResolver.callPipe(ast.name, input, args);
|
||||
this.needsValueUnwrapper = true;
|
||||
return convertToStatementIfNeeded(mode, this._valueUnwrapper.callMethod('unwrap', [value]));
|
||||
}
|
||||
visitFunctionCall(ast: cdAst.FunctionCall, mode: _Mode): any {
|
||||
return convertToStatementIfNeeded(mode, ast.target.visit(this, _Mode.Expression)
|
||||
.callFn(this.visitAll(ast.args, _Mode.Expression)));
|
||||
}
|
||||
visitImplicitReceiver(ast: cdAst.ImplicitReceiver, mode: _Mode): any {
|
||||
ensureExpressionMode(mode, ast);
|
||||
return IMPLICIT_RECEIVER;
|
||||
}
|
||||
visitInterpolation(ast: cdAst.Interpolation, mode: _Mode): any {
|
||||
ensureExpressionMode(mode, ast);
|
||||
var args = [o.literal(ast.expressions.length)];
|
||||
for (var i = 0; i < ast.strings.length - 1; i++) {
|
||||
args.push(o.literal(ast.strings[i]));
|
||||
args.push(ast.expressions[i].visit(this, _Mode.Expression));
|
||||
}
|
||||
args.push(o.literal(ast.strings[ast.strings.length - 1]));
|
||||
return o.importExpr(Identifiers.interpolate).callFn(args);
|
||||
}
|
||||
visitKeyedRead(ast: cdAst.KeyedRead, mode: _Mode): any {
|
||||
return convertToStatementIfNeeded(
|
||||
mode, ast.obj.visit(this, _Mode.Expression).key(ast.key.visit(this, _Mode.Expression)));
|
||||
}
|
||||
visitKeyedWrite(ast: cdAst.KeyedWrite, mode: _Mode): any {
|
||||
var obj: o.Expression = ast.obj.visit(this, _Mode.Expression);
|
||||
var key: o.Expression = ast.key.visit(this, _Mode.Expression);
|
||||
var value: o.Expression = ast.value.visit(this, _Mode.Expression);
|
||||
return convertToStatementIfNeeded(mode, obj.key(key).set(value));
|
||||
}
|
||||
visitLiteralArray(ast: cdAst.LiteralArray, mode: _Mode): any {
|
||||
return convertToStatementIfNeeded(
|
||||
mode, this._nameResolver.createLiteralArray(this.visitAll(ast.expressions, mode)));
|
||||
}
|
||||
visitLiteralMap(ast: cdAst.LiteralMap, mode: _Mode): any {
|
||||
var parts = [];
|
||||
for (var i = 0; i < ast.keys.length; i++) {
|
||||
parts.push([ast.keys[i], ast.values[i].visit(this, _Mode.Expression)]);
|
||||
}
|
||||
return convertToStatementIfNeeded(mode, this._nameResolver.createLiteralMap(parts));
|
||||
}
|
||||
visitLiteralPrimitive(ast: cdAst.LiteralPrimitive, mode: _Mode): any {
|
||||
return convertToStatementIfNeeded(mode, o.literal(ast.value));
|
||||
}
|
||||
visitMethodCall(ast: cdAst.MethodCall, mode: _Mode): any {
|
||||
var args = this.visitAll(ast.args, _Mode.Expression);
|
||||
var result = null;
|
||||
var receiver = ast.receiver.visit(this, _Mode.Expression);
|
||||
if (receiver === IMPLICIT_RECEIVER) {
|
||||
var varExpr = this._nameResolver.getLocal(ast.name);
|
||||
if (isPresent(varExpr)) {
|
||||
result = varExpr.callFn(args);
|
||||
} else {
|
||||
receiver = this._implicitReceiver;
|
||||
}
|
||||
}
|
||||
if (isBlank(result)) {
|
||||
result = receiver.callMethod(ast.name, args);
|
||||
}
|
||||
return convertToStatementIfNeeded(mode, result);
|
||||
}
|
||||
visitPrefixNot(ast: cdAst.PrefixNot, mode: _Mode): any {
|
||||
return convertToStatementIfNeeded(mode, o.not(ast.expression.visit(this, _Mode.Expression)));
|
||||
}
|
||||
visitPropertyRead(ast: cdAst.PropertyRead, mode: _Mode): any {
|
||||
var result = null;
|
||||
var receiver = ast.receiver.visit(this, _Mode.Expression);
|
||||
if (receiver === IMPLICIT_RECEIVER) {
|
||||
result = this._nameResolver.getLocal(ast.name);
|
||||
if (isBlank(result)) {
|
||||
receiver = this._implicitReceiver;
|
||||
}
|
||||
}
|
||||
if (isBlank(result)) {
|
||||
result = receiver.prop(ast.name);
|
||||
}
|
||||
return convertToStatementIfNeeded(mode, result);
|
||||
}
|
||||
visitPropertyWrite(ast: cdAst.PropertyWrite, mode: _Mode): any {
|
||||
var receiver: o.Expression = ast.receiver.visit(this, _Mode.Expression);
|
||||
if (receiver === IMPLICIT_RECEIVER) {
|
||||
var varExpr = this._nameResolver.getLocal(ast.name);
|
||||
if (isPresent(varExpr)) {
|
||||
throw new BaseException('Cannot assign to a reference or variable!');
|
||||
}
|
||||
receiver = this._implicitReceiver;
|
||||
}
|
||||
return convertToStatementIfNeeded(
|
||||
mode, receiver.prop(ast.name).set(ast.value.visit(this, _Mode.Expression)));
|
||||
}
|
||||
visitSafePropertyRead(ast: cdAst.SafePropertyRead, mode: _Mode): any {
|
||||
var receiver = ast.receiver.visit(this, _Mode.Expression);
|
||||
return convertToStatementIfNeeded(
|
||||
mode, receiver.isBlank().conditional(o.NULL_EXPR, receiver.prop(ast.name)));
|
||||
}
|
||||
visitSafeMethodCall(ast: cdAst.SafeMethodCall, mode: _Mode): any {
|
||||
var receiver = ast.receiver.visit(this, _Mode.Expression);
|
||||
var args = this.visitAll(ast.args, _Mode.Expression);
|
||||
return convertToStatementIfNeeded(
|
||||
mode, receiver.isBlank().conditional(o.NULL_EXPR, receiver.callMethod(ast.name, args)));
|
||||
}
|
||||
visitAll(asts: cdAst.AST[], mode: _Mode): any { return asts.map(ast => ast.visit(this, mode)); }
|
||||
visitQuote(ast: cdAst.Quote, mode: _Mode): any {
|
||||
throw new BaseException('Quotes are not supported for evaluation!');
|
||||
}
|
||||
}
|
||||
|
||||
function flattenStatements(arg: any, output: o.Statement[]) {
|
||||
if (isArray(arg)) {
|
||||
(<any[]>arg).forEach((entry) => flattenStatements(entry, output));
|
||||
} else {
|
||||
output.push(arg);
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
import * as o from '../output/output_ast';
|
||||
import {DetectChangesVars, ChangeDetectorStateEnum} from './constants';
|
||||
import {LifecycleHooks} from 'angular2/src/core/metadata/lifecycle_hooks';
|
||||
|
||||
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
|
||||
import {DirectiveAst} from '../template_ast';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileView} from './compile_view';
|
||||
|
||||
var STATE_IS_NEVER_CHECKED =
|
||||
o.THIS_EXPR.prop('cdState').identical(ChangeDetectorStateEnum.NeverChecked);
|
||||
var NOT_THROW_ON_CHANGES = o.not(DetectChangesVars.throwOnChange);
|
||||
|
||||
export function bindDirectiveDetectChangesLifecycleCallbacks(
|
||||
directiveAst: DirectiveAst, directiveInstance: o.Expression, compileElement: CompileElement) {
|
||||
var view = compileElement.view;
|
||||
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
|
||||
var lifecycleHooks = directiveAst.directive.lifecycleHooks;
|
||||
if (lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1 && directiveAst.inputs.length > 0) {
|
||||
detectChangesInInputsMethod.addStmt(new o.IfStmt(
|
||||
DetectChangesVars.changes.notIdentical(o.NULL_EXPR),
|
||||
[directiveInstance.callMethod('ngOnChanges', [DetectChangesVars.changes]).toStmt()]));
|
||||
}
|
||||
if (lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1) {
|
||||
detectChangesInInputsMethod.addStmt(
|
||||
new o.IfStmt(STATE_IS_NEVER_CHECKED.and(NOT_THROW_ON_CHANGES),
|
||||
[directiveInstance.callMethod('ngOnInit', []).toStmt()]));
|
||||
}
|
||||
if (lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1) {
|
||||
detectChangesInInputsMethod.addStmt(new o.IfStmt(
|
||||
NOT_THROW_ON_CHANGES, [directiveInstance.callMethod('ngDoCheck', []).toStmt()]));
|
||||
}
|
||||
}
|
||||
|
||||
export function bindDirectiveAfterContentLifecycleCallbacks(directiveMeta: CompileDirectiveMetadata,
|
||||
directiveInstance: o.Expression,
|
||||
compileElement: CompileElement) {
|
||||
var view = compileElement.view;
|
||||
var lifecycleHooks = directiveMeta.lifecycleHooks;
|
||||
var afterContentLifecycleCallbacksMethod = view.afterContentLifecycleCallbacksMethod;
|
||||
afterContentLifecycleCallbacksMethod.resetDebugInfo(compileElement.nodeIndex,
|
||||
compileElement.sourceAst);
|
||||
if (lifecycleHooks.indexOf(LifecycleHooks.AfterContentInit) !== -1) {
|
||||
afterContentLifecycleCallbacksMethod.addStmt(new o.IfStmt(
|
||||
STATE_IS_NEVER_CHECKED, [directiveInstance.callMethod('ngAfterContentInit', []).toStmt()]));
|
||||
}
|
||||
if (lifecycleHooks.indexOf(LifecycleHooks.AfterContentChecked) !== -1) {
|
||||
afterContentLifecycleCallbacksMethod.addStmt(
|
||||
directiveInstance.callMethod('ngAfterContentChecked', []).toStmt());
|
||||
}
|
||||
}
|
||||
|
||||
export function bindDirectiveAfterViewLifecycleCallbacks(directiveMeta: CompileDirectiveMetadata,
|
||||
directiveInstance: o.Expression,
|
||||
compileElement: CompileElement) {
|
||||
var view = compileElement.view;
|
||||
var lifecycleHooks = directiveMeta.lifecycleHooks;
|
||||
var afterViewLifecycleCallbacksMethod = view.afterViewLifecycleCallbacksMethod;
|
||||
afterViewLifecycleCallbacksMethod.resetDebugInfo(compileElement.nodeIndex,
|
||||
compileElement.sourceAst);
|
||||
if (lifecycleHooks.indexOf(LifecycleHooks.AfterViewInit) !== -1) {
|
||||
afterViewLifecycleCallbacksMethod.addStmt(new o.IfStmt(
|
||||
STATE_IS_NEVER_CHECKED, [directiveInstance.callMethod('ngAfterViewInit', []).toStmt()]));
|
||||
}
|
||||
if (lifecycleHooks.indexOf(LifecycleHooks.AfterViewChecked) !== -1) {
|
||||
afterViewLifecycleCallbacksMethod.addStmt(
|
||||
directiveInstance.callMethod('ngAfterViewChecked', []).toStmt());
|
||||
}
|
||||
}
|
||||
|
||||
export function bindDirectiveDestroyLifecycleCallbacks(directiveMeta: CompileDirectiveMetadata,
|
||||
directiveInstance: o.Expression,
|
||||
compileElement: CompileElement) {
|
||||
var onDestroyMethod = compileElement.view.destroyMethod;
|
||||
onDestroyMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||
if (directiveMeta.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1) {
|
||||
onDestroyMethod.addStmt(directiveInstance.callMethod('ngOnDestroy', []).toStmt());
|
||||
}
|
||||
}
|
||||
|
||||
export function bindPipeDestroyLifecycleCallbacks(pipeMeta: CompilePipeMetadata,
|
||||
pipeInstance: o.Expression, view: CompileView) {
|
||||
var onDestroyMethod = view.destroyMethod;
|
||||
if (pipeMeta.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1) {
|
||||
onDestroyMethod.addStmt(pipeInstance.callMethod('ngOnDestroy', []).toStmt());
|
||||
}
|
||||
}
|
210
modules/@angular/compiler/src/view_compiler/property_binder.ts
Normal file
210
modules/@angular/compiler/src/view_compiler/property_binder.ts
Normal file
@ -0,0 +1,210 @@
|
||||
import * as cdAst from '../expression_parser/ast';
|
||||
import * as o from '../output/output_ast';
|
||||
import {Identifiers} from '../identifiers';
|
||||
import {DetectChangesVars} from './constants';
|
||||
|
||||
import {
|
||||
BoundTextAst,
|
||||
BoundElementPropertyAst,
|
||||
DirectiveAst,
|
||||
PropertyBindingType,
|
||||
TemplateAst
|
||||
} from '../template_ast';
|
||||
|
||||
import {isBlank, isPresent, isArray} from 'angular2/src/facade/lang';
|
||||
|
||||
import {CompileView} from './compile_view';
|
||||
import {CompileElement, CompileNode} from './compile_element';
|
||||
import {CompileMethod} from './compile_method';
|
||||
|
||||
import {LifecycleHooks} from 'angular2/src/core/metadata/lifecycle_hooks';
|
||||
import {isDefaultChangeDetectionStrategy} from 'angular2/src/core/change_detection/constants';
|
||||
import {camelCaseToDashCase} from '../util';
|
||||
|
||||
import {convertCdExpressionToIr} from './expression_converter';
|
||||
|
||||
import {CompileBinding} from './compile_binding';
|
||||
|
||||
function createBindFieldExpr(exprIndex: number): o.ReadPropExpr {
|
||||
return o.THIS_EXPR.prop(`_expr_${exprIndex}`);
|
||||
}
|
||||
|
||||
function createCurrValueExpr(exprIndex: number): o.ReadVarExpr {
|
||||
return o.variable(`currVal_${exprIndex}`);
|
||||
}
|
||||
|
||||
function bind(view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr,
|
||||
parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[],
|
||||
method: CompileMethod) {
|
||||
var checkExpression =
|
||||
convertCdExpressionToIr(view, context, parsedExpression, DetectChangesVars.valUnwrapper);
|
||||
if (isBlank(checkExpression.expression)) {
|
||||
// e.g. an empty expression was given
|
||||
return;
|
||||
}
|
||||
|
||||
// private is fine here as no child view will reference the cached value...
|
||||
view.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
|
||||
view.createMethod.addStmt(
|
||||
o.THIS_EXPR.prop(fieldExpr.name).set(o.importExpr(Identifiers.uninitialized)).toStmt());
|
||||
|
||||
if (checkExpression.needsValueUnwrapper) {
|
||||
var initValueUnwrapperStmt = DetectChangesVars.valUnwrapper.callMethod('reset', []).toStmt();
|
||||
method.addStmt(initValueUnwrapperStmt);
|
||||
}
|
||||
method.addStmt(
|
||||
currValExpr.set(checkExpression.expression).toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
|
||||
var condition: o.Expression =
|
||||
o.importExpr(Identifiers.checkBinding)
|
||||
.callFn([DetectChangesVars.throwOnChange, fieldExpr, currValExpr]);
|
||||
if (checkExpression.needsValueUnwrapper) {
|
||||
condition = DetectChangesVars.valUnwrapper.prop('hasWrappedValue').or(condition);
|
||||
}
|
||||
method.addStmt(new o.IfStmt(
|
||||
condition,
|
||||
actions.concat([<o.Statement>o.THIS_EXPR.prop(fieldExpr.name).set(currValExpr).toStmt()])));
|
||||
}
|
||||
|
||||
export function bindRenderText(boundText: BoundTextAst, compileNode: CompileNode,
|
||||
view: CompileView) {
|
||||
var bindingIndex = view.bindings.length;
|
||||
view.bindings.push(new CompileBinding(compileNode, boundText));
|
||||
var currValExpr = createCurrValueExpr(bindingIndex);
|
||||
var valueField = createBindFieldExpr(bindingIndex);
|
||||
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
|
||||
|
||||
bind(view, currValExpr, valueField, boundText.value, view.componentContext,
|
||||
[
|
||||
o.THIS_EXPR.prop('renderer')
|
||||
.callMethod('setText', [compileNode.renderNode, currValExpr])
|
||||
.toStmt()
|
||||
],
|
||||
view.detectChangesRenderPropertiesMethod);
|
||||
}
|
||||
|
||||
function bindAndWriteToRenderer(boundProps: BoundElementPropertyAst[], context: o.Expression,
|
||||
compileElement: CompileElement) {
|
||||
var view = compileElement.view;
|
||||
var renderNode = compileElement.renderNode;
|
||||
boundProps.forEach((boundProp) => {
|
||||
var bindingIndex = view.bindings.length;
|
||||
view.bindings.push(new CompileBinding(compileElement, boundProp));
|
||||
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
|
||||
var fieldExpr = createBindFieldExpr(bindingIndex);
|
||||
var currValExpr = createCurrValueExpr(bindingIndex);
|
||||
var renderMethod: string;
|
||||
var renderValue: o.Expression = currValExpr;
|
||||
var updateStmts = [];
|
||||
switch (boundProp.type) {
|
||||
case PropertyBindingType.Property:
|
||||
renderMethod = 'setElementProperty';
|
||||
if (view.genConfig.logBindingUpdate) {
|
||||
updateStmts.push(logBindingUpdateStmt(renderNode, boundProp.name, currValExpr));
|
||||
}
|
||||
break;
|
||||
case PropertyBindingType.Attribute:
|
||||
renderMethod = 'setElementAttribute';
|
||||
renderValue =
|
||||
renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
|
||||
break;
|
||||
case PropertyBindingType.Class:
|
||||
renderMethod = 'setElementClass';
|
||||
break;
|
||||
case PropertyBindingType.Style:
|
||||
renderMethod = 'setElementStyle';
|
||||
var strValue: o.Expression = renderValue.callMethod('toString', []);
|
||||
if (isPresent(boundProp.unit)) {
|
||||
strValue = strValue.plus(o.literal(boundProp.unit));
|
||||
}
|
||||
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, strValue);
|
||||
break;
|
||||
}
|
||||
updateStmts.push(
|
||||
o.THIS_EXPR.prop('renderer')
|
||||
.callMethod(renderMethod, [renderNode, o.literal(boundProp.name), renderValue])
|
||||
.toStmt());
|
||||
|
||||
bind(view, currValExpr, fieldExpr, boundProp.value, context, updateStmts,
|
||||
view.detectChangesRenderPropertiesMethod);
|
||||
});
|
||||
}
|
||||
|
||||
export function bindRenderInputs(boundProps: BoundElementPropertyAst[],
|
||||
compileElement: CompileElement): void {
|
||||
bindAndWriteToRenderer(boundProps, compileElement.view.componentContext, compileElement);
|
||||
}
|
||||
|
||||
export function bindDirectiveHostProps(directiveAst: DirectiveAst, directiveInstance: o.Expression,
|
||||
compileElement: CompileElement): void {
|
||||
bindAndWriteToRenderer(directiveAst.hostProperties, directiveInstance, compileElement);
|
||||
}
|
||||
|
||||
export function bindDirectiveInputs(directiveAst: DirectiveAst, directiveInstance: o.Expression,
|
||||
compileElement: CompileElement) {
|
||||
if (directiveAst.inputs.length === 0) {
|
||||
return;
|
||||
}
|
||||
var view = compileElement.view;
|
||||
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
|
||||
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||
|
||||
var lifecycleHooks = directiveAst.directive.lifecycleHooks;
|
||||
var calcChangesMap = lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1;
|
||||
var isOnPushComp = directiveAst.directive.isComponent &&
|
||||
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
|
||||
if (calcChangesMap) {
|
||||
detectChangesInInputsMethod.addStmt(DetectChangesVars.changes.set(o.NULL_EXPR).toStmt());
|
||||
}
|
||||
if (isOnPushComp) {
|
||||
detectChangesInInputsMethod.addStmt(DetectChangesVars.changed.set(o.literal(false)).toStmt());
|
||||
}
|
||||
directiveAst.inputs.forEach((input) => {
|
||||
var bindingIndex = view.bindings.length;
|
||||
view.bindings.push(new CompileBinding(compileElement, input));
|
||||
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
|
||||
var fieldExpr = createBindFieldExpr(bindingIndex);
|
||||
var currValExpr = createCurrValueExpr(bindingIndex);
|
||||
var statements: o.Statement[] =
|
||||
[directiveInstance.prop(input.directiveName).set(currValExpr).toStmt()];
|
||||
if (calcChangesMap) {
|
||||
statements.push(new o.IfStmt(DetectChangesVars.changes.identical(o.NULL_EXPR), [
|
||||
DetectChangesVars.changes.set(o.literalMap([], new o.MapType(
|
||||
o.importType(Identifiers.SimpleChange))))
|
||||
.toStmt()
|
||||
]));
|
||||
statements.push(
|
||||
DetectChangesVars.changes.key(o.literal(input.directiveName))
|
||||
.set(o.importExpr(Identifiers.SimpleChange).instantiate([fieldExpr, currValExpr]))
|
||||
.toStmt());
|
||||
}
|
||||
if (isOnPushComp) {
|
||||
statements.push(DetectChangesVars.changed.set(o.literal(true)).toStmt());
|
||||
}
|
||||
if (view.genConfig.logBindingUpdate) {
|
||||
statements.push(
|
||||
logBindingUpdateStmt(compileElement.renderNode, input.directiveName, currValExpr));
|
||||
}
|
||||
bind(view, currValExpr, fieldExpr, input.value, view.componentContext, statements,
|
||||
detectChangesInInputsMethod);
|
||||
});
|
||||
if (isOnPushComp) {
|
||||
detectChangesInInputsMethod.addStmt(new o.IfStmt(DetectChangesVars.changed, [
|
||||
compileElement.appElement.prop('componentView')
|
||||
.callMethod('markAsCheckOnce', [])
|
||||
.toStmt()
|
||||
]));
|
||||
}
|
||||
}
|
||||
|
||||
function logBindingUpdateStmt(renderNode: o.Expression, propName: string,
|
||||
value: o.Expression): o.Statement {
|
||||
return o.THIS_EXPR.prop('renderer')
|
||||
.callMethod('setBindingDebugInfo',
|
||||
[
|
||||
renderNode,
|
||||
o.literal(`ng-reflect-${camelCaseToDashCase(propName)}`),
|
||||
value.isBlank().conditional(o.NULL_EXPR, value.callMethod('toString', []))
|
||||
])
|
||||
.toStmt();
|
||||
}
|
99
modules/@angular/compiler/src/view_compiler/util.ts
Normal file
99
modules/@angular/compiler/src/view_compiler/util.ts
Normal file
@ -0,0 +1,99 @@
|
||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {BaseException} from 'angular2/src/facade/exceptions';
|
||||
|
||||
import * as o from '../output/output_ast';
|
||||
import {
|
||||
CompileTokenMetadata,
|
||||
CompileDirectiveMetadata,
|
||||
CompileIdentifierMetadata
|
||||
} from '../compile_metadata';
|
||||
import {CompileView} from './compile_view';
|
||||
import {Identifiers} from '../identifiers';
|
||||
|
||||
export function getPropertyInView(property: o.Expression, callingView: CompileView,
|
||||
definedView: CompileView): o.Expression {
|
||||
if (callingView === definedView) {
|
||||
return property;
|
||||
} else {
|
||||
var viewProp: o.Expression = o.THIS_EXPR;
|
||||
var currView: CompileView = callingView;
|
||||
while (currView !== definedView && isPresent(currView.declarationElement.view)) {
|
||||
currView = currView.declarationElement.view;
|
||||
viewProp = viewProp.prop('parent');
|
||||
}
|
||||
if (currView !== definedView) {
|
||||
throw new BaseException(
|
||||
`Internal error: Could not calculate a property in a parent view: ${property}`);
|
||||
}
|
||||
if (property instanceof o.ReadPropExpr) {
|
||||
let readPropExpr: o.ReadPropExpr = property;
|
||||
// Note: Don't cast for members of the AppView base class...
|
||||
if (definedView.fields.some((field) => field.name == readPropExpr.name) ||
|
||||
definedView.getters.some((field) => field.name == readPropExpr.name)) {
|
||||
viewProp = viewProp.cast(definedView.classType);
|
||||
}
|
||||
}
|
||||
return o.replaceVarInExpression(o.THIS_EXPR.name, viewProp, property);
|
||||
}
|
||||
}
|
||||
|
||||
export function injectFromViewParentInjector(token: CompileTokenMetadata,
|
||||
optional: boolean): o.Expression {
|
||||
var args = [createDiTokenExpression(token)];
|
||||
if (optional) {
|
||||
args.push(o.NULL_EXPR);
|
||||
}
|
||||
return o.THIS_EXPR.prop('parentInjector').callMethod('get', args);
|
||||
}
|
||||
|
||||
export function getViewFactoryName(component: CompileDirectiveMetadata,
|
||||
embeddedTemplateIndex: number): string {
|
||||
return `viewFactory_${component.type.name}${embeddedTemplateIndex}`;
|
||||
}
|
||||
|
||||
|
||||
export function createDiTokenExpression(token: CompileTokenMetadata): o.Expression {
|
||||
if (isPresent(token.value)) {
|
||||
return o.literal(token.value);
|
||||
} else if (token.identifierIsInstance) {
|
||||
return o.importExpr(token.identifier)
|
||||
.instantiate([], o.importType(token.identifier, [], [o.TypeModifier.Const]));
|
||||
} else {
|
||||
return o.importExpr(token.identifier);
|
||||
}
|
||||
}
|
||||
|
||||
export function createFlatArray(expressions: o.Expression[]): o.Expression {
|
||||
var lastNonArrayExpressions = [];
|
||||
var result: o.Expression = o.literalArr([]);
|
||||
for (var i = 0; i < expressions.length; i++) {
|
||||
var expr = expressions[i];
|
||||
if (expr.type instanceof o.ArrayType) {
|
||||
if (lastNonArrayExpressions.length > 0) {
|
||||
result =
|
||||
result.callMethod(o.BuiltinMethod.ConcatArray, [o.literalArr(lastNonArrayExpressions)]);
|
||||
lastNonArrayExpressions = [];
|
||||
}
|
||||
result = result.callMethod(o.BuiltinMethod.ConcatArray, [expr]);
|
||||
} else {
|
||||
lastNonArrayExpressions.push(expr);
|
||||
}
|
||||
}
|
||||
if (lastNonArrayExpressions.length > 0) {
|
||||
result =
|
||||
result.callMethod(o.BuiltinMethod.ConcatArray, [o.literalArr(lastNonArrayExpressions)]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function createPureProxy(fn: o.Expression, argCount: number, pureProxyProp: o.ReadPropExpr,
|
||||
view: CompileView) {
|
||||
view.fields.push(new o.ClassField(pureProxyProp.name, null));
|
||||
var pureProxyId =
|
||||
argCount < Identifiers.pureProxies.length ? Identifiers.pureProxies[argCount] : null;
|
||||
if (isBlank(pureProxyId)) {
|
||||
throw new BaseException(`Unsupported number of argument for pure functions: ${argCount}`);
|
||||
}
|
||||
view.createMethod.addStmt(
|
||||
o.THIS_EXPR.prop(pureProxyProp.name).set(o.importExpr(pureProxyId).callFn([fn])).toStmt());
|
||||
}
|
121
modules/@angular/compiler/src/view_compiler/view_binder.ts
Normal file
121
modules/@angular/compiler/src/view_compiler/view_binder.ts
Normal file
@ -0,0 +1,121 @@
|
||||
import {
|
||||
ListWrapper,
|
||||
} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
TemplateAst,
|
||||
TemplateAstVisitor,
|
||||
NgContentAst,
|
||||
EmbeddedTemplateAst,
|
||||
ElementAst,
|
||||
ReferenceAst,
|
||||
VariableAst,
|
||||
BoundEventAst,
|
||||
BoundElementPropertyAst,
|
||||
AttrAst,
|
||||
BoundTextAst,
|
||||
TextAst,
|
||||
DirectiveAst,
|
||||
BoundDirectivePropertyAst,
|
||||
templateVisitAll,
|
||||
PropertyBindingType,
|
||||
ProviderAst
|
||||
} from '../template_ast';
|
||||
import {
|
||||
bindRenderText,
|
||||
bindRenderInputs,
|
||||
bindDirectiveInputs,
|
||||
bindDirectiveHostProps
|
||||
} from './property_binder';
|
||||
import {bindRenderOutputs, collectEventListeners, bindDirectiveOutputs} from './event_binder';
|
||||
import {
|
||||
bindDirectiveAfterContentLifecycleCallbacks,
|
||||
bindDirectiveAfterViewLifecycleCallbacks,
|
||||
bindDirectiveDestroyLifecycleCallbacks,
|
||||
bindPipeDestroyLifecycleCallbacks,
|
||||
bindDirectiveDetectChangesLifecycleCallbacks
|
||||
} from './lifecycle_binder';
|
||||
import {CompileView} from './compile_view';
|
||||
import {CompileElement, CompileNode} from './compile_element';
|
||||
|
||||
export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void {
|
||||
var visitor = new ViewBinderVisitor(view);
|
||||
templateVisitAll(visitor, parsedTemplate);
|
||||
view.pipes.forEach(
|
||||
(pipe) => { bindPipeDestroyLifecycleCallbacks(pipe.meta, pipe.instance, pipe.view); });
|
||||
}
|
||||
|
||||
class ViewBinderVisitor implements TemplateAstVisitor {
|
||||
private _nodeIndex: number = 0;
|
||||
|
||||
constructor(public view: CompileView) {}
|
||||
|
||||
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
|
||||
var node = this.view.nodes[this._nodeIndex++];
|
||||
bindRenderText(ast, node, this.view);
|
||||
return null;
|
||||
}
|
||||
visitText(ast: TextAst, parent: CompileElement): any {
|
||||
this._nodeIndex++;
|
||||
return null;
|
||||
}
|
||||
|
||||
visitNgContent(ast: NgContentAst, parent: CompileElement): any { return null; }
|
||||
|
||||
visitElement(ast: ElementAst, parent: CompileElement): any {
|
||||
var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
|
||||
var eventListeners = collectEventListeners(ast.outputs, ast.directives, compileElement);
|
||||
bindRenderInputs(ast.inputs, compileElement);
|
||||
bindRenderOutputs(eventListeners);
|
||||
ListWrapper.forEachWithIndex(ast.directives, (directiveAst, index) => {
|
||||
var directiveInstance = compileElement.directiveInstances[index];
|
||||
bindDirectiveInputs(directiveAst, directiveInstance, compileElement);
|
||||
bindDirectiveDetectChangesLifecycleCallbacks(directiveAst, directiveInstance, compileElement);
|
||||
|
||||
bindDirectiveHostProps(directiveAst, directiveInstance, compileElement);
|
||||
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
|
||||
});
|
||||
templateVisitAll(this, ast.children, compileElement);
|
||||
// afterContent and afterView lifecycles need to be called bottom up
|
||||
// so that children are notified before parents
|
||||
ListWrapper.forEachWithIndex(ast.directives, (directiveAst, index) => {
|
||||
var directiveInstance = compileElement.directiveInstances[index];
|
||||
bindDirectiveAfterContentLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
||||
compileElement);
|
||||
bindDirectiveAfterViewLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
||||
compileElement);
|
||||
bindDirectiveDestroyLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
||||
compileElement);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: CompileElement): any {
|
||||
var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
|
||||
var eventListeners = collectEventListeners(ast.outputs, ast.directives, compileElement);
|
||||
ListWrapper.forEachWithIndex(ast.directives, (directiveAst, index) => {
|
||||
var directiveInstance = compileElement.directiveInstances[index];
|
||||
bindDirectiveInputs(directiveAst, directiveInstance, compileElement);
|
||||
bindDirectiveDetectChangesLifecycleCallbacks(directiveAst, directiveInstance, compileElement);
|
||||
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
|
||||
bindDirectiveAfterContentLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
||||
compileElement);
|
||||
bindDirectiveAfterViewLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
||||
compileElement);
|
||||
bindDirectiveDestroyLifecycleCallbacks(directiveAst.directive, directiveInstance,
|
||||
compileElement);
|
||||
});
|
||||
bindView(compileElement.embeddedView, ast.children);
|
||||
return null;
|
||||
}
|
||||
|
||||
visitAttr(ast: AttrAst, ctx: any): any { return null; }
|
||||
visitDirective(ast: DirectiveAst, ctx: any): any { return null; }
|
||||
visitEvent(ast: BoundEventAst, eventTargetAndNames: Map<string, BoundEventAst>): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
visitReference(ast: ReferenceAst, ctx: any): any { return null; }
|
||||
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
||||
}
|
580
modules/@angular/compiler/src/view_compiler/view_builder.ts
Normal file
580
modules/@angular/compiler/src/view_compiler/view_builder.ts
Normal file
@ -0,0 +1,580 @@
|
||||
import {isPresent, isBlank, StringWrapper} from 'angular2/src/facade/lang';
|
||||
import {ListWrapper, StringMapWrapper, SetWrapper} from 'angular2/src/facade/collection';
|
||||
|
||||
import * as o from '../output/output_ast';
|
||||
import {Identifiers, identifierToken} from '../identifiers';
|
||||
import {
|
||||
ViewConstructorVars,
|
||||
InjectMethodVars,
|
||||
DetectChangesVars,
|
||||
ViewTypeEnum,
|
||||
ViewEncapsulationEnum,
|
||||
ChangeDetectionStrategyEnum,
|
||||
ViewProperties
|
||||
} from './constants';
|
||||
import {
|
||||
ChangeDetectionStrategy,
|
||||
isDefaultChangeDetectionStrategy
|
||||
} from 'angular2/src/core/change_detection/change_detection';
|
||||
|
||||
import {CompileView} from './compile_view';
|
||||
import {CompileElement, CompileNode} from './compile_element';
|
||||
|
||||
import {
|
||||
TemplateAst,
|
||||
TemplateAstVisitor,
|
||||
NgContentAst,
|
||||
EmbeddedTemplateAst,
|
||||
ElementAst,
|
||||
ReferenceAst,
|
||||
VariableAst,
|
||||
BoundEventAst,
|
||||
BoundElementPropertyAst,
|
||||
AttrAst,
|
||||
BoundTextAst,
|
||||
TextAst,
|
||||
DirectiveAst,
|
||||
BoundDirectivePropertyAst,
|
||||
templateVisitAll,
|
||||
PropertyBindingType,
|
||||
ProviderAst
|
||||
} from '../template_ast';
|
||||
|
||||
import {getViewFactoryName, createFlatArray, createDiTokenExpression} from './util';
|
||||
|
||||
import {ViewType} from 'angular2/src/core/linker/view_type';
|
||||
import {ViewEncapsulation} from 'angular2/src/core/metadata/view';
|
||||
|
||||
import {
|
||||
CompileIdentifierMetadata,
|
||||
CompileDirectiveMetadata,
|
||||
CompileTokenMetadata
|
||||
} from '../compile_metadata';
|
||||
|
||||
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||
const CLASS_ATTR = 'class';
|
||||
const STYLE_ATTR = 'style';
|
||||
|
||||
var parentRenderNodeVar = o.variable('parentRenderNode');
|
||||
var rootSelectorVar = o.variable('rootSelector');
|
||||
|
||||
export class ViewCompileDependency {
|
||||
constructor(public comp: CompileDirectiveMetadata,
|
||||
public factoryPlaceholder: CompileIdentifierMetadata) {}
|
||||
}
|
||||
|
||||
export function buildView(view: CompileView, template: TemplateAst[],
|
||||
targetDependencies: ViewCompileDependency[]): number {
|
||||
var builderVisitor = new ViewBuilderVisitor(view, targetDependencies);
|
||||
templateVisitAll(builderVisitor, template, view.declarationElement.isNull() ?
|
||||
view.declarationElement :
|
||||
view.declarationElement.parent);
|
||||
return builderVisitor.nestedViewCount;
|
||||
}
|
||||
|
||||
export function finishView(view: CompileView, targetStatements: o.Statement[]) {
|
||||
view.afterNodes();
|
||||
createViewTopLevelStmts(view, targetStatements);
|
||||
view.nodes.forEach((node) => {
|
||||
if (node instanceof CompileElement && node.hasEmbeddedView) {
|
||||
finishView(node.embeddedView, targetStatements);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
nestedViewCount: number = 0;
|
||||
|
||||
constructor(public view: CompileView, public targetDependencies: ViewCompileDependency[]) {}
|
||||
|
||||
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
|
||||
|
||||
private _addRootNodeAndProject(node: CompileNode, ngContentIndex: number,
|
||||
parent: CompileElement) {
|
||||
var vcAppEl =
|
||||
(node instanceof CompileElement && node.hasViewContainer) ? node.appElement : null;
|
||||
if (this._isRootNode(parent)) {
|
||||
// store appElement as root node only for ViewContainers
|
||||
if (this.view.viewType !== ViewType.COMPONENT) {
|
||||
this.view.rootNodesOrAppElements.push(isPresent(vcAppEl) ? vcAppEl : node.renderNode);
|
||||
}
|
||||
} else if (isPresent(parent.component) && isPresent(ngContentIndex)) {
|
||||
parent.addContentNode(ngContentIndex, isPresent(vcAppEl) ? vcAppEl : node.renderNode);
|
||||
}
|
||||
}
|
||||
|
||||
private _getParentRenderNode(parent: CompileElement): o.Expression {
|
||||
if (this._isRootNode(parent)) {
|
||||
if (this.view.viewType === ViewType.COMPONENT) {
|
||||
return parentRenderNodeVar;
|
||||
} else {
|
||||
// root node of an embedded/host view
|
||||
return o.NULL_EXPR;
|
||||
}
|
||||
} else {
|
||||
return isPresent(parent.component) &&
|
||||
parent.component.template.encapsulation !== ViewEncapsulation.Native ?
|
||||
o.NULL_EXPR :
|
||||
parent.renderNode;
|
||||
}
|
||||
}
|
||||
|
||||
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
|
||||
return this._visitText(ast, '', ast.ngContentIndex, parent);
|
||||
}
|
||||
visitText(ast: TextAst, parent: CompileElement): any {
|
||||
return this._visitText(ast, ast.value, ast.ngContentIndex, parent);
|
||||
}
|
||||
private _visitText(ast: TemplateAst, value: string, ngContentIndex: number,
|
||||
parent: CompileElement): o.Expression {
|
||||
var fieldName = `_text_${this.view.nodes.length}`;
|
||||
this.view.fields.push(
|
||||
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderText)));
|
||||
var renderNode = o.THIS_EXPR.prop(fieldName);
|
||||
var compileNode = new CompileNode(parent, this.view, this.view.nodes.length, renderNode, ast);
|
||||
var createRenderNode =
|
||||
o.THIS_EXPR.prop(fieldName)
|
||||
.set(ViewProperties.renderer.callMethod(
|
||||
'createText',
|
||||
[
|
||||
this._getParentRenderNode(parent),
|
||||
o.literal(value),
|
||||
this.view.createMethod.resetDebugInfoExpr(this.view.nodes.length, ast)
|
||||
]))
|
||||
.toStmt();
|
||||
this.view.nodes.push(compileNode);
|
||||
this.view.createMethod.addStmt(createRenderNode);
|
||||
this._addRootNodeAndProject(compileNode, ngContentIndex, parent);
|
||||
return renderNode;
|
||||
}
|
||||
|
||||
visitNgContent(ast: NgContentAst, parent: CompileElement): any {
|
||||
// the projected nodes originate from a different view, so we don't
|
||||
// have debug information for them...
|
||||
this.view.createMethod.resetDebugInfo(null, ast);
|
||||
var parentRenderNode = this._getParentRenderNode(parent);
|
||||
var nodesExpression = ViewProperties.projectableNodes.key(
|
||||
o.literal(ast.index),
|
||||
new o.ArrayType(o.importType(this.view.genConfig.renderTypes.renderNode)));
|
||||
if (parentRenderNode !== o.NULL_EXPR) {
|
||||
this.view.createMethod.addStmt(
|
||||
ViewProperties.renderer.callMethod(
|
||||
'projectNodes',
|
||||
[
|
||||
parentRenderNode,
|
||||
o.importExpr(Identifiers.flattenNestedViewRenderNodes)
|
||||
.callFn([nodesExpression])
|
||||
])
|
||||
.toStmt());
|
||||
} else if (this._isRootNode(parent)) {
|
||||
if (this.view.viewType !== ViewType.COMPONENT) {
|
||||
// store root nodes only for embedded/host views
|
||||
this.view.rootNodesOrAppElements.push(nodesExpression);
|
||||
}
|
||||
} else {
|
||||
if (isPresent(parent.component) && isPresent(ast.ngContentIndex)) {
|
||||
parent.addContentNode(ast.ngContentIndex, nodesExpression);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
visitElement(ast: ElementAst, parent: CompileElement): any {
|
||||
var nodeIndex = this.view.nodes.length;
|
||||
var createRenderNodeExpr;
|
||||
var debugContextExpr = this.view.createMethod.resetDebugInfoExpr(nodeIndex, ast);
|
||||
if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) {
|
||||
createRenderNodeExpr = o.THIS_EXPR.callMethod(
|
||||
'selectOrCreateHostElement', [o.literal(ast.name), rootSelectorVar, debugContextExpr]);
|
||||
} else {
|
||||
createRenderNodeExpr = ViewProperties.renderer.callMethod(
|
||||
'createElement',
|
||||
[this._getParentRenderNode(parent), o.literal(ast.name), debugContextExpr]);
|
||||
}
|
||||
var fieldName = `_el_${nodeIndex}`;
|
||||
this.view.fields.push(
|
||||
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderElement)));
|
||||
this.view.createMethod.addStmt(o.THIS_EXPR.prop(fieldName).set(createRenderNodeExpr).toStmt());
|
||||
|
||||
var renderNode = o.THIS_EXPR.prop(fieldName);
|
||||
|
||||
var directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||||
var component = directives.find(directive => directive.isComponent);
|
||||
var htmlAttrs = _readHtmlAttrs(ast.attrs);
|
||||
var attrNameAndValues = _mergeHtmlAndDirectiveAttrs(htmlAttrs, directives);
|
||||
for (var i = 0; i < attrNameAndValues.length; i++) {
|
||||
var attrName = attrNameAndValues[i][0];
|
||||
var attrValue = attrNameAndValues[i][1];
|
||||
this.view.createMethod.addStmt(
|
||||
ViewProperties.renderer.callMethod(
|
||||
'setElementAttribute',
|
||||
[renderNode, o.literal(attrName), o.literal(attrValue)])
|
||||
.toStmt());
|
||||
}
|
||||
var compileElement =
|
||||
new CompileElement(parent, this.view, nodeIndex, renderNode, ast, component, directives,
|
||||
ast.providers, ast.hasViewContainer, false, ast.references);
|
||||
this.view.nodes.push(compileElement);
|
||||
var compViewExpr: o.ReadVarExpr = null;
|
||||
if (isPresent(component)) {
|
||||
var nestedComponentIdentifier =
|
||||
new CompileIdentifierMetadata({name: getViewFactoryName(component, 0)});
|
||||
this.targetDependencies.push(new ViewCompileDependency(component, nestedComponentIdentifier));
|
||||
compViewExpr = o.variable(`compView_${nodeIndex}`);
|
||||
compileElement.setComponentView(compViewExpr);
|
||||
this.view.createMethod.addStmt(compViewExpr.set(o.importExpr(nestedComponentIdentifier)
|
||||
.callFn([
|
||||
ViewProperties.viewUtils,
|
||||
compileElement.injector,
|
||||
compileElement.appElement
|
||||
]))
|
||||
.toDeclStmt());
|
||||
}
|
||||
compileElement.beforeChildren();
|
||||
this._addRootNodeAndProject(compileElement, ast.ngContentIndex, parent);
|
||||
templateVisitAll(this, ast.children, compileElement);
|
||||
compileElement.afterChildren(this.view.nodes.length - nodeIndex - 1);
|
||||
|
||||
if (isPresent(compViewExpr)) {
|
||||
var codeGenContentNodes;
|
||||
if (this.view.component.type.isHost) {
|
||||
codeGenContentNodes = ViewProperties.projectableNodes;
|
||||
} else {
|
||||
codeGenContentNodes = o.literalArr(
|
||||
compileElement.contentNodesByNgContentIndex.map(nodes => createFlatArray(nodes)));
|
||||
}
|
||||
this.view.createMethod.addStmt(
|
||||
compViewExpr.callMethod('create',
|
||||
[compileElement.getComponent(), codeGenContentNodes, o.NULL_EXPR])
|
||||
.toStmt());
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: CompileElement): any {
|
||||
var nodeIndex = this.view.nodes.length;
|
||||
var fieldName = `_anchor_${nodeIndex}`;
|
||||
this.view.fields.push(
|
||||
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderComment)));
|
||||
this.view.createMethod.addStmt(
|
||||
o.THIS_EXPR.prop(fieldName)
|
||||
.set(ViewProperties.renderer.callMethod(
|
||||
'createTemplateAnchor',
|
||||
[
|
||||
this._getParentRenderNode(parent),
|
||||
this.view.createMethod.resetDebugInfoExpr(nodeIndex, ast)
|
||||
]))
|
||||
.toStmt());
|
||||
var renderNode = o.THIS_EXPR.prop(fieldName);
|
||||
|
||||
var templateVariableBindings = ast.variables.map(
|
||||
varAst => [varAst.value.length > 0 ? varAst.value : IMPLICIT_TEMPLATE_VAR, varAst.name]);
|
||||
|
||||
var directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||||
var compileElement =
|
||||
new CompileElement(parent, this.view, nodeIndex, renderNode, ast, null, directives,
|
||||
ast.providers, ast.hasViewContainer, true, ast.references);
|
||||
this.view.nodes.push(compileElement);
|
||||
|
||||
this.nestedViewCount++;
|
||||
var embeddedView = new CompileView(
|
||||
this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR,
|
||||
this.view.viewIndex + this.nestedViewCount, compileElement, templateVariableBindings);
|
||||
this.nestedViewCount += buildView(embeddedView, ast.children, this.targetDependencies);
|
||||
|
||||
compileElement.beforeChildren();
|
||||
this._addRootNodeAndProject(compileElement, ast.ngContentIndex, parent);
|
||||
compileElement.afterChildren(0);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
visitAttr(ast: AttrAst, ctx: any): any { return null; }
|
||||
visitDirective(ast: DirectiveAst, ctx: any): any { return null; }
|
||||
visitEvent(ast: BoundEventAst, eventTargetAndNames: Map<string, BoundEventAst>): any {
|
||||
return null;
|
||||
}
|
||||
|
||||
visitReference(ast: ReferenceAst, ctx: any): any { return null; }
|
||||
visitVariable(ast: VariableAst, ctx: any): any { return null; }
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any { return null; }
|
||||
visitElementProperty(ast: BoundElementPropertyAst, context: any): any { return null; }
|
||||
}
|
||||
|
||||
function _mergeHtmlAndDirectiveAttrs(declaredHtmlAttrs: {[key: string]: string},
|
||||
directives: CompileDirectiveMetadata[]): string[][] {
|
||||
var result: {[key: string]: string} = {};
|
||||
StringMapWrapper.forEach(declaredHtmlAttrs, (value, key) => { result[key] = value; });
|
||||
directives.forEach(directiveMeta => {
|
||||
StringMapWrapper.forEach(directiveMeta.hostAttributes, (value, name) => {
|
||||
var prevValue = result[name];
|
||||
result[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value;
|
||||
});
|
||||
});
|
||||
return mapToKeyValueArray(result);
|
||||
}
|
||||
|
||||
function _readHtmlAttrs(attrs: AttrAst[]): {[key: string]: string} {
|
||||
var htmlAttrs: {[key: string]: string} = {};
|
||||
attrs.forEach((ast) => { htmlAttrs[ast.name] = ast.value; });
|
||||
return htmlAttrs;
|
||||
}
|
||||
|
||||
function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
|
||||
if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
|
||||
return `${attrValue1} ${attrValue2}`;
|
||||
} else {
|
||||
return attrValue2;
|
||||
}
|
||||
}
|
||||
|
||||
function mapToKeyValueArray(data: {[key: string]: string}): string[][] {
|
||||
var entryArray = [];
|
||||
StringMapWrapper.forEach(data, (value, name) => { entryArray.push([name, value]); });
|
||||
// We need to sort to get a defined output order
|
||||
// for tests and for caching generated artifacts...
|
||||
ListWrapper.sort(entryArray, (entry1, entry2) => StringWrapper.compare(entry1[0], entry2[0]));
|
||||
var keyValueArray = [];
|
||||
entryArray.forEach((entry) => { keyValueArray.push([entry[0], entry[1]]); });
|
||||
return keyValueArray;
|
||||
}
|
||||
|
||||
function createViewTopLevelStmts(view: CompileView, targetStatements: o.Statement[]) {
|
||||
var nodeDebugInfosVar: o.Expression = o.NULL_EXPR;
|
||||
if (view.genConfig.genDebugInfo) {
|
||||
nodeDebugInfosVar = o.variable(`nodeDebugInfos_${view.component.type.name}${view.viewIndex}`);
|
||||
targetStatements.push(
|
||||
(<o.ReadVarExpr>nodeDebugInfosVar)
|
||||
.set(o.literalArr(view.nodes.map(createStaticNodeDebugInfo),
|
||||
new o.ArrayType(new o.ExternalType(Identifiers.StaticNodeDebugInfo),
|
||||
[o.TypeModifier.Const])))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
}
|
||||
|
||||
|
||||
var renderCompTypeVar: o.ReadVarExpr = o.variable(`renderType_${view.component.type.name}`);
|
||||
if (view.viewIndex === 0) {
|
||||
targetStatements.push(renderCompTypeVar.set(o.NULL_EXPR)
|
||||
.toDeclStmt(o.importType(Identifiers.RenderComponentType)));
|
||||
}
|
||||
|
||||
var viewClass = createViewClass(view, renderCompTypeVar, nodeDebugInfosVar);
|
||||
targetStatements.push(viewClass);
|
||||
targetStatements.push(createViewFactory(view, viewClass, renderCompTypeVar));
|
||||
}
|
||||
|
||||
function createStaticNodeDebugInfo(node: CompileNode): o.Expression {
|
||||
var compileElement = node instanceof CompileElement ? node : null;
|
||||
var providerTokens: o.Expression[] = [];
|
||||
var componentToken: o.Expression = o.NULL_EXPR;
|
||||
var varTokenEntries = [];
|
||||
if (isPresent(compileElement)) {
|
||||
providerTokens = compileElement.getProviderTokens();
|
||||
if (isPresent(compileElement.component)) {
|
||||
componentToken = createDiTokenExpression(identifierToken(compileElement.component.type));
|
||||
}
|
||||
StringMapWrapper.forEach(compileElement.referenceTokens, (token, varName) => {
|
||||
varTokenEntries.push(
|
||||
[varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]);
|
||||
});
|
||||
}
|
||||
return o.importExpr(Identifiers.StaticNodeDebugInfo)
|
||||
.instantiate(
|
||||
[
|
||||
o.literalArr(providerTokens, new o.ArrayType(o.DYNAMIC_TYPE, [o.TypeModifier.Const])),
|
||||
componentToken,
|
||||
o.literalMap(varTokenEntries, new o.MapType(o.DYNAMIC_TYPE, [o.TypeModifier.Const]))
|
||||
],
|
||||
o.importType(Identifiers.StaticNodeDebugInfo, null, [o.TypeModifier.Const]));
|
||||
}
|
||||
|
||||
function createViewClass(view: CompileView, renderCompTypeVar: o.ReadVarExpr,
|
||||
nodeDebugInfosVar: o.Expression): o.ClassStmt {
|
||||
var viewConstructorArgs = [
|
||||
new o.FnParam(ViewConstructorVars.viewUtils.name, o.importType(Identifiers.ViewUtils)),
|
||||
new o.FnParam(ViewConstructorVars.parentInjector.name, o.importType(Identifiers.Injector)),
|
||||
new o.FnParam(ViewConstructorVars.declarationEl.name, o.importType(Identifiers.AppElement))
|
||||
];
|
||||
var superConstructorArgs = [
|
||||
o.variable(view.className),
|
||||
renderCompTypeVar,
|
||||
ViewTypeEnum.fromValue(view.viewType),
|
||||
ViewConstructorVars.viewUtils,
|
||||
ViewConstructorVars.parentInjector,
|
||||
ViewConstructorVars.declarationEl,
|
||||
ChangeDetectionStrategyEnum.fromValue(getChangeDetectionMode(view))
|
||||
];
|
||||
if (view.genConfig.genDebugInfo) {
|
||||
superConstructorArgs.push(nodeDebugInfosVar);
|
||||
}
|
||||
var viewConstructor = new o.ClassMethod(null, viewConstructorArgs,
|
||||
[o.SUPER_EXPR.callFn(superConstructorArgs).toStmt()]);
|
||||
|
||||
var viewMethods = [
|
||||
new o.ClassMethod('createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)],
|
||||
generateCreateMethod(view), o.importType(Identifiers.AppElement)),
|
||||
new o.ClassMethod(
|
||||
'injectorGetInternal',
|
||||
[
|
||||
new o.FnParam(InjectMethodVars.token.name, o.DYNAMIC_TYPE),
|
||||
// Note: Can't use o.INT_TYPE here as the method in AppView uses number
|
||||
new o.FnParam(InjectMethodVars.requestNodeIndex.name, o.NUMBER_TYPE),
|
||||
new o.FnParam(InjectMethodVars.notFoundResult.name, o.DYNAMIC_TYPE)
|
||||
],
|
||||
addReturnValuefNotEmpty(view.injectorGetMethod.finish(), InjectMethodVars.notFoundResult),
|
||||
o.DYNAMIC_TYPE),
|
||||
new o.ClassMethod('detectChangesInternal',
|
||||
[new o.FnParam(DetectChangesVars.throwOnChange.name, o.BOOL_TYPE)],
|
||||
generateDetectChangesMethod(view)),
|
||||
new o.ClassMethod('dirtyParentQueriesInternal', [], view.dirtyParentQueriesMethod.finish()),
|
||||
new o.ClassMethod('destroyInternal', [], view.destroyMethod.finish())
|
||||
].concat(view.eventHandlerMethods);
|
||||
var superClass = view.genConfig.genDebugInfo ? Identifiers.DebugAppView : Identifiers.AppView;
|
||||
var viewClass = new o.ClassStmt(view.className, o.importExpr(superClass, [getContextType(view)]),
|
||||
view.fields, view.getters, viewConstructor,
|
||||
viewMethods.filter((method) => method.body.length > 0));
|
||||
return viewClass;
|
||||
}
|
||||
|
||||
function createViewFactory(view: CompileView, viewClass: o.ClassStmt,
|
||||
renderCompTypeVar: o.ReadVarExpr): o.Statement {
|
||||
var viewFactoryArgs = [
|
||||
new o.FnParam(ViewConstructorVars.viewUtils.name, o.importType(Identifiers.ViewUtils)),
|
||||
new o.FnParam(ViewConstructorVars.parentInjector.name, o.importType(Identifiers.Injector)),
|
||||
new o.FnParam(ViewConstructorVars.declarationEl.name, o.importType(Identifiers.AppElement))
|
||||
];
|
||||
var initRenderCompTypeStmts = [];
|
||||
var templateUrlInfo;
|
||||
if (view.component.template.templateUrl == view.component.type.moduleUrl) {
|
||||
templateUrlInfo =
|
||||
`${view.component.type.moduleUrl} class ${view.component.type.name} - inline template`;
|
||||
} else {
|
||||
templateUrlInfo = view.component.template.templateUrl;
|
||||
}
|
||||
if (view.viewIndex === 0) {
|
||||
initRenderCompTypeStmts = [
|
||||
new o.IfStmt(renderCompTypeVar.identical(o.NULL_EXPR),
|
||||
[
|
||||
renderCompTypeVar.set(ViewConstructorVars
|
||||
.viewUtils.callMethod('createRenderComponentType',
|
||||
[
|
||||
o.literal(templateUrlInfo),
|
||||
o.literal(view.component
|
||||
.template.ngContentSelectors.length),
|
||||
ViewEncapsulationEnum
|
||||
.fromValue(view.component.template.encapsulation),
|
||||
view.styles
|
||||
]))
|
||||
.toStmt()
|
||||
])
|
||||
];
|
||||
}
|
||||
return o.fn(viewFactoryArgs, initRenderCompTypeStmts.concat([
|
||||
new o.ReturnStatement(o.variable(viewClass.name)
|
||||
.instantiate(viewClass.constructorMethod.params.map(
|
||||
(param) => o.variable(param.name))))
|
||||
]),
|
||||
o.importType(Identifiers.AppView, [getContextType(view)]))
|
||||
.toDeclStmt(view.viewFactory.name, [o.StmtModifier.Final]);
|
||||
}
|
||||
|
||||
function generateCreateMethod(view: CompileView): o.Statement[] {
|
||||
var parentRenderNodeExpr: o.Expression = o.NULL_EXPR;
|
||||
var parentRenderNodeStmts = [];
|
||||
if (view.viewType === ViewType.COMPONENT) {
|
||||
parentRenderNodeExpr = ViewProperties.renderer.callMethod(
|
||||
'createViewRoot', [o.THIS_EXPR.prop('declarationAppElement').prop('nativeElement')]);
|
||||
parentRenderNodeStmts = [
|
||||
parentRenderNodeVar.set(parentRenderNodeExpr)
|
||||
.toDeclStmt(o.importType(view.genConfig.renderTypes.renderNode), [o.StmtModifier.Final])
|
||||
];
|
||||
}
|
||||
var resultExpr: o.Expression;
|
||||
if (view.viewType === ViewType.HOST) {
|
||||
resultExpr = (<CompileElement>view.nodes[0]).appElement;
|
||||
} else {
|
||||
resultExpr = o.NULL_EXPR;
|
||||
}
|
||||
return parentRenderNodeStmts.concat(view.createMethod.finish())
|
||||
.concat([
|
||||
o.THIS_EXPR.callMethod('init',
|
||||
[
|
||||
createFlatArray(view.rootNodesOrAppElements),
|
||||
o.literalArr(view.nodes.map(node => node.renderNode)),
|
||||
o.literalArr(view.disposables),
|
||||
o.literalArr(view.subscriptions)
|
||||
])
|
||||
.toStmt(),
|
||||
new o.ReturnStatement(resultExpr)
|
||||
]);
|
||||
}
|
||||
|
||||
function generateDetectChangesMethod(view: CompileView): o.Statement[] {
|
||||
var stmts = [];
|
||||
if (view.detectChangesInInputsMethod.isEmpty() && view.updateContentQueriesMethod.isEmpty() &&
|
||||
view.afterContentLifecycleCallbacksMethod.isEmpty() &&
|
||||
view.detectChangesRenderPropertiesMethod.isEmpty() &&
|
||||
view.updateViewQueriesMethod.isEmpty() && view.afterViewLifecycleCallbacksMethod.isEmpty()) {
|
||||
return stmts;
|
||||
}
|
||||
ListWrapper.addAll(stmts, view.detectChangesInInputsMethod.finish());
|
||||
stmts.push(
|
||||
o.THIS_EXPR.callMethod('detectContentChildrenChanges', [DetectChangesVars.throwOnChange])
|
||||
.toStmt());
|
||||
var afterContentStmts = view.updateContentQueriesMethod.finish().concat(
|
||||
view.afterContentLifecycleCallbacksMethod.finish());
|
||||
if (afterContentStmts.length > 0) {
|
||||
stmts.push(new o.IfStmt(o.not(DetectChangesVars.throwOnChange), afterContentStmts));
|
||||
}
|
||||
ListWrapper.addAll(stmts, view.detectChangesRenderPropertiesMethod.finish());
|
||||
stmts.push(o.THIS_EXPR.callMethod('detectViewChildrenChanges', [DetectChangesVars.throwOnChange])
|
||||
.toStmt());
|
||||
var afterViewStmts =
|
||||
view.updateViewQueriesMethod.finish().concat(view.afterViewLifecycleCallbacksMethod.finish());
|
||||
if (afterViewStmts.length > 0) {
|
||||
stmts.push(new o.IfStmt(o.not(DetectChangesVars.throwOnChange), afterViewStmts));
|
||||
}
|
||||
|
||||
var varStmts = [];
|
||||
var readVars = o.findReadVarNames(stmts);
|
||||
if (SetWrapper.has(readVars, DetectChangesVars.changed.name)) {
|
||||
varStmts.push(DetectChangesVars.changed.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE));
|
||||
}
|
||||
if (SetWrapper.has(readVars, DetectChangesVars.changes.name)) {
|
||||
varStmts.push(DetectChangesVars.changes.set(o.NULL_EXPR)
|
||||
.toDeclStmt(new o.MapType(o.importType(Identifiers.SimpleChange))));
|
||||
}
|
||||
if (SetWrapper.has(readVars, DetectChangesVars.valUnwrapper.name)) {
|
||||
varStmts.push(
|
||||
DetectChangesVars.valUnwrapper.set(o.importExpr(Identifiers.ValueUnwrapper).instantiate([]))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
}
|
||||
return varStmts.concat(stmts);
|
||||
}
|
||||
|
||||
function addReturnValuefNotEmpty(statements: o.Statement[], value: o.Expression): o.Statement[] {
|
||||
if (statements.length > 0) {
|
||||
return statements.concat([new o.ReturnStatement(value)]);
|
||||
} else {
|
||||
return statements;
|
||||
}
|
||||
}
|
||||
|
||||
function getContextType(view: CompileView): o.Type {
|
||||
if (view.viewType === ViewType.COMPONENT) {
|
||||
return o.importType(view.component.type);
|
||||
}
|
||||
return o.DYNAMIC_TYPE;
|
||||
}
|
||||
|
||||
function getChangeDetectionMode(view: CompileView): ChangeDetectionStrategy {
|
||||
var mode: ChangeDetectionStrategy;
|
||||
if (view.viewType === ViewType.COMPONENT) {
|
||||
mode = isDefaultChangeDetectionStrategy(view.component.changeDetection) ?
|
||||
ChangeDetectionStrategy.CheckAlways :
|
||||
ChangeDetectionStrategy.CheckOnce;
|
||||
} else {
|
||||
mode = ChangeDetectionStrategy.CheckAlways;
|
||||
}
|
||||
return mode;
|
||||
}
|
37
modules/@angular/compiler/src/view_compiler/view_compiler.ts
Normal file
37
modules/@angular/compiler/src/view_compiler/view_compiler.ts
Normal file
@ -0,0 +1,37 @@
|
||||
import {Injectable} from 'angular2/src/core/di';
|
||||
|
||||
import * as o from '../output/output_ast';
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileView} from './compile_view';
|
||||
import {buildView, finishView, ViewCompileDependency} from './view_builder';
|
||||
import {bindView} from './view_binder';
|
||||
|
||||
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
|
||||
|
||||
import {TemplateAst} from '../template_ast';
|
||||
import {CompilerConfig} from '../config';
|
||||
|
||||
export class ViewCompileResult {
|
||||
constructor(public statements: o.Statement[], public viewFactoryVar: string,
|
||||
public dependencies: ViewCompileDependency[]) {}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ViewCompiler {
|
||||
constructor(private _genConfig: CompilerConfig) {}
|
||||
|
||||
compileComponent(component: CompileDirectiveMetadata, template: TemplateAst[],
|
||||
styles: o.Expression, pipes: CompilePipeMetadata[]): ViewCompileResult {
|
||||
var statements = [];
|
||||
var dependencies = [];
|
||||
var view = new CompileView(component, this._genConfig, pipes, styles, 0,
|
||||
CompileElement.createNull(), []);
|
||||
buildView(view, template, dependencies);
|
||||
// Need to separate binding from creation to be able to refer to
|
||||
// variables that have been declared after usage.
|
||||
bindView(view, template);
|
||||
finishView(view, statements);
|
||||
|
||||
return new ViewCompileResult(statements, view.viewFactory.name, dependencies);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user