/** * @license * Copyright Google Inc. All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ import {ViewEncapsulation} from '@angular/core'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata'; import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter'; import {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util'; import {isPresent} from '../facade/lang'; import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers'; import {createClassStmt} from '../output/class_builder'; import * as o from '../output/output_ast'; import {ParseSourceSpan} from '../parse_util'; import {ChangeDetectorStatus, ViewType, isDefaultChangeDetectionStrategy} from '../private_import_core'; import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast'; import {CompileElement, CompileNode} from './compile_element'; import {CompileView, CompileViewRootNode, CompileViewRootNodeType} from './compile_view'; import {ChangeDetectorStatusEnum, DetectChangesVars, InjectMethodVars, ViewConstructorVars, ViewEncapsulationEnum, ViewProperties, ViewTypeEnum} from './constants'; import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewFactoryDependency} from './deps'; import {getViewFactoryName} from './util'; const IMPLICIT_TEMPLATE_VAR = '\$implicit'; const CLASS_ATTR = 'class'; const STYLE_ATTR = 'style'; const NG_CONTAINER_TAG = 'ng-container'; var parentRenderNodeVar = o.variable('parentRenderNode'); var rootSelectorVar = o.variable('rootSelector'); export function buildView( view: CompileView, template: TemplateAst[], targetDependencies: Array): number { var builderVisitor = new ViewBuilderVisitor(view, targetDependencies); const parentEl = view.declarationElement.isNull() ? view.declarationElement : view.declarationElement.parent; templateVisitAll(builderVisitor, template, parentEl); if (view.viewType === ViewType.EMBEDDED || view.viewType === ViewType.HOST) { view.lastRenderNode = builderVisitor.getOrCreateLastRenderNode(); } 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: Array) {} private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; } private _addRootNodeAndProject(node: CompileNode) { var projectedNode = _getOuterContainerOrSelf(node); var parent = projectedNode.parent; var ngContentIndex = (projectedNode.sourceAst).ngContentIndex; var viewContainer = (node instanceof CompileElement && node.hasViewContainer) ? node.viewContainer : null; if (this._isRootNode(parent)) { if (this.view.viewType !== ViewType.COMPONENT) { this.view.rootNodes.push(new CompileViewRootNode( viewContainer ? CompileViewRootNodeType.ViewContainer : CompileViewRootNodeType.Node, viewContainer || node.renderNode)); } } else if (isPresent(parent.component) && isPresent(ngContentIndex)) { parent.addContentNode( ngContentIndex, new CompileViewRootNode( viewContainer ? CompileViewRootNodeType.ViewContainer : CompileViewRootNodeType.Node, viewContainer || node.renderNode)); } } private _getParentRenderNode(parent: CompileElement): o.Expression { parent = _getOuterContainerParentOrSelf(parent); 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; } } getOrCreateLastRenderNode(): o.Expression { const view = this.view; if (view.rootNodes.length === 0 || view.rootNodes[view.rootNodes.length - 1].type !== CompileViewRootNodeType.Node) { var fieldName = `_el_${view.nodes.length}`; view.fields.push( new o.ClassField(fieldName, o.importType(view.genConfig.renderTypes.renderElement))); view.createMethod.addStmt(o.THIS_EXPR.prop(fieldName) .set(ViewProperties.renderer.callMethod( 'createTemplateAnchor', [o.NULL_EXPR, o.NULL_EXPR])) .toStmt()); view.rootNodes.push( new CompileViewRootNode(CompileViewRootNodeType.Node, o.THIS_EXPR.prop(fieldName))); } return view.rootNodes[view.rootNodes.length - 1].expr; } visitBoundText(ast: BoundTextAst, parent: CompileElement): any { return this._visitText(ast, '', parent); } visitText(ast: TextAst, parent: CompileElement): any { return this._visitText(ast, ast.value, parent); } private _visitText(ast: TemplateAst, value: string, 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); 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); if (parentRenderNode !== o.NULL_EXPR) { this.view.createMethod.addStmt( ViewProperties.renderer .callMethod( 'projectNodes', [ parentRenderNode, o.THIS_EXPR.callMethod('projectedNodes', [o.literal(ast.index)]) ]) .toStmt()); } else if (this._isRootNode(parent)) { if (this.view.viewType !== ViewType.COMPONENT) { // store root nodes only for embedded/host views this.view.rootNodes.push( new CompileViewRootNode(CompileViewRootNodeType.NgContent, null, ast.index)); } } else { if (isPresent(parent.component) && isPresent(ast.ngContentIndex)) { parent.addContentNode( ast.ngContentIndex, new CompileViewRootNode(CompileViewRootNodeType.NgContent, null, ast.index)); } } return null; } visitElement(ast: ElementAst, parent: CompileElement): any { var nodeIndex = this.view.nodes.length; var createRenderNodeExpr: o.Expression; var debugContextExpr = this.view.createMethod.resetDebugInfoExpr(nodeIndex, ast); var directives = ast.directives.map(directiveAst => directiveAst.directive); var component = directives.find(directive => directive.isComponent); if (ast.name === NG_CONTAINER_TAG) { createRenderNodeExpr = ViewProperties.renderer.callMethod( 'createTemplateAnchor', [this._getParentRenderNode(parent), debugContextExpr]); } else { const htmlAttrs = _readHtmlAttrs(ast.attrs); const attrNameAndValues = createInlineArray( _mergeHtmlAndDirectiveAttrs(htmlAttrs, directives).map(v => o.literal(v))); if (nodeIndex === 0 && this.view.viewType === ViewType.HOST) { createRenderNodeExpr = o.importExpr(resolveIdentifier(Identifiers.selectOrCreateRenderHostElement)).callFn([ ViewProperties.renderer, o.literal(ast.name), attrNameAndValues, rootSelectorVar, debugContextExpr ]); } else { createRenderNodeExpr = o.importExpr(resolveIdentifier(Identifiers.createRenderElement)).callFn([ ViewProperties.renderer, this._getParentRenderNode(parent), o.literal(ast.name), attrNameAndValues, 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 compileElement = new CompileElement( parent, this.view, nodeIndex, renderNode, ast, component, directives, ast.providers, ast.hasViewContainer, false, ast.references, this.targetDependencies); this.view.nodes.push(compileElement); var compViewExpr: o.ReadPropExpr = null; if (isPresent(component)) { let nestedComponentIdentifier = new CompileIdentifierMetadata({name: getViewFactoryName(component, 0)}); this.targetDependencies.push( new ViewFactoryDependency(component.type, nestedComponentIdentifier)); compViewExpr = o.THIS_EXPR.prop(`compView_${nodeIndex}`); // fix highlighting: ` this.view.fields.push(new o.ClassField( compViewExpr.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.importType(component.type)]))); this.view.viewChildren.push(compViewExpr); compileElement.setComponentView(compViewExpr); this.view.createMethod.addStmt( compViewExpr .set(o.importExpr(nestedComponentIdentifier).callFn([ ViewProperties.viewUtils, o.THIS_EXPR, o.literal(nodeIndex), renderNode ])) .toStmt()); } compileElement.beforeChildren(); this._addRootNodeAndProject(compileElement); templateVisitAll(this, ast.children, compileElement); compileElement.afterChildren(this.view.nodes.length - nodeIndex - 1); if (isPresent(compViewExpr)) { this.view.createMethod.addStmt( compViewExpr.callMethod('create', [compileElement.getComponent()]).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.targetDependencies); 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.animations, this.view.viewIndex + this.nestedViewCount, compileElement, templateVariableBindings); this.nestedViewCount += buildView(embeddedView, ast.children, this.targetDependencies); compileElement.beforeChildren(); this._addRootNodeAndProject(compileElement); 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): 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; } } /** * Walks up the nodes while the direct parent is a container. * * Returns the outer container or the node itself when it is not a direct child of a container. * * @internal */ function _getOuterContainerOrSelf(node: CompileNode): CompileNode { const view = node.view; while (_isNgContainer(node.parent, view)) { node = node.parent; } return node; } /** * Walks up the nodes while they are container and returns the first parent which is not. * * Returns the parent of the outer container or the node itself when it is not a container. * * @internal */ function _getOuterContainerParentOrSelf(el: CompileElement): CompileElement { const view = el.view; while (_isNgContainer(el, view)) { el = el.parent; } return el; } function _isNgContainer(node: CompileNode, view: CompileView): boolean { return !node.isNull() && (node.sourceAst).name === NG_CONTAINER_TAG && node.view === view; } function _mergeHtmlAndDirectiveAttrs( declaredHtmlAttrs: {[key: string]: string}, directives: CompileDirectiveMetadata[]): string[] { const mapResult: {[key: string]: string} = {}; Object.keys(declaredHtmlAttrs).forEach(key => { mapResult[key] = declaredHtmlAttrs[key]; }); directives.forEach(directiveMeta => { Object.keys(directiveMeta.hostAttributes).forEach(name => { const value = directiveMeta.hostAttributes[name]; const prevValue = mapResult[name]; mapResult[name] = isPresent(prevValue) ? mergeAttributeValue(name, prevValue, value) : value; }); }); const arrResult: string[] = []; // Note: We need to sort to get a defined output order // for tests and for caching generated artifacts... Object.keys(mapResult).sort().forEach( (attrName) => { arrResult.push(attrName, mapResult[attrName]); }); return arrResult; } 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 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}`); // fix highlighting: ` targetStatements.push( (nodeDebugInfosVar) .set(o.literalArr( view.nodes.map(createStaticNodeDebugInfo), new o.ArrayType( new o.ExternalType(resolveIdentifier(Identifiers.StaticNodeDebugInfo)), [o.TypeModifier.Const]))) .toDeclStmt(null, [o.StmtModifier.Final])); } var renderCompTypeVar: o.ReadVarExpr = o.variable(`renderType_${view.component.type.name}`); // fix highlighting: ` if (view.viewIndex === 0) { let templateUrlInfo: string; 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; } targetStatements.push( renderCompTypeVar .set(o.importExpr(resolveIdentifier(Identifiers.createRenderComponentType)).callFn([ view.genConfig.genDebugInfo ? o.literal(templateUrlInfo) : o.literal(''), o.literal(view.component.template.ngContentSelectors.length), ViewEncapsulationEnum.fromValue(view.component.template.encapsulation), view.styles, o.literalMap(view.animations.map( (entry): [string, o.Expression] => [entry.name, entry.fnExp])), ])) .toDeclStmt(o.importType(resolveIdentifier(Identifiers.RenderComponentType)))); } var viewClass = createViewClass(view, renderCompTypeVar, nodeDebugInfosVar); targetStatements.push(viewClass); targetStatements.push(createViewFactory(view, viewClass)); } 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: any[] = []; if (isPresent(compileElement)) { providerTokens = compileElement.getProviderTokens(); if (isPresent(compileElement.component)) { componentToken = createDiTokenExpression(identifierToken(compileElement.component.type)); } Object.keys(compileElement.referenceTokens).forEach(varName => { const token = compileElement.referenceTokens[varName]; varTokenEntries.push( [varName, isPresent(token) ? createDiTokenExpression(token) : o.NULL_EXPR]); }); } return o.importExpr(resolveIdentifier(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( resolveIdentifier(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(resolveIdentifier(Identifiers.ViewUtils))), new o.FnParam( ViewConstructorVars.parentView.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])), new o.FnParam(ViewConstructorVars.parentIndex.name, o.NUMBER_TYPE), new o.FnParam(ViewConstructorVars.parentElement.name, o.DYNAMIC_TYPE) ]; var superConstructorArgs = [ o.variable(view.className), renderCompTypeVar, ViewTypeEnum.fromValue(view.viewType), ViewConstructorVars.viewUtils, ViewConstructorVars.parentView, ViewConstructorVars.parentIndex, ViewConstructorVars.parentElement, ChangeDetectorStatusEnum.fromValue(getChangeDetectionMode(view)) ]; if (view.genConfig.genDebugInfo) { superConstructorArgs.push(nodeDebugInfosVar); } var viewMethods = [ new o.ClassMethod( 'createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)], generateCreateMethod(view), o.importType(resolveIdentifier(Identifiers.ComponentRef), [o.DYNAMIC_TYPE])), 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', [], generateDestroyMethod(view)), new o.ClassMethod('detachInternal', [], view.detachMethod.finish()), generateVisitRootNodesMethod(view), generateVisitProjectableNodesMethod(view), generateCreateEmbeddedViewsMethod(view) ].filter((method) => method.body.length > 0); var superClass = view.genConfig.genDebugInfo ? Identifiers.DebugAppView : Identifiers.AppView; var viewClass = createClassStmt({ name: view.className, parent: o.importExpr(resolveIdentifier(superClass), [getContextType(view)]), parentArgs: superConstructorArgs, ctorParams: viewConstructorArgs, builders: [{methods: viewMethods}, view] }); return viewClass; } function generateDestroyMethod(view: CompileView): o.Statement[] { const stmts: o.Statement[] = []; view.viewContainers.forEach((viewContainer) => { stmts.push(viewContainer.callMethod('destroyNestedViews', []).toStmt()); }); view.viewChildren.forEach( (viewChild) => { stmts.push(viewChild.callMethod('destroy', []).toStmt()); }); stmts.push(...view.destroyMethod.finish()); return stmts; } function createViewFactory(view: CompileView, viewClass: o.ClassStmt): o.Statement { var viewFactoryArgs = [ new o.FnParam( ViewConstructorVars.viewUtils.name, o.importType(resolveIdentifier(Identifiers.ViewUtils))), new o.FnParam( ViewConstructorVars.parentView.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])), new o.FnParam(ViewConstructorVars.parentIndex.name, o.NUMBER_TYPE), new o.FnParam(ViewConstructorVars.parentElement.name, o.DYNAMIC_TYPE) ]; return o .fn(viewFactoryArgs, [ new o.ReturnStatement(o.variable(viewClass.name) .instantiate(viewClass.constructorMethod.params.map( (param) => o.variable(param.name)))), ], o.importType(resolveIdentifier(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: any[] = []; if (view.viewType === ViewType.COMPONENT) { parentRenderNodeExpr = ViewProperties.renderer.callMethod('createViewRoot', [o.THIS_EXPR.prop('parentElement')]); parentRenderNodeStmts = [parentRenderNodeVar.set(parentRenderNodeExpr) .toDeclStmt( o.importType(view.genConfig.renderTypes.renderNode), [o.StmtModifier.Final])]; } var resultExpr: o.Expression; if (view.viewType === ViewType.HOST) { const hostEl = view.nodes[0]; resultExpr = o.importExpr(resolveIdentifier(Identifiers.ComponentRef_), [o.DYNAMIC_TYPE]).instantiate([ o.literal(hostEl.nodeIndex), o.THIS_EXPR, hostEl.renderNode, hostEl.getComponent() ]); } else { resultExpr = o.NULL_EXPR; } return parentRenderNodeStmts.concat(view.createMethod.finish(), [ o.THIS_EXPR .callMethod( 'init', [ view.lastRenderNode, o.literalArr(view.nodes.map(node => node.renderNode)), view.disposables.length ? o.literalArr(view.disposables) : o.NULL_EXPR, ]) .toStmt(), new o.ReturnStatement(resultExpr) ]); } function generateDetectChangesMethod(view: CompileView): o.Statement[] { var stmts: any[] = []; if (view.animationBindingsMethod.isEmpty() && view.detectChangesInInputsMethod.isEmpty() && view.updateContentQueriesMethod.isEmpty() && view.afterContentLifecycleCallbacksMethod.isEmpty() && view.detectChangesRenderPropertiesMethod.isEmpty() && view.updateViewQueriesMethod.isEmpty() && view.afterViewLifecycleCallbacksMethod.isEmpty()) { return stmts; } stmts.push(...view.animationBindingsMethod.finish()); stmts.push(...view.detectChangesInInputsMethod.finish()); view.viewContainers.forEach((viewContainer) => { stmts.push( viewContainer.callMethod('detectChangesInNestedViews', [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)); } stmts.push(...view.detectChangesRenderPropertiesMethod.finish()); view.viewChildren.forEach((viewChild) => { stmts.push(viewChild.callMethod('detectChanges', [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: any[] = []; var readVars = o.findReadVarNames(stmts); if (readVars.has(DetectChangesVars.changed.name)) { varStmts.push(DetectChangesVars.changed.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE)); } if (readVars.has(DetectChangesVars.changes.name)) { varStmts.push( DetectChangesVars.changes.set(o.NULL_EXPR) .toDeclStmt(new o.MapType(o.importType(resolveIdentifier(Identifiers.SimpleChange))))); } varStmts.push(...createSharedBindingVariablesIfNeeded(stmts)); 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): ChangeDetectorStatus { var mode: ChangeDetectorStatus; if (view.viewType === ViewType.COMPONENT) { mode = isDefaultChangeDetectionStrategy(view.component.changeDetection) ? ChangeDetectorStatus.CheckAlways : ChangeDetectorStatus.CheckOnce; } else { mode = ChangeDetectorStatus.CheckAlways; } return mode; } function generateVisitRootNodesMethod(view: CompileView): o.ClassMethod { const cbVar = o.variable('cb'); const ctxVar = o.variable('ctx'); const stmts: o.Statement[] = generateVisitNodesStmts(view.rootNodes, cbVar, ctxVar); return new o.ClassMethod( 'visitRootNodesInternal', [new o.FnParam(cbVar.name, o.DYNAMIC_TYPE), new o.FnParam(ctxVar.name, o.DYNAMIC_TYPE)], stmts); } function generateVisitProjectableNodesMethod(view: CompileView): o.ClassMethod { const nodeIndexVar = o.variable('nodeIndex'); const ngContentIndexVar = o.variable('ngContentIndex'); const cbVar = o.variable('cb'); const ctxVar = o.variable('ctx'); const stmts: o.Statement[] = []; view.nodes.forEach((node) => { if (node instanceof CompileElement && node.component) { node.contentNodesByNgContentIndex.forEach((projectedNodes, ngContentIndex) => { stmts.push(new o.IfStmt( nodeIndexVar.equals(o.literal(node.nodeIndex)) .and(ngContentIndexVar.equals(o.literal(ngContentIndex))), generateVisitNodesStmts(projectedNodes, cbVar, ctxVar))); }); } }); return new o.ClassMethod( 'visitProjectableNodesInternal', [ new o.FnParam(nodeIndexVar.name, o.NUMBER_TYPE), new o.FnParam(ngContentIndexVar.name, o.NUMBER_TYPE), new o.FnParam(cbVar.name, o.DYNAMIC_TYPE), new o.FnParam(ctxVar.name, o.DYNAMIC_TYPE) ], stmts); } function generateVisitNodesStmts( nodes: CompileViewRootNode[], cb: o.Expression, ctx: o.Expression): o.Statement[] { const stmts: o.Statement[] = []; nodes.forEach((node) => { switch (node.type) { case CompileViewRootNodeType.Node: stmts.push(cb.callFn([node.expr, ctx]).toStmt()); break; case CompileViewRootNodeType.ViewContainer: stmts.push(cb.callFn([node.expr.prop('nativeElement'), ctx]).toStmt()); stmts.push(node.expr.callMethod('visitNestedViewRootNodes', [cb, ctx]).toStmt()); break; case CompileViewRootNodeType.NgContent: stmts.push( o.THIS_EXPR.callMethod('visitProjectedNodes', [o.literal(node.ngContentIndex), cb, ctx]) .toStmt()); break; } }); return stmts; } function generateCreateEmbeddedViewsMethod(view: CompileView) { const nodeIndexVar = o.variable('nodeIndex'); const stmts: o.Statement[] = []; view.nodes.forEach((node) => { if (node instanceof CompileElement) { if (node.embeddedView) { const parentNodeIndex = node.isRootElement() ? null : node.parent.nodeIndex; stmts.push(new o.IfStmt( nodeIndexVar.equals(o.literal(node.nodeIndex)), [new o.ReturnStatement(node.embeddedView.viewFactory.callFn([ ViewProperties.viewUtils, o.THIS_EXPR, o.literal(node.nodeIndex), node.renderNode ]))])); } } }); stmts.push(new o.ReturnStatement(o.NULL_EXPR)); return new o.ClassMethod( 'createEmbeddedViewInternal', [new o.FnParam(nodeIndexVar.name, o.NUMBER_TYPE)], stmts, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])); }