602 lines
26 KiB
TypeScript
602 lines
26 KiB
TypeScript
|
import {isPresent, 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,
|
||
|
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 {HOST_VIEW_ELEMENT_NAME} from 'angular2/src/core/linker/view';
|
||
|
|
||
|
import {
|
||
|
CompileIdentifierMetadata,
|
||
|
CompileDirectiveMetadata,
|
||
|
CompileTokenMetadata
|
||
|
} from '../compile_metadata';
|
||
|
|
||
|
import {bindView} from './view_binder';
|
||
|
|
||
|
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[],
|
||
|
targetStatements: o.Statement[]): number {
|
||
|
var builderVisitor = new ViewBuilderVisitor(view, targetDependencies, targetStatements);
|
||
|
templateVisitAll(builderVisitor, template, view.declarationElement.isNull() ?
|
||
|
view.declarationElement :
|
||
|
view.declarationElement.parent);
|
||
|
// Need to separate binding from creation to be able to refer to
|
||
|
// variables that have been declared after usage.
|
||
|
bindView(view, template);
|
||
|
view.afterNodes();
|
||
|
|
||
|
createViewTopLevelStmts(view, targetStatements);
|
||
|
|
||
|
return builderVisitor.nestedViewCount;
|
||
|
}
|
||
|
|
||
|
|
||
|
class ViewBuilderVisitor implements TemplateAstVisitor {
|
||
|
nestedViewCount: number = 0;
|
||
|
|
||
|
constructor(public view: CompileView, public targetDependencies: ViewCompileDependency[],
|
||
|
public targetStatements: o.Statement[]) {}
|
||
|
|
||
|
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
|
||
|
|
||
|
private _addRootNodeAndProject(node: CompileNode, ngContentIndex: number,
|
||
|
parent: CompileElement) {
|
||
|
var appEl = node instanceof CompileElement ? node.getOptionalAppElement() : null;
|
||
|
if (this._isRootNode(parent)) {
|
||
|
// store root nodes only for embedded/host views
|
||
|
if (this.view.viewType !== ViewType.COMPONENT) {
|
||
|
this.view.rootNodesOrAppElements.push(isPresent(appEl) ? appEl : node.renderNode);
|
||
|
}
|
||
|
} else if (isPresent(parent.component) && isPresent(ngContentIndex)) {
|
||
|
parent.addContentNode(ngContentIndex, isPresent(appEl) ? appEl : 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),
|
||
|
[o.StmtModifier.Private]));
|
||
|
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);
|
||
|
var createElementExpr = ViewProperties.renderer.callMethod(
|
||
|
'createElement',
|
||
|
[this._getParentRenderNode(parent), o.literal(ast.name), debugContextExpr]);
|
||
|
if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) {
|
||
|
createRenderNodeExpr =
|
||
|
rootSelectorVar.identical(o.NULL_EXPR)
|
||
|
.conditional(createElementExpr,
|
||
|
ViewProperties.renderer.callMethod('selectRootElement',
|
||
|
[rootSelectorVar, debugContextExpr]));
|
||
|
} else {
|
||
|
createRenderNodeExpr = createElementExpr;
|
||
|
}
|
||
|
var fieldName = `_el_${nodeIndex}`;
|
||
|
this.view.fields.push(
|
||
|
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderElement),
|
||
|
[o.StmtModifier.Private]));
|
||
|
var createRenderNode = o.THIS_EXPR.prop(fieldName).set(createRenderNodeExpr).toStmt();
|
||
|
|
||
|
var renderNode = o.THIS_EXPR.prop(fieldName);
|
||
|
|
||
|
var component = ast.getComponent();
|
||
|
var directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||
|
var variables =
|
||
|
_readHtmlAndDirectiveVariables(ast.exportAsVars, ast.directives, this.view.viewType);
|
||
|
this.view.createMethod.addStmt(createRenderNode);
|
||
|
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,
|
||
|
directives, ast.providers, variables);
|
||
|
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}`);
|
||
|
this.view.createMethod.addStmt(compViewExpr.set(o.importExpr(nestedComponentIdentifier)
|
||
|
.callFn([
|
||
|
ViewProperties.viewManager,
|
||
|
compileElement.getOrCreateInjector(),
|
||
|
compileElement.getOrCreateAppElement()
|
||
|
]))
|
||
|
.toDeclStmt());
|
||
|
compileElement.setComponent(component, compViewExpr);
|
||
|
}
|
||
|
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', [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),
|
||
|
[o.StmtModifier.Private]));
|
||
|
var createRenderNode = 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.vars.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,
|
||
|
directives, ast.providers, {});
|
||
|
this.view.nodes.push(compileElement);
|
||
|
this.view.createMethod.addStmt(createRenderNode);
|
||
|
|
||
|
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, this.targetStatements);
|
||
|
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
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 _readHtmlAndDirectiveVariables(elementExportAsVars: VariableAst[],
|
||
|
directives: DirectiveAst[],
|
||
|
viewType: ViewType): {[key: string]: CompileTokenMetadata} {
|
||
|
var variables: {[key: string]: CompileTokenMetadata} = {};
|
||
|
var component: CompileDirectiveMetadata = null;
|
||
|
directives.forEach((directive) => {
|
||
|
if (directive.directive.isComponent) {
|
||
|
component = directive.directive;
|
||
|
}
|
||
|
directive.exportAsVars.forEach(
|
||
|
varAst => { variables[varAst.name] = identifierToken(directive.directive.type); });
|
||
|
});
|
||
|
elementExportAsVars.forEach((varAst) => {
|
||
|
variables[varAst.name] = isPresent(component) ? identifierToken(component.type) : null;
|
||
|
});
|
||
|
if (viewType === ViewType.HOST) {
|
||
|
variables[HOST_VIEW_ELEMENT_NAME] = null;
|
||
|
}
|
||
|
return variables;
|
||
|
}
|
||
|
|
||
|
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.variableTokens, (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 emptyTemplateVariableBindings =
|
||
|
view.templateVariableBindings.map((entry) => [entry[0], o.NULL_EXPR]);
|
||
|
var viewConstructorArgs = [
|
||
|
new o.FnParam(ViewConstructorVars.viewManager.name, o.importType(Identifiers.AppViewManager_)),
|
||
|
new o.FnParam(ViewConstructorVars.parentInjector.name, o.importType(Identifiers.Injector)),
|
||
|
new o.FnParam(ViewConstructorVars.declarationEl.name, o.importType(Identifiers.AppElement))
|
||
|
];
|
||
|
var viewConstructor = new o.ClassMethod(null, viewConstructorArgs, [
|
||
|
o.SUPER_EXPR.callFn([
|
||
|
o.variable(view.className),
|
||
|
renderCompTypeVar,
|
||
|
ViewTypeEnum.fromValue(view.viewType),
|
||
|
o.literalMap(emptyTemplateVariableBindings),
|
||
|
ViewConstructorVars.viewManager,
|
||
|
ViewConstructorVars.parentInjector,
|
||
|
ViewConstructorVars.declarationEl,
|
||
|
ChangeDetectionStrategyEnum.fromValue(getChangeDetectionMode(view)),
|
||
|
o.literal(view.literalArrayCount),
|
||
|
o.literal(view.literalMapCount),
|
||
|
nodeDebugInfosVar
|
||
|
])
|
||
|
.toStmt()
|
||
|
]);
|
||
|
|
||
|
var viewMethods = [
|
||
|
new o.ClassMethod('createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)],
|
||
|
generateCreateMethod(view)),
|
||
|
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 viewClass = new o.ClassStmt(
|
||
|
view.className, o.importExpr(Identifiers.AppView, [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.viewManager.name, o.importType(Identifiers.AppViewManager_)),
|
||
|
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.viewManager
|
||
|
.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])
|
||
|
];
|
||
|
}
|
||
|
return parentRenderNodeStmts.concat(view.createMethod.finish())
|
||
|
.concat([
|
||
|
o.THIS_EXPR.callMethod('init',
|
||
|
[
|
||
|
createFlatArray(view.rootNodesOrAppElements),
|
||
|
o.literalArr(view.nodes.map(node => node.renderNode)),
|
||
|
o.literalMap(view.namedAppElements),
|
||
|
o.literalArr(view.disposables),
|
||
|
o.literalArr(view.subscriptions)
|
||
|
])
|
||
|
.toStmt()
|
||
|
]);
|
||
|
}
|
||
|
|
||
|
function generateDetectChangesMethod(view: CompileView): o.Statement[] {
|
||
|
var stmts = [];
|
||
|
if (view.detectChangesInInputsMethod.isEmpty() && view.updateContentQueriesMethod.isEmpty() &&
|
||
|
view.afterContentLifecycleCallbacksMethod.isEmpty() &&
|
||
|
view.detectChangesHostPropertiesMethod.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.detectChangesHostPropertiesMethod.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 {
|
||
|
var typeMeta = view.component.type;
|
||
|
return typeMeta.isHost ? o.DYNAMIC_TYPE : o.importType(typeMeta);
|
||
|
}
|
||
|
|
||
|
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;
|
||
|
}
|