perf: delete pre-view-engine core, compiler, platform-browser, etc code (#14788)
After the introduction of the view engine, we can drop a lot of code that is not used any more. This should reduce the size of the app bundles because a lot of this code was not being properly tree-shaken by today's tools even though it was dead code.
This commit is contained in:
@ -1,402 +0,0 @@
|
||||
/**
|
||||
* @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 {CompileDiDependencyMetadata, CompileDirectiveSummary, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, tokenName, tokenReference} from '../compile_metadata';
|
||||
import {createDiTokenExpression} from '../compiler_util/identifier_util';
|
||||
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, createIdentifier, createIdentifierToken, identifierToken, resolveIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {convertValueToOutputAst} from '../output/value_util';
|
||||
import {ProviderAst, ProviderAstType, ReferenceAst, TemplateAst} from '../template_parser/template_ast';
|
||||
|
||||
import {CompileMethod} from './compile_method';
|
||||
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
||||
import {CompileView, CompileViewRootNode} from './compile_view';
|
||||
import {InjectMethodVars} from './constants';
|
||||
import {ComponentFactoryDependency, DirectiveWrapperDependency} from './deps';
|
||||
import {getPropertyInView, injectFromViewParentInjector} 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 !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, []);
|
||||
}
|
||||
|
||||
public compViewExpr: o.Expression = null;
|
||||
public viewContainer: o.ReadPropExpr;
|
||||
public elementRef: o.Expression;
|
||||
public instances = new Map<any, o.Expression>();
|
||||
public directiveWrapperInstance = new Map<any, o.Expression>();
|
||||
private _resolvedProviders: Map<any, ProviderAst>;
|
||||
|
||||
private _queryCount = 0;
|
||||
private _queries = new Map<any, CompileQuery[]>();
|
||||
|
||||
public contentNodesByNgContentIndex: Array<CompileViewRootNode>[] = null;
|
||||
public embeddedView: CompileView;
|
||||
public referenceTokens: {[key: string]: CompileTokenMetadata};
|
||||
|
||||
constructor(
|
||||
parent: CompileElement, view: CompileView, nodeIndex: number, renderNode: o.Expression,
|
||||
sourceAst: TemplateAst, public component: CompileDirectiveSummary,
|
||||
private _directives: CompileDirectiveSummary[],
|
||||
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(createIdentifier(Identifiers.ElementRef)).instantiate([this.renderNode]);
|
||||
this.instances.set(resolveIdentifier(Identifiers.ElementRef), this.elementRef);
|
||||
this.instances.set(
|
||||
resolveIdentifier(Identifiers.Injector),
|
||||
o.THIS_EXPR.callMethod('injector', [o.literal(this.nodeIndex)]));
|
||||
this.instances.set(resolveIdentifier(Identifiers.Renderer), o.THIS_EXPR.prop('renderer'));
|
||||
if (this.hasViewContainer || this.hasEmbeddedView) {
|
||||
this._createViewContainer();
|
||||
}
|
||||
if (this.component) {
|
||||
this._createComponentFactoryResolver();
|
||||
}
|
||||
}
|
||||
|
||||
private _createViewContainer() {
|
||||
const fieldName = `_vc_${this.nodeIndex}`;
|
||||
const parentNodeIndex = this.isRootElement() ? null : this.parent.nodeIndex;
|
||||
// private is fine here as no child view will reference a ViewContainer
|
||||
this.view.fields.push(new o.ClassField(
|
||||
fieldName, o.importType(createIdentifier(Identifiers.ViewContainer)),
|
||||
[o.StmtModifier.Private]));
|
||||
const statement =
|
||||
o.THIS_EXPR.prop(fieldName)
|
||||
.set(o.importExpr(createIdentifier(Identifiers.ViewContainer)).instantiate([
|
||||
o.literal(this.nodeIndex), o.literal(parentNodeIndex), o.THIS_EXPR, this.renderNode
|
||||
]))
|
||||
.toStmt();
|
||||
this.view.createMethod.addStmt(statement);
|
||||
this.viewContainer = o.THIS_EXPR.prop(fieldName);
|
||||
this.instances.set(resolveIdentifier(Identifiers.ViewContainer), this.viewContainer);
|
||||
this.view.viewContainers.push(this.viewContainer);
|
||||
}
|
||||
|
||||
private _createComponentFactoryResolver() {
|
||||
const entryComponents = this.component.entryComponents.map((entryComponent) => {
|
||||
this.view.targetDependencies.push(
|
||||
new ComponentFactoryDependency(entryComponent.componentType));
|
||||
return {reference: entryComponent.componentFactory};
|
||||
});
|
||||
if (!entryComponents || entryComponents.length === 0) {
|
||||
return;
|
||||
}
|
||||
const createComponentFactoryResolverExpr =
|
||||
o.importExpr(createIdentifier(Identifiers.CodegenComponentFactoryResolver)).instantiate([
|
||||
o.literalArr(entryComponents.map((entryComponent) => o.importExpr(entryComponent))),
|
||||
injectFromViewParentInjector(
|
||||
this.view, createIdentifierToken(Identifiers.ComponentFactoryResolver), false)
|
||||
]);
|
||||
const provider: CompileProviderMetadata = {
|
||||
token: createIdentifierToken(Identifiers.ComponentFactoryResolver),
|
||||
useValue: createComponentFactoryResolverExpr
|
||||
};
|
||||
// Add ComponentFactoryResolver as first provider as it does not have deps on other providers
|
||||
// ProviderAstType.PrivateService as only the component and its view can see it,
|
||||
// but nobody else
|
||||
this._resolvedProvidersArray.unshift(new ProviderAst(
|
||||
provider.token, false, true, [provider], ProviderAstType.PrivateService, [],
|
||||
this.sourceAst.sourceSpan));
|
||||
}
|
||||
|
||||
setComponentView(compViewExpr: o.Expression) {
|
||||
this.compViewExpr = compViewExpr;
|
||||
this.contentNodesByNgContentIndex =
|
||||
new Array(this.component.template.ngContentSelectors.length);
|
||||
for (let i = 0; i < this.contentNodesByNgContentIndex.length; i++) {
|
||||
this.contentNodesByNgContentIndex[i] = [];
|
||||
}
|
||||
}
|
||||
|
||||
setEmbeddedView(embeddedView: CompileView) {
|
||||
this.embeddedView = embeddedView;
|
||||
if (isPresent(embeddedView)) {
|
||||
const createTemplateRefExpr =
|
||||
o.importExpr(createIdentifier(Identifiers.TemplateRef_)).instantiate([
|
||||
o.THIS_EXPR, o.literal(this.nodeIndex), this.renderNode
|
||||
]);
|
||||
const provider: CompileProviderMetadata = {
|
||||
token: createIdentifierToken(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.set(
|
||||
resolveIdentifier(Identifiers.ViewContainerRef), this.viewContainer.prop('vcRef'));
|
||||
}
|
||||
|
||||
this._resolvedProviders = new Map<any, ProviderAst>();
|
||||
this._resolvedProvidersArray.forEach(
|
||||
provider => this._resolvedProviders.set(tokenReference(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.
|
||||
Array.from(this._resolvedProviders.values()).forEach((resolvedProvider) => {
|
||||
const isDirectiveWrapper = resolvedProvider.providerType === ProviderAstType.Component ||
|
||||
resolvedProvider.providerType === ProviderAstType.Directive;
|
||||
const providerValueExpressions = resolvedProvider.providers.map((provider) => {
|
||||
if (provider.useExisting) {
|
||||
return this._getDependency(resolvedProvider.providerType, {token: provider.useExisting});
|
||||
} else if (provider.useFactory) {
|
||||
const deps = provider.deps || provider.useFactory.diDeps;
|
||||
const depsExpr =
|
||||
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
||||
return o.importExpr(provider.useFactory).callFn(depsExpr);
|
||||
} else if (provider.useClass) {
|
||||
const deps = provider.deps || provider.useClass.diDeps;
|
||||
const depsExpr =
|
||||
deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
|
||||
if (isDirectiveWrapper) {
|
||||
const dirMeta =
|
||||
this._directives.find(dir => dir.type.reference === provider.useClass.reference);
|
||||
this.view.targetDependencies.push(
|
||||
new DirectiveWrapperDependency(dirMeta.type.reference));
|
||||
return DirectiveWrapperExpressions.create({reference: dirMeta.wrapperType}, depsExpr);
|
||||
} else {
|
||||
return o.importExpr(provider.useClass)
|
||||
.instantiate(depsExpr, o.importType(provider.useClass));
|
||||
}
|
||||
} else {
|
||||
return convertValueToOutputAst(provider.useValue);
|
||||
}
|
||||
});
|
||||
const propName =
|
||||
`_${tokenName(resolvedProvider.token)}_${this.nodeIndex}_${this.instances.size}`;
|
||||
const instance = createProviderProperty(
|
||||
propName, providerValueExpressions, resolvedProvider.multiProvider,
|
||||
resolvedProvider.eager, this);
|
||||
if (isDirectiveWrapper) {
|
||||
this.directiveWrapperInstance.set(tokenReference(resolvedProvider.token), instance);
|
||||
this.instances.set(
|
||||
tokenReference(resolvedProvider.token), DirectiveWrapperExpressions.context(instance));
|
||||
} else {
|
||||
this.instances.set(tokenReference(resolvedProvider.token), instance);
|
||||
}
|
||||
});
|
||||
|
||||
for (let i = 0; i < this._directives.length; i++) {
|
||||
const directive = this._directives[i];
|
||||
const directiveInstance = this.instances.get(tokenReference(identifierToken(directive.type)));
|
||||
directive.queries.forEach((queryMeta) => { this._addQuery(queryMeta, directiveInstance); });
|
||||
}
|
||||
|
||||
Object.keys(this.referenceTokens).forEach(varName => {
|
||||
const token = this.referenceTokens[varName];
|
||||
let varValue: o.Expression;
|
||||
if (token) {
|
||||
varValue = this.instances.get(tokenReference(token));
|
||||
} else {
|
||||
varValue = this.renderNode;
|
||||
}
|
||||
this.view.locals.set(varName, varValue);
|
||||
});
|
||||
}
|
||||
|
||||
afterChildren(childNodeCount: number) {
|
||||
Array.from(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.
|
||||
const providerExpr = this.instances.get(tokenReference(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.
|
||||
const providerChildNodeCount =
|
||||
resolvedProvider.providerType === ProviderAstType.PrivateService ? 0 : childNodeCount;
|
||||
this.view.injectorGetMethod.addStmt(createInjectInternalCondition(
|
||||
this.nodeIndex, providerChildNodeCount, resolvedProvider, providerExpr));
|
||||
});
|
||||
}
|
||||
|
||||
finish() {
|
||||
Array.from(this._queries.values())
|
||||
.forEach(
|
||||
queries => queries.forEach(
|
||||
q => q.generateStatements(
|
||||
this.view.createMethod, this.view.updateContentQueriesMethod)));
|
||||
}
|
||||
|
||||
addContentNode(ngContentIndex: number, nodeExpr: CompileViewRootNode) {
|
||||
this.contentNodesByNgContentIndex[ngContentIndex].push(nodeExpr);
|
||||
}
|
||||
|
||||
getComponent(): o.Expression {
|
||||
return isPresent(this.component) ?
|
||||
this.instances.get(tokenReference(identifierToken(this.component.type))) :
|
||||
null;
|
||||
}
|
||||
|
||||
getProviderTokens(): CompileTokenMetadata[] {
|
||||
return Array.from(this._resolvedProviders.values()).map(provider => provider.token);
|
||||
}
|
||||
|
||||
getQueriesFor(token: CompileTokenMetadata): CompileQuery[] {
|
||||
const result: CompileQuery[] = [];
|
||||
let currentEl: CompileElement = this;
|
||||
let distance = 0;
|
||||
let queries: CompileQuery[];
|
||||
while (!currentEl.isNull()) {
|
||||
queries = currentEl._queries.get(tokenReference(token));
|
||||
if (isPresent(queries)) {
|
||||
result.push(...queries.filter((query) => query.meta.descendants || distance <= 1));
|
||||
}
|
||||
if (currentEl._directives.length > 0) {
|
||||
distance++;
|
||||
}
|
||||
currentEl = currentEl.parent;
|
||||
}
|
||||
queries = this.view.componentView.viewQueries.get(tokenReference(token));
|
||||
if (isPresent(queries)) {
|
||||
result.push(...queries);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _addQuery(queryMeta: CompileQueryMetadata, directiveInstance: o.Expression):
|
||||
CompileQuery {
|
||||
const propName =
|
||||
`_query_${tokenName(queryMeta.selectors[0])}_${this.nodeIndex}_${this._queryCount++}`;
|
||||
const queryList = createQueryList(propName, this.view);
|
||||
const query = new CompileQuery(queryMeta, queryList, directiveInstance, this.view);
|
||||
addQueryToTokenMap(this._queries, query);
|
||||
return query;
|
||||
}
|
||||
|
||||
private _getLocalDependency(
|
||||
requestingProviderType: ProviderAstType, dep: CompileDiDependencyMetadata): o.Expression {
|
||||
let result: o.Expression = null;
|
||||
if (isPresent(dep.token)) {
|
||||
// access builtins with special visibility
|
||||
if (!result) {
|
||||
if (tokenReference(dep.token) === resolveIdentifier(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 (!result) {
|
||||
const resolvedProvider = this._resolvedProviders.get(tokenReference(dep.token));
|
||||
// don't allow directives / public services to access private services.
|
||||
// only components and private services can access private services.
|
||||
if (resolvedProvider && (requestingProviderType === ProviderAstType.Directive ||
|
||||
requestingProviderType === ProviderAstType.PublicService) &&
|
||||
resolvedProvider.providerType === ProviderAstType.PrivateService) {
|
||||
return null;
|
||||
}
|
||||
result = this.instances.get(tokenReference(dep.token));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private _getDependency(requestingProviderType: ProviderAstType, dep: CompileDiDependencyMetadata):
|
||||
o.Expression {
|
||||
let currElement: CompileElement = this;
|
||||
let result: o.Expression = null;
|
||||
if (dep.isValue) {
|
||||
result = o.literal(dep.value);
|
||||
}
|
||||
if (!result && !dep.isSkipSelf) {
|
||||
result = this._getLocalDependency(requestingProviderType, dep);
|
||||
}
|
||||
// check parent elements
|
||||
while (!result && !currElement.parent.isNull()) {
|
||||
currElement = currElement.parent;
|
||||
result = currElement._getLocalDependency(ProviderAstType.PublicService, {token: dep.token});
|
||||
}
|
||||
|
||||
if (!result) {
|
||||
result = injectFromViewParentInjector(this.view, dep.token, dep.isOptional);
|
||||
}
|
||||
if (!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 {
|
||||
let indexCondition: o.Expression;
|
||||
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, providerValueExpressions: o.Expression[], isMulti: boolean, isEager: boolean,
|
||||
compileElement: CompileElement): o.Expression {
|
||||
const view = compileElement.view;
|
||||
let resolvedProviderValueExpr: o.Expression;
|
||||
let type: o.Type;
|
||||
if (isMulti) {
|
||||
resolvedProviderValueExpr = o.literalArr(providerValueExpressions);
|
||||
type = new o.ArrayType(o.DYNAMIC_TYPE);
|
||||
} else {
|
||||
resolvedProviderValueExpr = providerValueExpressions[0];
|
||||
type = providerValueExpressions[0].type;
|
||||
}
|
||||
if (!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 {
|
||||
const internalField = `_${propName}`;
|
||||
view.fields.push(new o.ClassField(internalField, type));
|
||||
const 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);
|
||||
}
|
@ -1,81 +0,0 @@
|
||||
/**
|
||||
* @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 * as o from '../output/output_ast';
|
||||
import {TemplateAst} from '../template_parser/template_ast';
|
||||
|
||||
import {CompileView} from './compile_view';
|
||||
|
||||
class _DebugState {
|
||||
constructor(public nodeIndex: number, public sourceAst: TemplateAst) {}
|
||||
}
|
||||
|
||||
const 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) {
|
||||
const expr = this._updateDebugContext(this._newState);
|
||||
if (expr) {
|
||||
this._bodyStatements.push(expr.toStmt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _updateDebugContext(newState: _DebugState): o.Expression {
|
||||
this._currState = this._newState = newState;
|
||||
if (this._debugEnabled) {
|
||||
const sourceLocation = newState.sourceAst ? newState.sourceAst.sourceSpan.start : null;
|
||||
|
||||
return o.THIS_EXPR.callMethod('debug', [
|
||||
o.literal(newState.nodeIndex),
|
||||
sourceLocation ? o.literal(sourceLocation.line) : o.NULL_EXPR,
|
||||
sourceLocation ? o.literal(sourceLocation.col) : o.NULL_EXPR
|
||||
]);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
resetDebugInfoExpr(nodeIndex: number, templateAst: TemplateAst): o.Expression {
|
||||
const res = this._updateDebugContext(new _DebugState(nodeIndex, templateAst));
|
||||
return res || o.NULL_EXPR;
|
||||
}
|
||||
|
||||
resetDebugInfo(nodeIndex: number, templateAst: TemplateAst) {
|
||||
this._newState = new _DebugState(nodeIndex, templateAst);
|
||||
}
|
||||
|
||||
push(...stmts: o.Statement[]) { this.addStmts(stmts); }
|
||||
|
||||
addStmt(stmt: o.Statement) {
|
||||
this._updateDebugContextIfNeeded();
|
||||
this._bodyStatements.push(stmt);
|
||||
}
|
||||
|
||||
addStmts(stmts: o.Statement[]) {
|
||||
this._updateDebugContextIfNeeded();
|
||||
this._bodyStatements.push(...stmts);
|
||||
}
|
||||
|
||||
finish(): o.Statement[] { return this._bodyStatements; }
|
||||
|
||||
isEmpty(): boolean { return this._bodyStatements.length === 0; }
|
||||
}
|
@ -1,94 +0,0 @@
|
||||
/**
|
||||
* @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 {CompilePipeSummary, tokenReference} from '../compile_metadata';
|
||||
import {createPureProxy} from '../compiler_util/identifier_util';
|
||||
import {Identifiers, createIdentifier, resolveIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
import {CompileView} from './compile_view';
|
||||
import {getPropertyInView, injectFromViewParentInjector} from './util';
|
||||
|
||||
export class CompilePipe {
|
||||
static call(view: CompileView, name: string, args: o.Expression[]): o.Expression {
|
||||
const compView = view.componentView;
|
||||
const meta = _findPipeMeta(compView, name);
|
||||
let pipe: CompilePipe;
|
||||
if (meta.pure) {
|
||||
// pure pipes live on the component view
|
||||
pipe = compView.purePipes.get(name);
|
||||
if (!pipe) {
|
||||
pipe = new CompilePipe(compView, meta);
|
||||
compView.purePipes.set(name, pipe);
|
||||
compView.pipes.push(pipe);
|
||||
}
|
||||
} else {
|
||||
// Non pure pipes live on the view that called it
|
||||
pipe = new CompilePipe(view, meta);
|
||||
view.pipes.push(pipe);
|
||||
}
|
||||
return pipe._call(view, args);
|
||||
}
|
||||
|
||||
instance: o.ReadPropExpr;
|
||||
private _purePipeProxyCount = 0;
|
||||
|
||||
constructor(public view: CompileView, public meta: CompilePipeSummary) {
|
||||
this.instance = o.THIS_EXPR.prop(`_pipe_${meta.name}_${view.pipeCount++}`);
|
||||
const deps = this.meta.type.diDeps.map((diDep) => {
|
||||
if (tokenReference(diDep.token) === resolveIdentifier(Identifiers.ChangeDetectorRef)) {
|
||||
return getPropertyInView(o.THIS_EXPR.prop('ref'), this.view, this.view.componentView);
|
||||
}
|
||||
return injectFromViewParentInjector(view, 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());
|
||||
}
|
||||
|
||||
get pure(): boolean { return this.meta.pure; }
|
||||
|
||||
private _call(callingView: CompileView, args: o.Expression[]): o.Expression {
|
||||
if (this.meta.pure) {
|
||||
// PurePipeProxies live on the view that called them.
|
||||
const purePipeProxyInstance =
|
||||
o.THIS_EXPR.prop(`${this.instance.name}_${this._purePipeProxyCount++}`);
|
||||
const pipeInstanceSeenFromPureProxy =
|
||||
getPropertyInView(this.instance, callingView, this.view);
|
||||
createPureProxy(
|
||||
pipeInstanceSeenFromPureProxy.prop('transform')
|
||||
.callMethod(o.BuiltinMethod.Bind, [pipeInstanceSeenFromPureProxy]),
|
||||
args.length, purePipeProxyInstance,
|
||||
{fields: callingView.fields, ctorStmts: callingView.createMethod});
|
||||
return o.importExpr(createIdentifier(Identifiers.castByValue))
|
||||
.callFn([purePipeProxyInstance, pipeInstanceSeenFromPureProxy.prop('transform')])
|
||||
.callFn(args);
|
||||
} else {
|
||||
return getPropertyInView(this.instance, callingView, this.view).callMethod('transform', args);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _findPipeMeta(view: CompileView, name: string): CompilePipeSummary {
|
||||
let pipeMeta: CompilePipeSummary = null;
|
||||
for (let i = view.pipeMetas.length - 1; i >= 0; i--) {
|
||||
const localPipeMeta = view.pipeMetas[i];
|
||||
if (localPipeMeta.name == name) {
|
||||
pipeMeta = localPipeMeta;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!pipeMeta) {
|
||||
throw new Error(
|
||||
`Illegal state: Could not find pipe ${name} although the parser should have detected this error!`);
|
||||
}
|
||||
return pipeMeta;
|
||||
}
|
@ -1,133 +0,0 @@
|
||||
/**
|
||||
* @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 {CompileQueryMetadata, tokenReference} from '../compile_metadata';
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileMethod} from './compile_method';
|
||||
import {CompileView} from './compile_view';
|
||||
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) {
|
||||
let currentView = view;
|
||||
const elPath: CompileElement[] = [];
|
||||
while (currentView && currentView !== this.view) {
|
||||
const parentEl = currentView.declarationElement;
|
||||
elPath.unshift(parentEl);
|
||||
currentView = parentEl.view;
|
||||
}
|
||||
const queryListForDirtyExpr = getPropertyInView(this.queryList, view, this.view);
|
||||
|
||||
let viewValues = this._values;
|
||||
elPath.forEach((el) => {
|
||||
const last =
|
||||
viewValues.values.length > 0 ? viewValues.values[viewValues.values.length - 1] : null;
|
||||
if (last instanceof ViewQueryValues && last.view === el.embeddedView) {
|
||||
viewValues = last;
|
||||
} else {
|
||||
const 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());
|
||||
}
|
||||
}
|
||||
|
||||
private _isStatic(): boolean {
|
||||
return !this._values.values.some(value => value instanceof ViewQueryValues);
|
||||
}
|
||||
|
||||
generateStatements(targetStaticMethod: CompileMethod, targetDynamicMethod: CompileMethod) {
|
||||
const values = createQueryValues(this._values);
|
||||
const updateStmts = [this.queryList.callMethod('reset', [o.literalArr(values)]).toStmt()];
|
||||
if (this.ownerDirectiveExpression) {
|
||||
const 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());
|
||||
}
|
||||
if (this.meta.first && this._isStatic()) {
|
||||
// for queries that don't change and the user asked for a single element,
|
||||
// set it immediately. That is e.g. needed for querying for ViewContainerRefs, ...
|
||||
// we don't do this for QueryLists for now as this would break the timing when
|
||||
// we call QueryList listeners...
|
||||
targetStaticMethod.addStmts(updateStmts);
|
||||
} else {
|
||||
targetDynamicMethod.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.viewContainer, entry.view, createQueryValues(entry));
|
||||
} else {
|
||||
return <o.Expression>entry;
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
function mapNestedViews(
|
||||
viewContainer: o.Expression, view: CompileView, expressions: o.Expression[]): o.Expression {
|
||||
const adjustedExpressions: o.Expression[] = expressions.map(
|
||||
(expr) => o.replaceVarInExpression(o.THIS_EXPR.name, o.variable('nestedView'), expr));
|
||||
return viewContainer.callMethod('mapNestedViews', [
|
||||
o.variable(view.className),
|
||||
o.fn(
|
||||
[new o.FnParam('nestedView', view.classType)],
|
||||
[new o.ReturnStatement(o.literalArr(adjustedExpressions))], o.DYNAMIC_TYPE)
|
||||
]);
|
||||
}
|
||||
|
||||
export function createQueryList(propertyName: string, compileView: CompileView): o.Expression {
|
||||
compileView.fields.push(new o.ClassField(
|
||||
propertyName, o.importType(createIdentifier(Identifiers.QueryList), [o.DYNAMIC_TYPE])));
|
||||
const expr = o.THIS_EXPR.prop(propertyName);
|
||||
compileView.createMethod.addStmt(
|
||||
o.THIS_EXPR.prop(propertyName)
|
||||
.set(o.importExpr(createIdentifier(Identifiers.QueryList), [o.DYNAMIC_TYPE]).instantiate([
|
||||
]))
|
||||
.toStmt());
|
||||
return expr;
|
||||
}
|
||||
|
||||
export function addQueryToTokenMap(map: Map<any, CompileQuery[]>, query: CompileQuery) {
|
||||
query.meta.selectors.forEach((selector) => {
|
||||
let entry = map.get(tokenReference(selector));
|
||||
if (!entry) {
|
||||
entry = [];
|
||||
map.set(tokenReference(selector), entry);
|
||||
}
|
||||
entry.push(query);
|
||||
});
|
||||
}
|
@ -1,176 +0,0 @@
|
||||
/**
|
||||
* @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 {ɵViewType as ViewType} from '@angular/core';
|
||||
|
||||
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
||||
import {CompileDirectiveMetadata, CompilePipeSummary, rendererTypeName, tokenName, viewClassName} from '../compile_metadata';
|
||||
import {EventHandlerVars, LegacyNameResolver} from '../compiler_util/expression_converter';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
import {CompileElement, CompileNode} from './compile_element';
|
||||
import {CompileMethod} from './compile_method';
|
||||
import {CompilePipe} from './compile_pipe';
|
||||
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
|
||||
import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency} from './deps';
|
||||
import {getPropertyInView} from './util';
|
||||
|
||||
export enum CompileViewRootNodeType {
|
||||
Node,
|
||||
ViewContainer,
|
||||
NgContent
|
||||
}
|
||||
|
||||
export class CompileViewRootNode {
|
||||
constructor(
|
||||
public type: CompileViewRootNodeType, public expr: o.Expression,
|
||||
public ngContentIndex?: number) {}
|
||||
}
|
||||
|
||||
export class CompileView implements LegacyNameResolver {
|
||||
public viewType: ViewType;
|
||||
public viewQueries: Map<any, CompileQuery[]>;
|
||||
|
||||
public viewChildren: o.Expression[] = [];
|
||||
|
||||
public nodes: CompileNode[] = [];
|
||||
|
||||
public rootNodes: CompileViewRootNode[] = [];
|
||||
public lastRenderNode: o.Expression = o.NULL_EXPR;
|
||||
|
||||
public viewContainers: o.Expression[] = [];
|
||||
|
||||
public createMethod: CompileMethod;
|
||||
public animationBindingsMethod: 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 detachMethod: CompileMethod;
|
||||
public methods: o.ClassMethod[] = [];
|
||||
|
||||
public ctorStmts: o.Statement[] = [];
|
||||
public fields: o.ClassField[] = [];
|
||||
public getters: o.ClassGetter[] = [];
|
||||
public disposables: 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 rendererTypeName: string;
|
||||
public classType: o.Type;
|
||||
public classExpr: o.ReadVarExpr;
|
||||
|
||||
public literalArrayCount = 0;
|
||||
public literalMapCount = 0;
|
||||
public pipeCount = 0;
|
||||
|
||||
public componentContext: o.Expression;
|
||||
|
||||
constructor(
|
||||
public component: CompileDirectiveMetadata, public genConfig: CompilerConfig,
|
||||
public pipeMetas: CompilePipeSummary[], public styles: o.Expression,
|
||||
public animations: AnimationEntryCompileResult[], public viewIndex: number,
|
||||
public declarationElement: CompileElement, public templateVariableBindings: string[][],
|
||||
public targetDependencies:
|
||||
Array<ComponentViewDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {
|
||||
this.createMethod = new CompileMethod(this);
|
||||
this.animationBindingsMethod = 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.detachMethod = new CompileMethod(this);
|
||||
|
||||
this.viewType = getViewType(component, viewIndex);
|
||||
this.className = viewClassName(component.type.reference, viewIndex);
|
||||
this.rendererTypeName = rendererTypeName(component.type.reference);
|
||||
this.classType = o.expressionType(o.variable(this.className));
|
||||
this.classExpr = o.variable(this.className);
|
||||
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);
|
||||
|
||||
const viewQueries = new Map<any, CompileQuery[]>();
|
||||
if (this.viewType === ViewType.COMPONENT) {
|
||||
const directiveInstance = o.THIS_EXPR.prop('context');
|
||||
this.component.viewQueries.forEach((queryMeta, queryIndex) => {
|
||||
const propName = `_viewQuery_${tokenName(queryMeta.selectors[0])}_${queryIndex}`;
|
||||
const queryList = createQueryList(propName, this);
|
||||
const query = new CompileQuery(queryMeta, queryList, directiveInstance, 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 {
|
||||
return CompilePipe.call(this, name, [input].concat(args));
|
||||
}
|
||||
|
||||
getLocal(name: string): o.Expression {
|
||||
if (name == EventHandlerVars.event.name) {
|
||||
return EventHandlerVars.event;
|
||||
}
|
||||
let currView: CompileView = this;
|
||||
let result = currView.locals.get(name);
|
||||
while (!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;
|
||||
}
|
||||
}
|
||||
|
||||
finish() {
|
||||
Array.from(this.viewQueries.values())
|
||||
.forEach(
|
||||
queries => queries.forEach(
|
||||
q => q.generateStatements(this.createMethod, this.updateViewQueriesMethod)));
|
||||
}
|
||||
}
|
||||
|
||||
function getViewType(component: CompileDirectiveMetadata, embeddedTemplateIndex: number): ViewType {
|
||||
if (embeddedTemplateIndex > 0) {
|
||||
return ViewType.EMBEDDED;
|
||||
}
|
||||
|
||||
if (component.isHost) {
|
||||
return ViewType.HOST;
|
||||
}
|
||||
|
||||
return ViewType.COMPONENT;
|
||||
}
|
@ -1,49 +0,0 @@
|
||||
/**
|
||||
* @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 {ChangeDetectionStrategy, ViewEncapsulation, ɵViewType as ViewType} from '@angular/core';
|
||||
import {createEnumExpression} from '../compiler_util/identifier_util';
|
||||
import {Identifiers} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
export class ViewTypeEnum {
|
||||
static fromValue(value: ViewType): o.Expression {
|
||||
return createEnumExpression(Identifiers.ViewType, value);
|
||||
}
|
||||
}
|
||||
|
||||
export class ViewEncapsulationEnum {
|
||||
static fromValue(value: ViewEncapsulation): o.Expression {
|
||||
return createEnumExpression(Identifiers.ViewEncapsulation, value);
|
||||
}
|
||||
}
|
||||
|
||||
export class ChangeDetectorStatusEnum {
|
||||
static fromValue(value: ChangeDetectorStatusEnum): o.Expression {
|
||||
return createEnumExpression(Identifiers.ChangeDetectorStatus, value);
|
||||
}
|
||||
}
|
||||
|
||||
export class ViewConstructorVars {
|
||||
static viewUtils = o.variable('viewUtils');
|
||||
static parentView = o.variable('parentView');
|
||||
static parentIndex = o.variable('parentIndex');
|
||||
static parentElement = o.variable('parentElement');
|
||||
}
|
||||
|
||||
export class ViewProperties {
|
||||
static renderer = o.THIS_EXPR.prop('renderer');
|
||||
static viewUtils = o.THIS_EXPR.prop('viewUtils');
|
||||
static throwOnChange = o.THIS_EXPR.prop('throwOnChange');
|
||||
}
|
||||
|
||||
export class InjectMethodVars {
|
||||
static token = o.variable('token');
|
||||
static requestNodeIndex = o.variable('requestNodeIndex');
|
||||
static notFoundResult = o.variable('notFoundResult');
|
||||
}
|
@ -1,31 +0,0 @@
|
||||
/**
|
||||
* @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
|
||||
*/
|
||||
|
||||
/**
|
||||
* This is currently not read, but will probably be used in the future.
|
||||
* We keep it as we already pass it through all the right places...
|
||||
*/
|
||||
export class ComponentViewDependency {
|
||||
constructor(public compType: any) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is currently not read, but will probably be used in the future.
|
||||
* We keep it as we already pass it through all the right places...
|
||||
*/
|
||||
export class ComponentFactoryDependency {
|
||||
constructor(public compType: any) {}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is currently not read, but will probably be used in the future.
|
||||
* We keep it as we already pass it through all the right places...
|
||||
*/
|
||||
export class DirectiveWrapperDependency {
|
||||
constructor(public dirType: any) {}
|
||||
}
|
@ -1,135 +0,0 @@
|
||||
/**
|
||||
* @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 {EventHandlerVars, convertActionBinding} from '../compiler_util/expression_converter';
|
||||
import {createInlineArray} from '../compiler_util/identifier_util';
|
||||
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
|
||||
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileMethod} from './compile_method';
|
||||
import {getHandleEventMethodName} from './util';
|
||||
|
||||
export function bindOutputs(
|
||||
boundEvents: BoundEventAst[], directives: DirectiveAst[], compileElement: CompileElement,
|
||||
bindToRenderer: boolean): boolean {
|
||||
const usedEvents = collectEvents(boundEvents, directives);
|
||||
if (!usedEvents.size) {
|
||||
return false;
|
||||
}
|
||||
if (bindToRenderer) {
|
||||
subscribeToRenderEvents(usedEvents, compileElement);
|
||||
}
|
||||
subscribeToDirectiveEvents(usedEvents, directives, compileElement);
|
||||
generateHandleEventMethod(boundEvents, directives, compileElement);
|
||||
return true;
|
||||
}
|
||||
|
||||
function collectEvents(
|
||||
boundEvents: BoundEventAst[], directives: DirectiveAst[]): Map<string, EventSummary> {
|
||||
const usedEvents = new Map<string, EventSummary>();
|
||||
boundEvents.forEach((event) => { usedEvents.set(event.fullName, event); });
|
||||
directives.forEach((dirAst) => {
|
||||
dirAst.hostEvents.forEach((event) => { usedEvents.set(event.fullName, event); });
|
||||
});
|
||||
return usedEvents;
|
||||
}
|
||||
|
||||
function subscribeToRenderEvents(
|
||||
usedEvents: Map<string, EventSummary>, compileElement: CompileElement) {
|
||||
const eventAndTargetExprs: o.Expression[] = [];
|
||||
usedEvents.forEach((event) => {
|
||||
if (!event.phase) {
|
||||
eventAndTargetExprs.push(o.literal(event.name), o.literal(event.target));
|
||||
}
|
||||
});
|
||||
if (eventAndTargetExprs.length) {
|
||||
const disposableVar = o.variable(`disposable_${compileElement.view.disposables.length}`);
|
||||
compileElement.view.disposables.push(disposableVar);
|
||||
compileElement.view.createMethod.addStmt(
|
||||
disposableVar
|
||||
.set(o.importExpr(createIdentifier(Identifiers.subscribeToRenderElement)).callFn([
|
||||
o.THIS_EXPR, compileElement.renderNode, createInlineArray(eventAndTargetExprs),
|
||||
handleEventExpr(compileElement)
|
||||
]))
|
||||
.toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
|
||||
}
|
||||
}
|
||||
|
||||
function subscribeToDirectiveEvents(
|
||||
usedEvents: Map<string, EventSummary>, directives: DirectiveAst[],
|
||||
compileElement: CompileElement) {
|
||||
const usedEventNames = Array.from(usedEvents.keys());
|
||||
directives.forEach((dirAst) => {
|
||||
const dirWrapper = compileElement.directiveWrapperInstance.get(dirAst.directive.type.reference);
|
||||
compileElement.view.createMethod.addStmts(DirectiveWrapperExpressions.subscribe(
|
||||
dirAst.directive, dirAst.hostProperties, usedEventNames, dirWrapper, o.THIS_EXPR,
|
||||
handleEventExpr(compileElement)));
|
||||
});
|
||||
}
|
||||
|
||||
function generateHandleEventMethod(
|
||||
boundEvents: BoundEventAst[], directives: DirectiveAst[], compileElement: CompileElement) {
|
||||
const hasComponentHostListener =
|
||||
directives.some((dirAst) => dirAst.hostEvents.some((event) => dirAst.directive.isComponent));
|
||||
|
||||
const markPathToRootStart = hasComponentHostListener ? compileElement.compViewExpr : o.THIS_EXPR;
|
||||
const handleEventStmts = new CompileMethod(compileElement.view);
|
||||
handleEventStmts.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||
handleEventStmts.push(markPathToRootStart.callMethod('markPathToRootAsCheckOnce', []).toStmt());
|
||||
const eventNameVar = o.variable('eventName');
|
||||
const resultVar = o.variable('result');
|
||||
handleEventStmts.push(resultVar.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE));
|
||||
|
||||
directives.forEach((dirAst, dirIdx) => {
|
||||
const dirWrapper = compileElement.directiveWrapperInstance.get(dirAst.directive.type.reference);
|
||||
if (dirAst.hostEvents.length > 0) {
|
||||
handleEventStmts.push(
|
||||
resultVar
|
||||
.set(DirectiveWrapperExpressions
|
||||
.handleEvent(
|
||||
dirAst.hostEvents, dirWrapper, eventNameVar, EventHandlerVars.event)
|
||||
.and(resultVar))
|
||||
.toStmt());
|
||||
}
|
||||
});
|
||||
boundEvents.forEach((renderEvent, renderEventIdx) => {
|
||||
const evalResult = convertActionBinding(
|
||||
compileElement.view, compileElement.view.componentContext, renderEvent.handler,
|
||||
`sub_${renderEventIdx}`);
|
||||
const trueStmts = evalResult.stmts;
|
||||
if (evalResult.allowDefault) {
|
||||
trueStmts.push(resultVar.set(evalResult.allowDefault.and(resultVar)).toStmt());
|
||||
}
|
||||
// TODO(tbosch): convert this into a `switch` once our OutputAst supports it.
|
||||
handleEventStmts.push(
|
||||
new o.IfStmt(eventNameVar.equals(o.literal(renderEvent.fullName)), trueStmts));
|
||||
});
|
||||
|
||||
handleEventStmts.push(new o.ReturnStatement(resultVar));
|
||||
compileElement.view.methods.push(new o.ClassMethod(
|
||||
getHandleEventMethodName(compileElement.nodeIndex),
|
||||
[
|
||||
new o.FnParam(eventNameVar.name, o.STRING_TYPE),
|
||||
new o.FnParam(EventHandlerVars.event.name, o.DYNAMIC_TYPE)
|
||||
],
|
||||
handleEventStmts.finish(), o.BOOL_TYPE));
|
||||
}
|
||||
|
||||
function handleEventExpr(compileElement: CompileElement) {
|
||||
const handleEventMethodName = getHandleEventMethodName(compileElement.nodeIndex);
|
||||
return o.THIS_EXPR.callMethod('eventHandler', [o.THIS_EXPR.prop(handleEventMethodName)]);
|
||||
}
|
||||
|
||||
type EventSummary = {
|
||||
name: string,
|
||||
target: string,
|
||||
phase: string
|
||||
};
|
@ -1,85 +0,0 @@
|
||||
/**
|
||||
* @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 {ɵLifecycleHooks as LifecycleHooks} from '@angular/core';
|
||||
|
||||
import {CompileDirectiveSummary, CompilePipeSummary} from '../compile_metadata';
|
||||
import {isFirstViewCheck} from '../compiler_util/binding_util';
|
||||
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||
import * as o from '../output/output_ast';
|
||||
import {DirectiveAst, ProviderAst, ProviderAstType} from '../template_parser/template_ast';
|
||||
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileView} from './compile_view';
|
||||
|
||||
export function bindDirectiveAfterContentLifecycleCallbacks(
|
||||
directiveMeta: CompileDirectiveSummary, directiveInstance: o.Expression,
|
||||
compileElement: CompileElement) {
|
||||
const view = compileElement.view;
|
||||
const lifecycleHooks = directiveMeta.type.lifecycleHooks;
|
||||
const afterContentLifecycleCallbacksMethod = view.afterContentLifecycleCallbacksMethod;
|
||||
afterContentLifecycleCallbacksMethod.resetDebugInfo(
|
||||
compileElement.nodeIndex, compileElement.sourceAst);
|
||||
if (lifecycleHooks.indexOf(LifecycleHooks.AfterContentInit) !== -1) {
|
||||
afterContentLifecycleCallbacksMethod.addStmt(new o.IfStmt(
|
||||
isFirstViewCheck(o.THIS_EXPR),
|
||||
[directiveInstance.callMethod('ngAfterContentInit', []).toStmt()]));
|
||||
}
|
||||
if (lifecycleHooks.indexOf(LifecycleHooks.AfterContentChecked) !== -1) {
|
||||
afterContentLifecycleCallbacksMethod.addStmt(
|
||||
directiveInstance.callMethod('ngAfterContentChecked', []).toStmt());
|
||||
}
|
||||
}
|
||||
|
||||
export function bindDirectiveAfterViewLifecycleCallbacks(
|
||||
directiveMeta: CompileDirectiveSummary, directiveInstance: o.Expression,
|
||||
compileElement: CompileElement) {
|
||||
const view = compileElement.view;
|
||||
const lifecycleHooks = directiveMeta.type.lifecycleHooks;
|
||||
const afterViewLifecycleCallbacksMethod = view.afterViewLifecycleCallbacksMethod;
|
||||
afterViewLifecycleCallbacksMethod.resetDebugInfo(
|
||||
compileElement.nodeIndex, compileElement.sourceAst);
|
||||
if (lifecycleHooks.indexOf(LifecycleHooks.AfterViewInit) !== -1) {
|
||||
afterViewLifecycleCallbacksMethod.addStmt(new o.IfStmt(
|
||||
isFirstViewCheck(o.THIS_EXPR),
|
||||
[directiveInstance.callMethod('ngAfterViewInit', []).toStmt()]));
|
||||
}
|
||||
if (lifecycleHooks.indexOf(LifecycleHooks.AfterViewChecked) !== -1) {
|
||||
afterViewLifecycleCallbacksMethod.addStmt(
|
||||
directiveInstance.callMethod('ngAfterViewChecked', []).toStmt());
|
||||
}
|
||||
}
|
||||
|
||||
export function bindDirectiveWrapperLifecycleCallbacks(
|
||||
dir: DirectiveAst, directiveWrapperIntance: o.Expression, compileElement: CompileElement) {
|
||||
compileElement.view.destroyMethod.addStmts(
|
||||
DirectiveWrapperExpressions.ngOnDestroy(dir.directive, directiveWrapperIntance));
|
||||
compileElement.view.detachMethod.addStmts(DirectiveWrapperExpressions.ngOnDetach(
|
||||
dir.hostProperties, directiveWrapperIntance, o.THIS_EXPR,
|
||||
compileElement.compViewExpr || o.THIS_EXPR, compileElement.renderNode));
|
||||
}
|
||||
|
||||
|
||||
export function bindInjectableDestroyLifecycleCallbacks(
|
||||
provider: ProviderAst, providerInstance: o.Expression, compileElement: CompileElement) {
|
||||
const onDestroyMethod = compileElement.view.destroyMethod;
|
||||
onDestroyMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||
if (provider.providerType !== ProviderAstType.Directive &&
|
||||
provider.providerType !== ProviderAstType.Component &&
|
||||
provider.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1) {
|
||||
onDestroyMethod.addStmt(providerInstance.callMethod('ngOnDestroy', []).toStmt());
|
||||
}
|
||||
}
|
||||
|
||||
export function bindPipeDestroyLifecycleCallbacks(
|
||||
pipeMeta: CompilePipeSummary, pipeInstance: o.Expression, view: CompileView) {
|
||||
const onDestroyMethod = view.destroyMethod;
|
||||
if (pipeMeta.type.lifecycleHooks.indexOf(LifecycleHooks.OnDestroy) !== -1) {
|
||||
onDestroyMethod.addStmt(pipeInstance.callMethod('ngOnDestroy', []).toStmt());
|
||||
}
|
||||
}
|
@ -1,146 +0,0 @@
|
||||
/**
|
||||
* @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 {SecurityContext, ɵisDefaultChangeDetectionStrategy as isDefaultChangeDetectionStrategy} from '@angular/core';
|
||||
import {createCheckBindingField} from '../compiler_util/binding_util';
|
||||
import {legacyConvertPropertyBinding} from '../compiler_util/expression_converter';
|
||||
import {createEnumExpression} from '../compiler_util/identifier_util';
|
||||
import {createCheckAnimationBindingStmts, createCheckRenderBindingStmt} from '../compiler_util/render_util';
|
||||
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
|
||||
import {Identifiers, createIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||
import {BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, PropertyBindingType} from '../template_parser/template_ast';
|
||||
|
||||
import {CompileElement, CompileNode} from './compile_element';
|
||||
import {CompileView} from './compile_view';
|
||||
import {getHandleEventMethodName} from './util';
|
||||
|
||||
export function bindRenderText(
|
||||
boundText: BoundTextAst, compileNode: CompileNode, view: CompileView): void {
|
||||
const valueField = createCheckBindingField(view);
|
||||
const evalResult = legacyConvertPropertyBinding(
|
||||
view, view, view.componentContext, boundText.value, valueField.bindingId);
|
||||
if (!evalResult) {
|
||||
return null;
|
||||
}
|
||||
|
||||
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileNode.nodeIndex, boundText);
|
||||
view.detectChangesRenderPropertiesMethod.addStmts(evalResult.stmts);
|
||||
view.detectChangesRenderPropertiesMethod.addStmt(
|
||||
o.importExpr(createIdentifier(Identifiers.checkRenderText))
|
||||
.callFn([
|
||||
o.THIS_EXPR, compileNode.renderNode, valueField.expression,
|
||||
valueField.expression.set(evalResult.currValExpr),
|
||||
evalResult.forceUpdate || o.literal(false)
|
||||
])
|
||||
.toStmt());
|
||||
}
|
||||
|
||||
export function bindRenderInputs(
|
||||
boundProps: BoundElementPropertyAst[], boundOutputs: BoundEventAst[], hasEvents: boolean,
|
||||
compileElement: CompileElement) {
|
||||
const view = compileElement.view;
|
||||
const renderNode = compileElement.renderNode;
|
||||
|
||||
boundProps.forEach((boundProp) => {
|
||||
const bindingField = createCheckBindingField(view);
|
||||
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
|
||||
const evalResult = legacyConvertPropertyBinding(
|
||||
view, view, compileElement.view.componentContext, boundProp.value, bindingField.bindingId);
|
||||
if (!evalResult) {
|
||||
return;
|
||||
}
|
||||
let compileMethod = view.detectChangesRenderPropertiesMethod;
|
||||
switch (boundProp.type) {
|
||||
case PropertyBindingType.Property:
|
||||
case PropertyBindingType.Attribute:
|
||||
case PropertyBindingType.Class:
|
||||
case PropertyBindingType.Style:
|
||||
compileMethod.addStmts(createCheckRenderBindingStmt(
|
||||
o.THIS_EXPR, renderNode, boundProp, bindingField.expression, evalResult));
|
||||
break;
|
||||
case PropertyBindingType.Animation:
|
||||
compileMethod = view.animationBindingsMethod;
|
||||
const {checkUpdateStmts, checkDetachStmts} = createCheckAnimationBindingStmts(
|
||||
o.THIS_EXPR, o.THIS_EXPR, boundProp, boundOutputs,
|
||||
(hasEvents ? o.THIS_EXPR.prop(getHandleEventMethodName(compileElement.nodeIndex)) :
|
||||
o.importExpr(createIdentifier(Identifiers.noop)))
|
||||
.callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR]),
|
||||
compileElement.renderNode, bindingField.expression, evalResult);
|
||||
view.detachMethod.addStmts(checkDetachStmts);
|
||||
compileMethod.addStmts(checkUpdateStmts);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export function bindDirectiveHostProps(
|
||||
directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression,
|
||||
compileElement: CompileElement, elementName: string,
|
||||
schemaRegistry: ElementSchemaRegistry): void {
|
||||
// We need to provide the SecurityContext for properties that could need sanitization.
|
||||
const runtimeSecurityCtxExprs =
|
||||
directiveAst.hostProperties.filter(boundProp => boundProp.needsRuntimeSecurityContext)
|
||||
.map((boundProp) => {
|
||||
let ctx: SecurityContext;
|
||||
switch (boundProp.type) {
|
||||
case PropertyBindingType.Property:
|
||||
ctx = schemaRegistry.securityContext(elementName, boundProp.name, false);
|
||||
break;
|
||||
case PropertyBindingType.Attribute:
|
||||
ctx = schemaRegistry.securityContext(elementName, boundProp.name, true);
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Illegal state: Only property / attribute bindings can have an unknown security context! Binding ${boundProp.name}`);
|
||||
}
|
||||
return createEnumExpression(Identifiers.SecurityContext, ctx);
|
||||
});
|
||||
compileElement.view.detectChangesRenderPropertiesMethod.addStmts(
|
||||
DirectiveWrapperExpressions.checkHost(
|
||||
directiveAst.hostProperties, directiveWrapperInstance, o.THIS_EXPR,
|
||||
compileElement.compViewExpr || o.THIS_EXPR, compileElement.renderNode,
|
||||
runtimeSecurityCtxExprs));
|
||||
}
|
||||
|
||||
export function bindDirectiveInputs(
|
||||
directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression, dirIndex: number,
|
||||
compileElement: CompileElement) {
|
||||
const view = compileElement.view;
|
||||
const detectChangesInInputsMethod = view.detectChangesInInputsMethod;
|
||||
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
|
||||
|
||||
directiveAst.inputs.forEach((input, inputIdx) => {
|
||||
// Note: We can't use `fields.length` here, as we are not adding a field!
|
||||
const bindingId = `${compileElement.nodeIndex}_${dirIndex}_${inputIdx}`;
|
||||
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
|
||||
const evalResult =
|
||||
legacyConvertPropertyBinding(view, view, view.componentContext, input.value, bindingId);
|
||||
if (!evalResult) {
|
||||
return;
|
||||
}
|
||||
detectChangesInInputsMethod.addStmts(evalResult.stmts);
|
||||
detectChangesInInputsMethod.addStmt(
|
||||
directiveWrapperInstance
|
||||
.callMethod(
|
||||
`check_${input.directiveName}`,
|
||||
[o.THIS_EXPR, evalResult.currValExpr, evalResult.forceUpdate || o.literal(false)])
|
||||
.toStmt());
|
||||
});
|
||||
const isOnPushComp = directiveAst.directive.isComponent &&
|
||||
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
|
||||
const directiveDetectChangesExpr = DirectiveWrapperExpressions.ngDoCheck(
|
||||
directiveWrapperInstance, o.THIS_EXPR, compileElement.renderNode);
|
||||
const directiveDetectChangesStmt = isOnPushComp ?
|
||||
new o.IfStmt(
|
||||
directiveDetectChangesExpr,
|
||||
[compileElement.compViewExpr.callMethod('markAsCheckOnce', []).toStmt()]) :
|
||||
directiveDetectChangesExpr.toStmt();
|
||||
detectChangesInInputsMethod.addStmt(directiveDetectChangesStmt);
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
/**
|
||||
* @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 {CompileTokenMetadata, tokenReference} from '../compile_metadata';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileQuery} from './compile_query';
|
||||
|
||||
|
||||
// Note: We can't do this when we create the CompileElements already,
|
||||
// as we create embedded views before the <ng-template> elements themselves.
|
||||
export function bindQueryValues(ce: CompileElement) {
|
||||
const queriesWithReads: _QueryWithRead[] = [];
|
||||
|
||||
ce.getProviderTokens().forEach((token) => {
|
||||
const queriesForProvider = ce.getQueriesFor(token);
|
||||
queriesWithReads.push(...queriesForProvider.map(query => new _QueryWithRead(query, token)));
|
||||
});
|
||||
|
||||
Object.keys(ce.referenceTokens).forEach(varName => {
|
||||
const varToken = {value: varName};
|
||||
queriesWithReads.push(
|
||||
...ce.getQueriesFor(varToken).map(query => new _QueryWithRead(query, varToken)));
|
||||
});
|
||||
|
||||
queriesWithReads.forEach((queryWithRead) => {
|
||||
let value: o.Expression;
|
||||
if (queryWithRead.read.identifier) {
|
||||
// query for an identifier
|
||||
value = ce.instances.get(tokenReference(queryWithRead.read));
|
||||
} else {
|
||||
// query for a reference
|
||||
const token = ce.referenceTokens[queryWithRead.read.value];
|
||||
if (token) {
|
||||
value = ce.instances.get(tokenReference(token));
|
||||
} else {
|
||||
value = ce.elementRef;
|
||||
}
|
||||
}
|
||||
if (value) {
|
||||
queryWithRead.query.addValue(value, ce.view);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
class _QueryWithRead {
|
||||
public read: CompileTokenMetadata;
|
||||
constructor(public query: CompileQuery, match: CompileTokenMetadata) {
|
||||
this.read = query.meta.read || match;
|
||||
}
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
/**
|
||||
* @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 {ɵViewType as ViewType} from '@angular/core';
|
||||
|
||||
import {CompileTokenMetadata} from '../compile_metadata';
|
||||
import {createDiTokenExpression} from '../compiler_util/identifier_util';
|
||||
import * as o from '../output/output_ast';
|
||||
|
||||
import {CompileView} from './compile_view';
|
||||
|
||||
export function getPropertyInView(
|
||||
property: o.Expression, callingView: CompileView, definedView: CompileView): o.Expression {
|
||||
if (callingView === definedView) {
|
||||
return property;
|
||||
} else {
|
||||
let viewProp: o.Expression = o.THIS_EXPR;
|
||||
let currView: CompileView = callingView;
|
||||
while (currView !== definedView && currView.declarationElement.view) {
|
||||
currView = currView.declarationElement.view;
|
||||
viewProp = viewProp.prop('parentView');
|
||||
}
|
||||
if (currView !== definedView) {
|
||||
throw new Error(
|
||||
`Internal error: Could not calculate a property in a parent view: ${property}`);
|
||||
}
|
||||
return property.visitExpression(new _ReplaceViewTransformer(viewProp, definedView), null);
|
||||
}
|
||||
}
|
||||
|
||||
class _ReplaceViewTransformer extends o.ExpressionTransformer {
|
||||
constructor(private _viewExpr: o.Expression, private _view: CompileView) { super(); }
|
||||
private _isThis(expr: o.Expression): boolean {
|
||||
return expr instanceof o.ReadVarExpr && expr.builtin === o.BuiltinVar.This;
|
||||
}
|
||||
|
||||
visitReadVarExpr(ast: o.ReadVarExpr, context: any): any {
|
||||
return this._isThis(ast) ? this._viewExpr : ast;
|
||||
}
|
||||
visitReadPropExpr(ast: o.ReadPropExpr, context: any): any {
|
||||
if (this._isThis(ast.receiver)) {
|
||||
// Note: Don't cast for members of the AppView base class...
|
||||
if (this._view.fields.some((field) => field.name == ast.name) ||
|
||||
this._view.getters.some((field) => field.name == ast.name)) {
|
||||
return this._viewExpr.cast(this._view.classType).prop(ast.name);
|
||||
}
|
||||
}
|
||||
return super.visitReadPropExpr(ast, context);
|
||||
}
|
||||
}
|
||||
|
||||
export function injectFromViewParentInjector(
|
||||
view: CompileView, token: CompileTokenMetadata, optional: boolean): o.Expression {
|
||||
let viewExpr: o.Expression;
|
||||
if (view.viewType === ViewType.HOST) {
|
||||
viewExpr = o.THIS_EXPR;
|
||||
} else {
|
||||
viewExpr = o.THIS_EXPR.prop('parentView');
|
||||
}
|
||||
const args = [createDiTokenExpression(token), o.THIS_EXPR.prop('parentIndex')];
|
||||
if (optional) {
|
||||
args.push(o.NULL_EXPR);
|
||||
}
|
||||
return viewExpr.callMethod('injectorGet', args);
|
||||
}
|
||||
|
||||
export function getHandleEventMethodName(elementIndex: number): string {
|
||||
return `handleEvent_${elementIndex}`;
|
||||
}
|
@ -1,113 +0,0 @@
|
||||
/**
|
||||
* @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 {tokenReference} from '../compile_metadata';
|
||||
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
|
||||
|
||||
import {CompileElement} from './compile_element';
|
||||
import {CompileView} from './compile_view';
|
||||
import {bindOutputs} from './event_binder';
|
||||
import {bindDirectiveAfterContentLifecycleCallbacks, bindDirectiveAfterViewLifecycleCallbacks, bindDirectiveWrapperLifecycleCallbacks, bindInjectableDestroyLifecycleCallbacks, bindPipeDestroyLifecycleCallbacks} from './lifecycle_binder';
|
||||
import {bindDirectiveHostProps, bindDirectiveInputs, bindRenderInputs, bindRenderText} from './property_binder';
|
||||
import {bindQueryValues} from './query_binder';
|
||||
|
||||
export function bindView(
|
||||
view: CompileView, parsedTemplate: TemplateAst[], schemaRegistry: ElementSchemaRegistry): void {
|
||||
const visitor = new ViewBinderVisitor(view, schemaRegistry);
|
||||
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, private _schemaRegistry: ElementSchemaRegistry) {}
|
||||
|
||||
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
|
||||
const 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 {
|
||||
const compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
|
||||
bindQueryValues(compileElement);
|
||||
const hasEvents = bindOutputs(ast.outputs, ast.directives, compileElement, true);
|
||||
bindRenderInputs(ast.inputs, ast.outputs, hasEvents, compileElement);
|
||||
ast.directives.forEach((directiveAst, dirIndex) => {
|
||||
const directiveWrapperInstance =
|
||||
compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference);
|
||||
bindDirectiveInputs(directiveAst, directiveWrapperInstance, dirIndex, compileElement);
|
||||
bindDirectiveHostProps(
|
||||
directiveAst, directiveWrapperInstance, compileElement, ast.name, this._schemaRegistry);
|
||||
});
|
||||
templateVisitAll(this, ast.children, compileElement);
|
||||
// afterContent and afterView lifecycles need to be called bottom up
|
||||
// so that children are notified before parents
|
||||
ast.directives.forEach((directiveAst) => {
|
||||
const directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
|
||||
const directiveWrapperInstance =
|
||||
compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference);
|
||||
bindDirectiveAfterContentLifecycleCallbacks(
|
||||
directiveAst.directive, directiveInstance, compileElement);
|
||||
bindDirectiveAfterViewLifecycleCallbacks(
|
||||
directiveAst.directive, directiveInstance, compileElement);
|
||||
bindDirectiveWrapperLifecycleCallbacks(
|
||||
directiveAst, directiveWrapperInstance, compileElement);
|
||||
});
|
||||
ast.providers.forEach((providerAst) => {
|
||||
const providerInstance = compileElement.instances.get(tokenReference(providerAst.token));
|
||||
bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement);
|
||||
});
|
||||
return null;
|
||||
}
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, parent: CompileElement): any {
|
||||
const compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
|
||||
bindQueryValues(compileElement);
|
||||
bindOutputs(ast.outputs, ast.directives, compileElement, false);
|
||||
ast.directives.forEach((directiveAst, dirIndex) => {
|
||||
const directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
|
||||
const directiveWrapperInstance =
|
||||
compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference);
|
||||
bindDirectiveInputs(directiveAst, directiveWrapperInstance, dirIndex, compileElement);
|
||||
|
||||
bindDirectiveAfterContentLifecycleCallbacks(
|
||||
directiveAst.directive, directiveInstance, compileElement);
|
||||
bindDirectiveAfterViewLifecycleCallbacks(
|
||||
directiveAst.directive, directiveInstance, compileElement);
|
||||
bindDirectiveWrapperLifecycleCallbacks(
|
||||
directiveAst, directiveWrapperInstance, compileElement);
|
||||
});
|
||||
ast.providers.forEach((providerAst) => {
|
||||
const providerInstance = compileElement.instances.get(tokenReference(providerAst.token));
|
||||
bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement);
|
||||
});
|
||||
bindView(compileElement.embeddedView, ast.children, this._schemaRegistry);
|
||||
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; }
|
||||
}
|
@ -1,697 +0,0 @@
|
||||
/**
|
||||
* @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, ɵChangeDetectorStatus as ChangeDetectorStatus, ɵViewType as ViewType, ɵisDefaultChangeDetectionStrategy as isDefaultChangeDetectionStrategy} from '@angular/core';
|
||||
import {CompileDirectiveSummary, identifierModuleUrl, identifierName} from '../compile_metadata';
|
||||
import {legacyCreateSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter';
|
||||
import {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util';
|
||||
import {isPresent} from '../facade/lang';
|
||||
import {Identifiers, createIdentifier, identifierToken} from '../identifiers';
|
||||
import {createClassStmt} from '../output/class_builder';
|
||||
import * as o from '../output/output_ast';
|
||||
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, InjectMethodVars, ViewConstructorVars, ViewEncapsulationEnum, ViewProperties, ViewTypeEnum} from './constants';
|
||||
import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency} from './deps';
|
||||
|
||||
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
|
||||
const CLASS_ATTR = 'class';
|
||||
const STYLE_ATTR = 'style';
|
||||
const NG_CONTAINER_TAG = 'ng-container';
|
||||
|
||||
const parentRenderNodeVar = o.variable('parentRenderNode');
|
||||
const rootSelectorVar = o.variable('rootSelector');
|
||||
|
||||
export function buildView(
|
||||
view: CompileView, template: TemplateAst[],
|
||||
targetDependencies:
|
||||
Array<ComponentViewDependency|ComponentFactoryDependency|DirectiveWrapperDependency>):
|
||||
number {
|
||||
const 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.nodes.forEach((node) => {
|
||||
if (node instanceof CompileElement) {
|
||||
node.finish();
|
||||
if (node.hasEmbeddedView) {
|
||||
finishView(node.embeddedView, targetStatements);
|
||||
}
|
||||
}
|
||||
});
|
||||
view.finish();
|
||||
createViewTopLevelStmts(view, targetStatements);
|
||||
}
|
||||
|
||||
class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
nestedViewCount: number = 0;
|
||||
|
||||
constructor(
|
||||
public view: CompileView,
|
||||
public targetDependencies:
|
||||
Array<ComponentViewDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
|
||||
|
||||
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
|
||||
|
||||
private _addRootNodeAndProject(node: CompileNode) {
|
||||
const projectedNode = _getOuterContainerOrSelf(node);
|
||||
const parent = projectedNode.parent;
|
||||
const ngContentIndex = (<any>projectedNode.sourceAst).ngContentIndex;
|
||||
const 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) {
|
||||
const 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 {
|
||||
const fieldName = `_text_${this.view.nodes.length}`;
|
||||
this.view.fields.push(
|
||||
new o.ClassField(fieldName, o.importType(this.view.genConfig.renderTypes.renderText)));
|
||||
const renderNode = o.THIS_EXPR.prop(fieldName);
|
||||
const compileNode = new CompileNode(parent, this.view, this.view.nodes.length, renderNode, ast);
|
||||
const 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);
|
||||
const parentRenderNode = this._getParentRenderNode(parent);
|
||||
if (parentRenderNode !== o.NULL_EXPR) {
|
||||
this.view.createMethod.addStmt(
|
||||
o.THIS_EXPR.callMethod('projectNodes', [parentRenderNode, 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 {
|
||||
const nodeIndex = this.view.nodes.length;
|
||||
let createRenderNodeExpr: o.Expression;
|
||||
const debugContextExpr = this.view.createMethod.resetDebugInfoExpr(nodeIndex, ast);
|
||||
const directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||||
const 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(createIdentifier(Identifiers.selectOrCreateRenderHostElement)).callFn([
|
||||
ViewProperties.renderer, o.literal(ast.name), attrNameAndValues, rootSelectorVar,
|
||||
debugContextExpr
|
||||
]);
|
||||
} else {
|
||||
createRenderNodeExpr =
|
||||
o.importExpr(createIdentifier(Identifiers.createRenderElement)).callFn([
|
||||
ViewProperties.renderer, this._getParentRenderNode(parent), o.literal(ast.name),
|
||||
attrNameAndValues, debugContextExpr
|
||||
]);
|
||||
}
|
||||
}
|
||||
const 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());
|
||||
|
||||
const renderNode = o.THIS_EXPR.prop(fieldName);
|
||||
|
||||
const compileElement = new CompileElement(
|
||||
parent, this.view, nodeIndex, renderNode, ast, component, directives, ast.providers,
|
||||
ast.hasViewContainer, false, ast.references);
|
||||
this.view.nodes.push(compileElement);
|
||||
let compViewExpr: o.ReadPropExpr = null;
|
||||
if (isPresent(component)) {
|
||||
this.targetDependencies.push(new ComponentViewDependency(component.type.reference));
|
||||
|
||||
compViewExpr = o.THIS_EXPR.prop(`compView_${nodeIndex}`); // fix highlighting: `
|
||||
this.view.fields.push(new o.ClassField(
|
||||
compViewExpr.name,
|
||||
o.importType(createIdentifier(Identifiers.AppView), [o.importType(component.type)])));
|
||||
this.view.viewChildren.push(compViewExpr);
|
||||
compileElement.setComponentView(compViewExpr);
|
||||
this.view.createMethod.addStmt(
|
||||
compViewExpr
|
||||
.set(o.importExpr({reference: component.componentViewType}).instantiate([
|
||||
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 {
|
||||
const nodeIndex = this.view.nodes.length;
|
||||
const 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());
|
||||
const renderNode = o.THIS_EXPR.prop(fieldName);
|
||||
|
||||
const templateVariableBindings = ast.variables.map(
|
||||
varAst => [varAst.value.length > 0 ? varAst.value : IMPLICIT_TEMPLATE_VAR, varAst.name]);
|
||||
|
||||
const directives = ast.directives.map(directiveAst => directiveAst.directive);
|
||||
const 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++;
|
||||
const 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.targetDependencies);
|
||||
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<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; }
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() && (<ElementAst>node.sourceAst).name === NG_CONTAINER_TAG &&
|
||||
node.view === view;
|
||||
}
|
||||
|
||||
|
||||
function _mergeHtmlAndDirectiveAttrs(
|
||||
declaredHtmlAttrs: {[key: string]: string}, directives: CompileDirectiveSummary[]): 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} {
|
||||
const 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[]) {
|
||||
let nodeDebugInfosVar: o.Expression = o.NULL_EXPR;
|
||||
if (view.genConfig.genDebugInfo) {
|
||||
nodeDebugInfosVar = o.variable(
|
||||
`nodeDebugInfos_${identifierName(view.component.type)}${view.viewIndex}`); // fix
|
||||
// highlighting:
|
||||
// `
|
||||
targetStatements.push(
|
||||
(<o.ReadVarExpr>nodeDebugInfosVar)
|
||||
.set(o.literalArr(
|
||||
view.nodes.map(createStaticNodeDebugInfo),
|
||||
new o.ArrayType(
|
||||
o.importType(createIdentifier(Identifiers.StaticNodeDebugInfo)),
|
||||
[o.TypeModifier.Const])))
|
||||
.toDeclStmt(null, [o.StmtModifier.Final]));
|
||||
}
|
||||
|
||||
|
||||
const renderCompTypeVar: o.ReadVarExpr =
|
||||
o.variable(view.rendererTypeName); // fix highlighting: `
|
||||
if (view.viewIndex === 0) {
|
||||
let templateUrlInfo: string;
|
||||
if (view.component.template.templateUrl == identifierModuleUrl(view.component.type)) {
|
||||
templateUrlInfo =
|
||||
`${identifierModuleUrl(view.component.type)} class ${identifierName(view.component.type)} - inline template`;
|
||||
} else {
|
||||
templateUrlInfo = view.component.template.templateUrl;
|
||||
}
|
||||
targetStatements.push(
|
||||
renderCompTypeVar
|
||||
.set(o.importExpr(createIdentifier(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]),
|
||||
null, true),
|
||||
]))
|
||||
.toDeclStmt(o.importType(createIdentifier(Identifiers.RenderComponentType))));
|
||||
}
|
||||
|
||||
const viewClass = createViewClass(view, renderCompTypeVar, nodeDebugInfosVar);
|
||||
targetStatements.push(viewClass);
|
||||
}
|
||||
|
||||
function createStaticNodeDebugInfo(node: CompileNode): o.Expression {
|
||||
const compileElement = node instanceof CompileElement ? node : null;
|
||||
let providerTokens: o.Expression[] = [];
|
||||
let componentToken: o.Expression = o.NULL_EXPR;
|
||||
const varTokenEntries: any[] = [];
|
||||
if (isPresent(compileElement)) {
|
||||
providerTokens =
|
||||
compileElement.getProviderTokens().map((token) => createDiTokenExpression(token));
|
||||
|
||||
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(createIdentifier(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(
|
||||
createIdentifier(Identifiers.StaticNodeDebugInfo), null, [o.TypeModifier.Const]));
|
||||
}
|
||||
|
||||
function createViewClass(
|
||||
view: CompileView, renderCompTypeVar: o.ReadVarExpr,
|
||||
nodeDebugInfosVar: o.Expression): o.ClassStmt {
|
||||
const viewConstructorArgs = [
|
||||
new o.FnParam(
|
||||
ViewConstructorVars.viewUtils.name, o.importType(createIdentifier(Identifiers.ViewUtils))),
|
||||
new o.FnParam(
|
||||
ViewConstructorVars.parentView.name,
|
||||
o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
|
||||
new o.FnParam(ViewConstructorVars.parentIndex.name, o.NUMBER_TYPE),
|
||||
new o.FnParam(ViewConstructorVars.parentElement.name, o.DYNAMIC_TYPE)
|
||||
];
|
||||
const 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);
|
||||
}
|
||||
if (view.viewType === ViewType.EMBEDDED) {
|
||||
viewConstructorArgs.push(new o.FnParam(
|
||||
'declaredViewContainer', o.importType(createIdentifier(Identifiers.ViewContainer))));
|
||||
superConstructorArgs.push(o.variable('declaredViewContainer'));
|
||||
}
|
||||
const viewMethods = [
|
||||
new o.ClassMethod(
|
||||
'createInternal', [new o.FnParam(rootSelectorVar.name, o.STRING_TYPE)],
|
||||
generateCreateMethod(view),
|
||||
o.importType(createIdentifier(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', [], 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);
|
||||
const superClass = view.genConfig.genDebugInfo ? Identifiers.DebugAppView : Identifiers.AppView;
|
||||
|
||||
const viewClass = createClassStmt({
|
||||
name: view.className,
|
||||
parent: o.importExpr(createIdentifier(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 generateCreateMethod(view: CompileView): o.Statement[] {
|
||||
let parentRenderNodeExpr: o.Expression = o.NULL_EXPR;
|
||||
let 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])];
|
||||
}
|
||||
let resultExpr: o.Expression;
|
||||
if (view.viewType === ViewType.HOST) {
|
||||
const hostEl = <CompileElement>view.nodes[0];
|
||||
resultExpr =
|
||||
o.importExpr(createIdentifier(Identifiers.ComponentRef_), [o.DYNAMIC_TYPE]).instantiate([
|
||||
o.literal(hostEl.nodeIndex), o.THIS_EXPR, hostEl.renderNode, hostEl.getComponent()
|
||||
]);
|
||||
} else {
|
||||
resultExpr = o.NULL_EXPR;
|
||||
}
|
||||
const allNodesExpr: o.Expression =
|
||||
ViewProperties.renderer.cast(o.DYNAMIC_TYPE)
|
||||
.prop('directRenderer')
|
||||
.conditional(o.NULL_EXPR, o.literalArr(view.nodes.map(node => node.renderNode)));
|
||||
|
||||
return parentRenderNodeStmts.concat(view.createMethod.finish(), [
|
||||
o.THIS_EXPR
|
||||
.callMethod(
|
||||
'init',
|
||||
[
|
||||
view.lastRenderNode,
|
||||
allNodesExpr,
|
||||
view.disposables.length ? o.literalArr(view.disposables) : o.NULL_EXPR,
|
||||
])
|
||||
.toStmt(),
|
||||
new o.ReturnStatement(resultExpr)
|
||||
]);
|
||||
}
|
||||
|
||||
function generateDetectChangesMethod(view: CompileView): o.Statement[] {
|
||||
const stmts: any[] = [];
|
||||
if (view.animationBindingsMethod.isEmpty() && view.detectChangesInInputsMethod.isEmpty() &&
|
||||
view.updateContentQueriesMethod.isEmpty() &&
|
||||
view.afterContentLifecycleCallbacksMethod.isEmpty() &&
|
||||
view.detectChangesRenderPropertiesMethod.isEmpty() &&
|
||||
view.updateViewQueriesMethod.isEmpty() && view.afterViewLifecycleCallbacksMethod.isEmpty() &&
|
||||
view.viewContainers.length === 0 && view.viewChildren.length === 0) {
|
||||
return stmts;
|
||||
}
|
||||
stmts.push(...view.animationBindingsMethod.finish());
|
||||
stmts.push(...view.detectChangesInInputsMethod.finish());
|
||||
view.viewContainers.forEach((viewContainer) => {
|
||||
stmts.push(
|
||||
viewContainer.callMethod('detectChangesInNestedViews', [ViewProperties.throwOnChange])
|
||||
.toStmt());
|
||||
});
|
||||
const afterContentStmts = view.updateContentQueriesMethod.finish().concat(
|
||||
view.afterContentLifecycleCallbacksMethod.finish());
|
||||
if (afterContentStmts.length > 0) {
|
||||
stmts.push(new o.IfStmt(o.not(ViewProperties.throwOnChange), afterContentStmts));
|
||||
}
|
||||
stmts.push(...view.detectChangesRenderPropertiesMethod.finish());
|
||||
view.viewChildren.forEach((viewChild) => {
|
||||
stmts.push(
|
||||
viewChild.callMethod('internalDetectChanges', [ViewProperties.throwOnChange]).toStmt());
|
||||
});
|
||||
const afterViewStmts =
|
||||
view.updateViewQueriesMethod.finish().concat(view.afterViewLifecycleCallbacksMethod.finish());
|
||||
if (afterViewStmts.length > 0) {
|
||||
stmts.push(new o.IfStmt(o.not(ViewProperties.throwOnChange), afterViewStmts));
|
||||
}
|
||||
|
||||
const varStmts = legacyCreateSharedBindingVariablesIfNeeded(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 {
|
||||
let 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): o.ClassMethod {
|
||||
const nodeIndexVar = o.variable('nodeIndex');
|
||||
const stmts: o.Statement[] = [];
|
||||
view.nodes.forEach((node) => {
|
||||
if (node instanceof CompileElement) {
|
||||
if (node.embeddedView) {
|
||||
stmts.push(new o.IfStmt(
|
||||
nodeIndexVar.equals(o.literal(node.nodeIndex)),
|
||||
[new o.ReturnStatement(node.embeddedView.classExpr.instantiate([
|
||||
ViewProperties.viewUtils, o.THIS_EXPR, o.literal(node.nodeIndex), node.renderNode,
|
||||
node.viewContainer
|
||||
]))]));
|
||||
}
|
||||
}
|
||||
});
|
||||
if (stmts.length > 0) {
|
||||
stmts.push(new o.ReturnStatement(o.NULL_EXPR));
|
||||
}
|
||||
return new o.ClassMethod(
|
||||
'createEmbeddedViewInternal', [new o.FnParam(nodeIndexVar.name, o.NUMBER_TYPE)], stmts,
|
||||
o.importType(createIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE]));
|
||||
}
|
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user