refactor(compiler): introduce directive wrappers to generate less code

- for now only wraps the `@Input` properties and calls
  to `ngOnInit`, `ngDoCheck` and `ngOnChanges` of directives.
- also groups eval sources by NgModule.

Part of #11683
This commit is contained in:
Tobias Bosch 2016-10-13 16:34:37 -07:00 committed by Alex Rickabaugh
parent c951822c35
commit b0a03fcab3
23 changed files with 606 additions and 278 deletions

View File

@ -11,7 +11,7 @@
* Intended to be used in a build step.
*/
import * as compiler from '@angular/compiler';
import {Component, NgModule, ViewEncapsulation} from '@angular/core';
import {Directive, NgModule, ViewEncapsulation} from '@angular/core';
import {AngularCompilerOptions, NgcCliOptions} from '@angular/tsc-wrapped';
import * as path from 'path';
import * as ts from 'typescript';
@ -58,7 +58,7 @@ export class CodeGeneratorModuleCollector {
private readFileMetadata(absSourcePath: string): FileMetadata {
const moduleMetadata = this.staticReflector.getModuleMetadata(absSourcePath);
const result: FileMetadata = {components: [], ngModules: [], fileUrl: absSourcePath};
const result: FileMetadata = {directives: [], ngModules: [], fileUrl: absSourcePath};
if (!moduleMetadata) {
console.log(`WARNING: no metadata found for ${absSourcePath}`);
return result;
@ -78,8 +78,8 @@ export class CodeGeneratorModuleCollector {
annotations.forEach((annotation) => {
if (annotation instanceof NgModule) {
result.ngModules.push(staticType);
} else if (annotation instanceof Component) {
result.components.push(staticType);
} else if (annotation instanceof Directive) {
result.directives.push(staticType);
}
});
}
@ -127,7 +127,7 @@ export class CodeGenerator {
(fileMeta) =>
this.compiler
.compile(
fileMeta.fileUrl, analyzedNgModules, fileMeta.components, fileMeta.ngModules)
fileMeta.fileUrl, analyzedNgModules, fileMeta.directives, fileMeta.ngModules)
.then((generatedModules) => {
generatedModules.forEach((generatedModule) => {
const sourceFile = this.program.getSourceFile(fileMeta.fileUrl);
@ -193,8 +193,9 @@ export class CodeGenerator {
// TODO(vicb): do not pass cliOptions.i18nFormat here
const offlineCompiler = new compiler.OfflineCompiler(
resolver, normalizer, tmplParser, new compiler.StyleCompiler(urlResolver),
new compiler.ViewCompiler(config), new compiler.NgModuleCompiler(),
new compiler.TypeScriptEmitter(reflectorHost), cliOptions.locale, cliOptions.i18nFormat);
new compiler.ViewCompiler(config), new compiler.DirectiveWrapperCompiler(config),
new compiler.NgModuleCompiler(), new compiler.TypeScriptEmitter(reflectorHost),
cliOptions.locale, cliOptions.i18nFormat);
return new CodeGenerator(
options, program, compilerHost, staticReflector, offlineCompiler, reflectorHost);
@ -203,6 +204,6 @@ export class CodeGenerator {
export interface FileMetadata {
fileUrl: string;
components: StaticSymbol[];
directives: StaticSymbol[];
ngModules: StaticSymbol[];
}

View File

@ -113,7 +113,7 @@ export class Extractor {
const url = fileMeta.fileUrl;
return Promise.all(fileMeta.components.map(compType => {
const compMeta = this.metadataResolver.getDirectiveMetadata(<any>compType);
const ngModule = analyzedNgModules.ngModuleByComponent.get(compType);
const ngModule = analyzedNgModules.ngModuleByDirective.get(compType);
if (!ngModule) {
throw new Error(
`Cannot determine the module for component ${compMeta.type.name}!`);

View File

@ -44,6 +44,7 @@ export * from './src/metadata_resolver';
export * from './src/ml_parser/html_parser';
export * from './src/ml_parser/interpolation_config';
export {NgModuleCompiler} from './src/ng_module_compiler';
export {DirectiveWrapperCompiler} from './src/directive_wrapper_compiler';
export * from './src/output/path_util';
export * from './src/output/ts_emitter';
export * from './src/parse_util';

View File

@ -11,6 +11,7 @@ import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, In
import {CompilerConfig} from './config';
import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveResolver} from './directive_resolver';
import {DirectiveWrapperCompiler} from './directive_wrapper_compiler';
import {Lexer} from './expression_parser/lexer';
import {Parser} from './expression_parser/parser';
import * as i18n from './i18n/index';
@ -64,6 +65,7 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
StyleCompiler,
ViewCompiler,
NgModuleCompiler,
DirectiveWrapperCompiler,
{provide: CompilerConfig, useValue: new CompilerConfig()},
RuntimeCompiler,
{provide: Compiler, useExisting: RuntimeCompiler},

View File

@ -0,0 +1,186 @@
/**
* @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 {Injectable} from '@angular/core';
import {CompileDirectiveMetadata, CompileIdentifierMetadata} from './compile_metadata';
import {CompilerConfig} from './config';
import {Identifiers, resolveIdentifier} from './identifiers';
import * as o from './output/output_ast';
import {LifecycleHooks, isDefaultChangeDetectionStrategy} from './private_import_core';
export class DirectiveWrapperCompileResult {
constructor(public statements: o.Statement[], public dirWrapperClassVar: string) {}
}
const CONTEXT_FIELD_NAME = 'context';
const CHANGES_FIELD_NAME = 'changes';
const CHANGED_FIELD_NAME = 'changed';
const CURR_VALUE_VAR = o.variable('currValue');
const THROW_ON_CHANGE_VAR = o.variable('throwOnChange');
const FORCE_UPDATE_VAR = o.variable('forceUpdate');
const VIEW_VAR = o.variable('view');
const RENDER_EL_VAR = o.variable('el');
const RESET_CHANGES_STMT = o.THIS_EXPR.prop(CHANGES_FIELD_NAME).set(o.literalMap([])).toStmt();
/**
* We generate directive wrappers to prevent code bloat when a directive is used.
* A directive wrapper encapsulates
* the dirty checking for `@Input`, the handling of `@HostListener` / `@HostBinding`
* and calling the lifecyclehooks `ngOnInit`, `ngOnChanges`, `ngDoCheck`.
*
* So far, only `@Input` and the lifecycle hooks have been implemented.
*/
@Injectable()
export class DirectiveWrapperCompiler {
static dirWrapperClassName(id: CompileIdentifierMetadata) { return `Wrapper_${id.name}`; }
constructor(private compilerConfig: CompilerConfig) {}
compile(dirMeta: CompileDirectiveMetadata): DirectiveWrapperCompileResult {
const dirDepParamNames: string[] = [];
for (let i = 0; i < dirMeta.type.diDeps.length; i++) {
dirDepParamNames.push(`p${i}`);
}
const dirLifecycleHooks = dirMeta.type.lifecycleHooks;
let lifecycleHooks: GenConfig = {
genChanges: dirLifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1 ||
this.compilerConfig.logBindingUpdate,
ngOnChanges: dirLifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1,
ngOnInit: dirLifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1,
ngDoCheck: dirLifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1
};
const fields: o.ClassField[] = [
new o.ClassField(CONTEXT_FIELD_NAME, o.importType(dirMeta.type)),
new o.ClassField(CHANGED_FIELD_NAME, o.BOOL_TYPE),
];
const ctorStmts: o.Statement[] =
[o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt()];
if (lifecycleHooks.genChanges) {
fields.push(new o.ClassField(CHANGES_FIELD_NAME, new o.MapType(o.DYNAMIC_TYPE)));
ctorStmts.push(RESET_CHANGES_STMT);
}
const methods: o.ClassMethod[] = [];
Object.keys(dirMeta.inputs).forEach((inputFieldName, idx) => {
const fieldName = `_${inputFieldName}`;
// private is fine here as no child view will reference the cached value...
fields.push(new o.ClassField(fieldName, null, [o.StmtModifier.Private]));
ctorStmts.push(o.THIS_EXPR.prop(fieldName)
.set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)))
.toStmt());
methods.push(checkInputMethod(inputFieldName, o.THIS_EXPR.prop(fieldName), lifecycleHooks));
});
methods.push(detectChangesInternalMethod(lifecycleHooks, this.compilerConfig.genDebugInfo));
ctorStmts.push(
o.THIS_EXPR.prop(CONTEXT_FIELD_NAME)
.set(o.importExpr(dirMeta.type)
.instantiate(dirDepParamNames.map((paramName) => o.variable(paramName))))
.toStmt());
const ctor = new o.ClassMethod(
null, dirDepParamNames.map((paramName) => new o.FnParam(paramName, o.DYNAMIC_TYPE)),
ctorStmts);
const wrapperClassName = DirectiveWrapperCompiler.dirWrapperClassName(dirMeta.type);
const classStmt = new o.ClassStmt(wrapperClassName, null, fields, [], ctor, methods);
return new DirectiveWrapperCompileResult([classStmt], wrapperClassName);
}
}
function detectChangesInternalMethod(
lifecycleHooks: GenConfig, logBindingUpdate: boolean): o.ClassMethod {
const changedVar = o.variable('changed');
const stmts: o.Statement[] = [
changedVar.set(o.THIS_EXPR.prop(CHANGED_FIELD_NAME)).toDeclStmt(),
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(false)).toStmt(),
];
const lifecycleStmts: o.Statement[] = [];
if (lifecycleHooks.genChanges) {
const onChangesStmts: o.Statement[] = [];
if (lifecycleHooks.ngOnChanges) {
onChangesStmts.push(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME)
.callMethod('ngOnChanges', [o.THIS_EXPR.prop(CHANGES_FIELD_NAME)])
.toStmt());
}
if (logBindingUpdate) {
onChangesStmts.push(
o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfoForChanges))
.callFn(
[VIEW_VAR.prop('renderer'), RENDER_EL_VAR, o.THIS_EXPR.prop(CHANGES_FIELD_NAME)])
.toStmt());
}
onChangesStmts.push(RESET_CHANGES_STMT);
lifecycleStmts.push(new o.IfStmt(changedVar, onChangesStmts));
}
if (lifecycleHooks.ngOnInit) {
lifecycleStmts.push(new o.IfStmt(
VIEW_VAR.prop('numberOfChecks').identical(new o.LiteralExpr(0)),
[o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngOnInit', []).toStmt()]));
}
if (lifecycleHooks.ngDoCheck) {
lifecycleStmts.push(o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).callMethod('ngDoCheck', []).toStmt());
}
if (lifecycleStmts.length > 0) {
stmts.push(new o.IfStmt(o.not(THROW_ON_CHANGE_VAR), lifecycleStmts));
}
stmts.push(new o.ReturnStatement(changedVar));
return new o.ClassMethod(
'detectChangesInternal',
[
new o.FnParam(
VIEW_VAR.name, o.importType(resolveIdentifier(Identifiers.AppView), [o.DYNAMIC_TYPE])),
new o.FnParam(RENDER_EL_VAR.name, o.DYNAMIC_TYPE),
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
],
stmts, o.BOOL_TYPE);
}
function checkInputMethod(
input: string, fieldExpr: o.ReadPropExpr, lifecycleHooks: GenConfig): o.ClassMethod {
var onChangeStatements: o.Statement[] = [
o.THIS_EXPR.prop(CHANGED_FIELD_NAME).set(o.literal(true)).toStmt(),
o.THIS_EXPR.prop(CONTEXT_FIELD_NAME).prop(input).set(CURR_VALUE_VAR).toStmt(),
];
if (lifecycleHooks.genChanges) {
onChangeStatements.push(o.THIS_EXPR.prop(CHANGES_FIELD_NAME)
.key(o.literal(input))
.set(o.importExpr(resolveIdentifier(Identifiers.SimpleChange))
.instantiate([fieldExpr, CURR_VALUE_VAR]))
.toStmt());
}
onChangeStatements.push(fieldExpr.set(CURR_VALUE_VAR).toStmt());
var methodBody: o.Statement[] = [
new o.IfStmt(
FORCE_UPDATE_VAR.or(o.importExpr(resolveIdentifier(Identifiers.checkBinding))
.callFn([THROW_ON_CHANGE_VAR, fieldExpr, CURR_VALUE_VAR])),
onChangeStatements),
];
return new o.ClassMethod(
`check_${input}`,
[
new o.FnParam(CURR_VALUE_VAR.name, o.DYNAMIC_TYPE),
new o.FnParam(THROW_ON_CHANGE_VAR.name, o.BOOL_TYPE),
new o.FnParam(FORCE_UPDATE_VAR.name, o.BOOL_TYPE),
],
methodBody);
}
interface GenConfig {
genChanges: boolean;
ngOnChanges: boolean;
ngOnInit: boolean;
ngDoCheck: boolean;
}

View File

@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, LOCALE_ID as LOCALE_ID_, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT as TRANSLATIONS_FORMAT_, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {ANALYZE_FOR_ENTRY_COMPONENTS, AnimationTransitionEvent, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes, castByValue, checkBinding, clearStyles, collectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, reflector, registerModuleFactory, renderStyles} from './private_import_core';
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, view_utils} from './private_import_core';
import {assetUrl} from './util';
var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
@ -33,7 +33,7 @@ export class Identifiers {
static ViewUtils: IdentifierSpec = {
name: 'ViewUtils',
moduleUrl: assetUrl('core', 'linker/view_utils'),
runtime: ViewUtils
runtime: view_utils.ViewUtils
};
static AppView:
IdentifierSpec = {name: 'AppView', moduleUrl: APP_VIEW_MODULE_URL, runtime: AppView};
@ -161,45 +161,48 @@ export class Identifiers {
static checkBinding: IdentifierSpec = {
name: 'checkBinding',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: checkBinding
runtime: view_utils.checkBinding
};
static flattenNestedViewRenderNodes: IdentifierSpec = {
name: 'flattenNestedViewRenderNodes',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: flattenNestedViewRenderNodes
runtime: view_utils.flattenNestedViewRenderNodes
};
static devModeEqual:
IdentifierSpec = {name: 'devModeEqual', moduleUrl: CD_MODULE_URL, runtime: devModeEqual};
static interpolate: IdentifierSpec = {
name: 'interpolate',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: interpolate
runtime: view_utils.interpolate
};
static castByValue: IdentifierSpec = {
name: 'castByValue',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: castByValue
runtime: view_utils.castByValue
};
static EMPTY_ARRAY: IdentifierSpec = {
name: 'EMPTY_ARRAY',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: EMPTY_ARRAY
runtime: view_utils.EMPTY_ARRAY
};
static EMPTY_MAP: IdentifierSpec = {
name: 'EMPTY_MAP',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.EMPTY_MAP
};
static EMPTY_MAP:
IdentifierSpec = {name: 'EMPTY_MAP', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: EMPTY_MAP};
static pureProxies = [
null,
{name: 'pureProxy1', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy1},
{name: 'pureProxy2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy2},
{name: 'pureProxy3', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy3},
{name: 'pureProxy4', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy4},
{name: 'pureProxy5', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy5},
{name: 'pureProxy6', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy6},
{name: 'pureProxy7', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy7},
{name: 'pureProxy8', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy8},
{name: 'pureProxy9', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy9},
{name: 'pureProxy10', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: pureProxy10},
{name: 'pureProxy1', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy1},
{name: 'pureProxy2', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy2},
{name: 'pureProxy3', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy3},
{name: 'pureProxy4', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy4},
{name: 'pureProxy5', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy5},
{name: 'pureProxy6', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy6},
{name: 'pureProxy7', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy7},
{name: 'pureProxy8', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy8},
{name: 'pureProxy9', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy9},
{name: 'pureProxy10', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.pureProxy10},
];
static SecurityContext: IdentifierSpec = {
name: 'SecurityContext',
@ -259,12 +262,22 @@ export class Identifiers {
static LOCALE_ID: IdentifierSpec = {
name: 'LOCALE_ID',
moduleUrl: assetUrl('core', 'i18n/tokens'),
runtime: LOCALE_ID_
runtime: LOCALE_ID
};
static TRANSLATIONS_FORMAT: IdentifierSpec = {
name: 'TRANSLATIONS_FORMAT',
moduleUrl: assetUrl('core', 'i18n/tokens'),
runtime: TRANSLATIONS_FORMAT_
runtime: TRANSLATIONS_FORMAT
};
static setBindingDebugInfo: IdentifierSpec = {
name: 'setBindingDebugInfo',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.setBindingDebugInfo
};
static setBindingDebugInfoForChanges: IdentifierSpec = {
name: 'setBindingDebugInfoForChanges',
moduleUrl: VIEW_UTILS_MODULE_URL,
runtime: view_utils.setBindingDebugInfoForChanges
};
static AnimationTransition: IdentifierSpec = {
name: 'AnimationTransition',

View File

@ -116,8 +116,7 @@ export class CompileMetadataResolver {
return null;
}
getDirectiveMetadata(directiveType: Type<any>, throwIfNotFound = true):
cpl.CompileDirectiveMetadata {
getDirectiveMetadata(directiveType: any, throwIfNotFound = true): cpl.CompileDirectiveMetadata {
directiveType = resolveForwardRef(directiveType);
let meta = this._directiveCache.get(directiveType);
if (!meta) {

View File

@ -12,6 +12,7 @@ import {AnimationCompiler} from './animation/animation_compiler';
import {AnimationParser} from './animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, CompileProviderMetadata, StaticSymbol, createHostComponentMeta} from './compile_metadata';
import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveWrapperCompileResult, DirectiveWrapperCompiler} from './directive_wrapper_compiler';
import {Identifiers, resolveIdentifier, resolveIdentifierToken} from './identifiers';
import {CompileMetadataResolver} from './metadata_resolver';
import {NgModuleCompiler} from './ng_module_compiler';
@ -19,7 +20,7 @@ import {OutputEmitter} from './output/abstract_emitter';
import * as o from './output/output_ast';
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
import {TemplateParser} from './template_parser/template_parser';
import {ComponentFactoryDependency, ViewCompileResult, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewCompileResult, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
export class SourceModule {
constructor(public moduleUrl: string, public source: string) {}
@ -27,25 +28,23 @@ export class SourceModule {
export class NgModulesSummary {
constructor(
public ngModuleByComponent: Map<StaticSymbol, CompileNgModuleMetadata>,
public ngModuleByDirective: Map<StaticSymbol, CompileNgModuleMetadata>,
public ngModules: CompileNgModuleMetadata[]) {}
}
export function analyzeModules(
ngModules: StaticSymbol[], metadataResolver: CompileMetadataResolver) {
const ngModuleByComponent = new Map<StaticSymbol, CompileNgModuleMetadata>();
const ngModuleByDirective = new Map<StaticSymbol, CompileNgModuleMetadata>();
const modules: CompileNgModuleMetadata[] = [];
ngModules.forEach((ngModule) => {
const ngModuleMeta = metadataResolver.getNgModuleMetadata(<any>ngModule);
modules.push(ngModuleMeta);
ngModuleMeta.declaredDirectives.forEach((dirMeta: CompileDirectiveMetadata) => {
if (dirMeta.isComponent) {
ngModuleByComponent.set(dirMeta.type.reference, ngModuleMeta);
}
ngModuleByDirective.set(dirMeta.type.reference, ngModuleMeta);
});
});
return new NgModulesSummary(ngModuleByComponent, modules);
return new NgModulesSummary(ngModuleByDirective, modules);
}
export class OfflineCompiler {
@ -56,6 +55,7 @@ export class OfflineCompiler {
private _metadataResolver: CompileMetadataResolver,
private _directiveNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _dirWrapperCompiler: DirectiveWrapperCompiler,
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
private _localeId: string, private _translationFormat: string) {}
@ -69,7 +69,7 @@ export class OfflineCompiler {
}
compile(
moduleUrl: string, ngModulesSummary: NgModulesSummary, components: StaticSymbol[],
moduleUrl: string, ngModulesSummary: NgModulesSummary, directives: StaticSymbol[],
ngModules: StaticSymbol[]): Promise<SourceModule[]> {
const fileSuffix = _splitTypescriptSuffix(moduleUrl)[1];
const statements: o.Statement[] = [];
@ -80,11 +80,18 @@ export class OfflineCompiler {
exportedVars.push(
...ngModules.map((ngModuleType) => this._compileModule(ngModuleType, statements)));
// compile directive wrappers
exportedVars.push(...directives.map(
(directiveType) => this._compileDirectiveWrapper(directiveType, statements)));
// compile components
return Promise
.all(components.map((compType) => {
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>compType);
const ngModule = ngModulesSummary.ngModuleByComponent.get(compType);
.all(directives.map((dirType) => {
const compMeta = this._metadataResolver.getDirectiveMetadata(<any>dirType);
if (!compMeta.isComponent) {
return Promise.resolve(null);
}
const ngModule = ngModulesSummary.ngModuleByDirective.get(dirType);
if (!ngModule) {
throw new Error(`Cannot determine the module for component ${compMeta.type.name}!`);
}
@ -148,6 +155,15 @@ export class OfflineCompiler {
return appCompileResult.ngModuleFactoryVar;
}
private _compileDirectiveWrapper(directiveType: StaticSymbol, targetStatements: o.Statement[]):
string {
const dirMeta = this._metadataResolver.getDirectiveMetadata(directiveType);
const dirCompileResult = this._dirWrapperCompiler.compile(dirMeta);
targetStatements.push(...dirCompileResult.statements);
return dirCompileResult.dirWrapperClassVar;
}
private _compileComponentFactory(
compMeta: CompileDirectiveMetadata, fileSuffix: string,
targetStatements: o.Statement[]): string {
@ -217,6 +233,9 @@ function _resolveViewStatements(compileResult: ViewCompileResult): o.Statement[]
const cfd = <ComponentFactoryDependency>dep;
cfd.placeholder.name = _componentFactoryName(cfd.comp);
cfd.placeholder.moduleUrl = _ngfactoryModuleUrl(cfd.comp.moduleUrl);
} else if (dep instanceof DirectiveWrapperDependency) {
const dwd = <DirectiveWrapperDependency>dep;
dwd.placeholder.moduleUrl = _ngfactoryModuleUrl(dwd.dir.moduleUrl);
}
});
return compileResult.statements;
@ -231,8 +250,8 @@ function _resolveStyleStatements(
return compileResult.statements;
}
function _ngfactoryModuleUrl(compUrl: string): string {
const urlWithSuffix = _splitTypescriptSuffix(compUrl);
function _ngfactoryModuleUrl(dirUrl: string): string {
const urlWithSuffix = _splitTypescriptSuffix(dirUrl);
return `${urlWithSuffix[0]}.ngfactory${urlWithSuffix[1]}`;
}

View File

@ -81,7 +81,7 @@ function createDynamicClass(
_executeFunctionStatements(
ctorParamNames, args, _classStmt.constructorMethod.body, instanceCtx, _visitor);
};
var superClass = _classStmt.parent.visitExpression(_visitor, _ctx);
var superClass = _classStmt.parent ? _classStmt.parent.visitExpression(_visitor, _ctx) : Object;
ctor.prototype = Object.create(superClass.prototype, propertyDescriptors);
return ctor;
}

View File

@ -27,13 +27,7 @@ export const NgModuleInjector: typeof r.NgModuleInjector = r.NgModuleInjector;
export const registerModuleFactory: typeof r.registerModuleFactory = r.registerModuleFactory;
export type ViewType = typeof r._ViewType;
export const ViewType: typeof r.ViewType = r.ViewType;
export const MAX_INTERPOLATION_VALUES: typeof r.MAX_INTERPOLATION_VALUES =
r.MAX_INTERPOLATION_VALUES;
export const checkBinding: typeof r.checkBinding = r.checkBinding;
export const flattenNestedViewRenderNodes: typeof r.flattenNestedViewRenderNodes =
r.flattenNestedViewRenderNodes;
export const interpolate: typeof r.interpolate = r.interpolate;
export const ViewUtils: typeof r.ViewUtils = r.ViewUtils;
export const view_utils: typeof r.view_utils = r.view_utils;
export const DebugContext: typeof r.DebugContext = r.DebugContext;
export const StaticNodeDebugInfo: typeof r.StaticNodeDebugInfo = r.StaticNodeDebugInfo;
export const devModeEqual: typeof r.devModeEqual = r.devModeEqual;
@ -42,19 +36,6 @@ export const ValueUnwrapper: typeof r.ValueUnwrapper = r.ValueUnwrapper;
export const TemplateRef_: typeof r.TemplateRef_ = r.TemplateRef_;
export type RenderDebugInfo = typeof r._RenderDebugInfo;
export const RenderDebugInfo: typeof r.RenderDebugInfo = r.RenderDebugInfo;
export const EMPTY_ARRAY: typeof r.EMPTY_ARRAY = r.EMPTY_ARRAY;
export const EMPTY_MAP: typeof r.EMPTY_MAP = r.EMPTY_MAP;
export const pureProxy1: typeof r.pureProxy1 = r.pureProxy1;
export const pureProxy2: typeof r.pureProxy2 = r.pureProxy2;
export const pureProxy3: typeof r.pureProxy3 = r.pureProxy3;
export const pureProxy4: typeof r.pureProxy4 = r.pureProxy4;
export const pureProxy5: typeof r.pureProxy5 = r.pureProxy5;
export const pureProxy6: typeof r.pureProxy6 = r.pureProxy6;
export const pureProxy7: typeof r.pureProxy7 = r.pureProxy7;
export const pureProxy8: typeof r.pureProxy8 = r.pureProxy8;
export const pureProxy9: typeof r.pureProxy9 = r.pureProxy9;
export const pureProxy10: typeof r.pureProxy10 = r.pureProxy10;
export const castByValue: typeof r.castByValue = r.castByValue;
export type Console = typeof r._Console;
export const Console: typeof r.Console = r.Console;
export const reflector: typeof r.reflector = r.reflector;

View File

@ -7,11 +7,13 @@
*/
import {Compiler, ComponentFactory, Injectable, Injector, ModuleWithComponentFactories, NgModuleFactory, SchemaMetadata, Type} from '@angular/core';
import {AnimationCompiler} from './animation/animation_compiler';
import {AnimationParser} from './animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, CompilePipeMetadata, ProviderMeta, createHostComponentMeta} from './compile_metadata';
import {CompilerConfig} from './config';
import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveWrapperCompiler} from './directive_wrapper_compiler';
import {stringify} from './facade/lang';
import {CompileMetadataResolver} from './metadata_resolver';
import {NgModuleCompiler} from './ng_module_compiler';
@ -22,7 +24,8 @@ import {ComponentStillLoadingError} from './private_import_core';
import {CompiledStylesheet, StyleCompiler} from './style_compiler';
import {TemplateParser} from './template_parser/template_parser';
import {SyncAsyncResult} from './util';
import {ComponentFactoryDependency, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewCompiler, ViewFactoryDependency} from './view_compiler/view_compiler';
/**
* An internal module of the Angular compiler that begins with component types,
@ -37,6 +40,7 @@ import {ComponentFactoryDependency, ViewCompiler, ViewFactoryDependency} from '.
export class RuntimeCompiler implements Compiler {
private _compiledTemplateCache = new Map<Type<any>, CompiledTemplate>();
private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>();
private _compiledDirectiveWrapperCache = new Map<Type<any>, Type<any>>();
private _compiledNgModuleCache = new Map<Type<any>, NgModuleFactory<any>>();
private _animationParser = new AnimationParser();
private _animationCompiler = new AnimationCompiler();
@ -45,7 +49,9 @@ export class RuntimeCompiler implements Compiler {
private _injector: Injector, private _metadataResolver: CompileMetadataResolver,
private _templateNormalizer: DirectiveNormalizer, private _templateParser: TemplateParser,
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
private _ngModuleCompiler: NgModuleCompiler, private _compilerConfig: CompilerConfig) {}
private _ngModuleCompiler: NgModuleCompiler,
private _directiveWrapperCompiler: DirectiveWrapperCompiler,
private _compilerConfig: CompilerConfig) {}
get injector(): Injector { return this._injector; }
@ -80,10 +86,11 @@ export class RuntimeCompiler implements Compiler {
const moduleMeta = this._metadataResolver.getNgModuleMetadata(moduleType);
const componentFactories: ComponentFactory<any>[] = [];
const templates = new Set<CompiledTemplate>();
moduleMeta.transitiveModule.modules.forEach((moduleMeta) => {
moduleMeta.declaredDirectives.forEach((dirMeta) => {
moduleMeta.transitiveModule.modules.forEach((localModuleMeta) => {
localModuleMeta.declaredDirectives.forEach((dirMeta) => {
if (dirMeta.isComponent) {
const template = this._createCompiledHostTemplate(dirMeta.type.reference);
const template =
this._createCompiledHostTemplate(dirMeta.type.reference, localModuleMeta);
templates.add(template);
componentFactories.push(template.proxyComponentFactory);
}
@ -119,7 +126,7 @@ export class RuntimeCompiler implements Compiler {
interpretStatements(compileResult.statements, compileResult.ngModuleFactoryVar);
} else {
ngModuleFactory = jitStatements(
`${moduleMeta.type.name}.ngfactory.js`, compileResult.statements,
`/${moduleMeta.type.name}/module.ngfactory.js`, compileResult.statements,
compileResult.ngModuleFactoryVar);
}
this._compiledNgModuleCache.set(moduleMeta.type.reference, ngModuleFactory);
@ -135,22 +142,32 @@ export class RuntimeCompiler implements Compiler {
var loadingPromises: Promise<any>[] = [];
const ngModule = this._metadataResolver.getNgModuleMetadata(mainModule);
const moduleByDirective = new Map<any, CompileNgModuleMetadata>();
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
localModuleMeta.declaredDirectives.forEach((dirMeta) => {
moduleByDirective.set(dirMeta.type.reference, localModuleMeta);
this._compileDirectiveWrapper(dirMeta, localModuleMeta);
if (dirMeta.isComponent) {
templates.add(this._createCompiledTemplate(dirMeta, localModuleMeta));
}
});
});
ngModule.transitiveModule.modules.forEach((localModuleMeta) => {
localModuleMeta.declaredDirectives.forEach((dirMeta) => {
if (dirMeta.isComponent) {
templates.add(this._createCompiledTemplate(dirMeta, localModuleMeta));
dirMeta.entryComponents.forEach((entryComponentType) => {
templates.add(this._createCompiledHostTemplate(entryComponentType.reference));
const moduleMeta = moduleByDirective.get(entryComponentType.reference);
templates.add(
this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
});
// TODO: what about entryComponents of entryComponents? maybe skip here and just do the
// below?
}
});
localModuleMeta.entryComponents.forEach((entryComponentType) => {
templates.add(this._createCompiledHostTemplate(entryComponentType.reference));
// TODO: what about entryComponents of entryComponents?
const moduleMeta = moduleByDirective.get(entryComponentType.reference);
templates.add(this._createCompiledHostTemplate(entryComponentType.reference, moduleMeta));
});
});
templates.forEach((template) => {
if (template.loading) {
if (isSync) {
@ -189,14 +206,19 @@ export class RuntimeCompiler implements Compiler {
this._compiledNgModuleCache.clear();
}
private _createCompiledHostTemplate(compType: Type<any>): CompiledTemplate {
private _createCompiledHostTemplate(compType: Type<any>, ngModule: CompileNgModuleMetadata):
CompiledTemplate {
if (!ngModule) {
throw new Error(
`Component ${stringify(compType)} is not part of any NgModule or the module has not been imported into your module.`);
}
var compiledTemplate = this._compiledHostTemplateCache.get(compType);
if (!compiledTemplate) {
var compMeta = this._metadataResolver.getDirectiveMetadata(compType);
assertComponent(compMeta);
var hostMeta = createHostComponentMeta(compMeta);
compiledTemplate = new CompiledTemplate(
true, compMeta.selector, compMeta.type, [compMeta], [], [],
true, compMeta.selector, compMeta.type, ngModule, [compMeta],
this._templateNormalizer.normalizeDirective(hostMeta));
this._compiledHostTemplateCache.set(compType, compiledTemplate);
}
@ -209,8 +231,7 @@ export class RuntimeCompiler implements Compiler {
if (!compiledTemplate) {
assertComponent(compMeta);
compiledTemplate = new CompiledTemplate(
false, compMeta.selector, compMeta.type, ngModule.transitiveModule.directives,
ngModule.transitiveModule.pipes, ngModule.schemas,
false, compMeta.selector, compMeta.type, ngModule, ngModule.transitiveModule.directives,
this._templateNormalizer.normalizeDirective(compMeta));
this._compiledTemplateCache.set(compMeta.type.reference, compiledTemplate);
}
@ -221,13 +242,8 @@ export class RuntimeCompiler implements Compiler {
const compiledTemplate = isHost ? this._compiledHostTemplateCache.get(compType) :
this._compiledTemplateCache.get(compType);
if (!compiledTemplate) {
if (isHost) {
throw new Error(
`Illegal state: Compiled view for component ${stringify(compType)} does not exist!`);
} else {
throw new Error(
`Component ${stringify(compType)} is not part of any NgModule or the module has not been imported into your module.`);
}
throw new Error(
`Illegal state: Compiled view for component ${stringify(compType)} does not exist!`);
}
return compiledTemplate;
}
@ -241,6 +257,30 @@ export class RuntimeCompiler implements Compiler {
return compiledTemplate;
}
private _assertDirectiveWrapper(dirType: any): Type<any> {
const dirWrapper = this._compiledDirectiveWrapperCache.get(dirType);
if (!dirWrapper) {
throw new Error(
`Illegal state: Directive wrapper for ${stringify(dirType)} has not been compiled!`);
}
return dirWrapper;
}
private _compileDirectiveWrapper(
dirMeta: CompileDirectiveMetadata, moduleMeta: CompileNgModuleMetadata): void {
const compileResult = this._directiveWrapperCompiler.compile(dirMeta);
const statements = compileResult.statements;
let directiveWrapperClass: any;
if (!this._compilerConfig.useJit) {
directiveWrapperClass = interpretStatements(statements, compileResult.dirWrapperClassVar);
} else {
directiveWrapperClass = jitStatements(
`/${moduleMeta.type.name}/${dirMeta.type.name}/wrapper.ngfactory.js`, statements,
compileResult.dirWrapperClassVar);
}
this._compiledDirectiveWrapperCache.set(dirMeta.type.reference, directiveWrapperClass);
}
private _compileTemplate(template: CompiledTemplate) {
if (template.isCompiled) {
return;
@ -275,6 +315,9 @@ export class RuntimeCompiler implements Compiler {
depTemplate = this._assertComponentLoaded(cfd.comp.reference, true);
cfd.placeholder.reference = depTemplate.proxyComponentFactory;
cfd.placeholder.name = `compFactory_${cfd.comp.name}`;
} else if (dep instanceof DirectiveWrapperDependency) {
let dwd = <DirectiveWrapperDependency>dep;
dwd.placeholder.reference = this._assertDirectiveWrapper(dwd.dir.reference);
}
});
const statements =
@ -286,8 +329,8 @@ export class RuntimeCompiler implements Compiler {
factory = interpretStatements(statements, compileResult.viewFactoryVar);
} else {
factory = jitStatements(
`${template.compType.name}${template.isHost?'_Host':''}.ngfactory.js`, statements,
compileResult.viewFactoryVar);
`/${template.ngModule.type.name}/${template.compType.name}/${template.isHost?'host':'component'}.ngfactory.js`,
statements, compileResult.viewFactoryVar);
}
template.compiled(factory);
}
@ -310,7 +353,7 @@ export class RuntimeCompiler implements Compiler {
if (!this._compilerConfig.useJit) {
return interpretStatements(result.statements, result.stylesVar);
} else {
return jitStatements(`${result.meta.moduleUrl}.css.js`, result.statements, result.stylesVar);
return jitStatements(`/${result.meta.moduleUrl}.css.js`, result.statements, result.stylesVar);
}
}
}
@ -325,13 +368,17 @@ class CompiledTemplate {
isCompiledWithDeps = false;
viewComponentTypes: Type<any>[] = [];
viewDirectives: CompileDirectiveMetadata[] = [];
viewPipes: CompilePipeMetadata[];
schemas: SchemaMetadata[];
constructor(
public isHost: boolean, selector: string, public compType: CompileIdentifierMetadata,
viewDirectivesAndComponents: CompileDirectiveMetadata[],
public viewPipes: CompilePipeMetadata[], public schemas: SchemaMetadata[],
public ngModule: CompileNgModuleMetadata,
viewDirectiveAndComponents: CompileDirectiveMetadata[],
_normalizeResult: SyncAsyncResult<CompileDirectiveMetadata>) {
viewDirectivesAndComponents.forEach((dirMeta) => {
this.viewPipes = ngModule.transitiveModule.pipes;
this.schemas = ngModule.schemas;
viewDirectiveAndComponents.forEach((dirMeta) => {
if (dirMeta.isComponent) {
this.viewComponentTypes.push(dirMeta.type.reference);
} else {

View File

@ -20,7 +20,7 @@ import {expandNodes} from '../ml_parser/icu_ast_expander';
import {InterpolationConfig} from '../ml_parser/interpolation_config';
import {mergeNsAndName, splitNsName} from '../ml_parser/tags';
import {ParseError, ParseErrorLevel, ParseSourceSpan} from '../parse_util';
import {Console, MAX_INTERPOLATION_VALUES} from '../private_import_core';
import {Console, view_utils} from '../private_import_core';
import {ProviderElementContext, ProviderViewContext} from '../provider_analyzer';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {CssSelector, SelectorMatcher} from '../selector';
@ -246,8 +246,9 @@ class TemplateParseVisitor implements html.Visitor {
if (ast) this._reportParserErrors(ast.errors, sourceSpan);
this._checkPipes(ast, sourceSpan);
if (isPresent(ast) &&
(<Interpolation>ast.ast).expressions.length > MAX_INTERPOLATION_VALUES) {
throw new Error(`Only support at most ${MAX_INTERPOLATION_VALUES} interpolation values!`);
(<Interpolation>ast.ast).expressions.length > view_utils.MAX_INTERPOLATION_VALUES) {
throw new Error(
`Only support at most ${view_utils.MAX_INTERPOLATION_VALUES} interpolation values!`);
}
return ast;
} catch (e) {

View File

@ -8,6 +8,7 @@
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileIdentifierMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata} from '../compile_metadata';
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {ListWrapper, MapWrapper} from '../facade/collection';
import {isPresent} from '../facade/lang';
import {Identifiers, identifierToken, resolveIdentifier, resolveIdentifierToken} from '../identifiers';
@ -19,7 +20,8 @@ import {createDiTokenExpression} from '../util';
import {CompileMethod} from './compile_method';
import {CompileQuery, addQueryToTokenMap, createQueryList} from './compile_query';
import {CompileView} from './compile_view';
import {InjectMethodVars} from './constants';
import {InjectMethodVars, ViewProperties} from './constants';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewFactoryDependency} from './deps';
import {getPropertyInView, injectFromViewParentInjector} from './util';
export class CompileNode {
@ -34,7 +36,7 @@ export class CompileNode {
export class CompileElement extends CompileNode {
static createNull(): CompileElement {
return new CompileElement(null, null, null, null, null, null, [], [], false, false, []);
return new CompileElement(null, null, null, null, null, null, [], [], false, false, [], []);
}
private _compViewExpr: o.Expression = null;
@ -42,6 +44,7 @@ export class CompileElement extends CompileNode {
public elementRef: o.Expression;
public injector: o.Expression;
public instances = new Map<any, o.Expression>();
public directiveWrapperInstance = new Map<any, o.Expression>();
private _resolvedProviders: Map<any, ProviderAst>;
private _queryCount = 0;
@ -57,7 +60,9 @@ export class CompileElement extends CompileNode {
sourceAst: TemplateAst, public component: CompileDirectiveMetadata,
private _directives: CompileDirectiveMetadata[],
private _resolvedProvidersArray: ProviderAst[], public hasViewContainer: boolean,
public hasEmbeddedView: boolean, references: ReferenceAst[]) {
public hasEmbeddedView: boolean, references: ReferenceAst[],
private _targetDependencies:
Array<ViewFactoryDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {
super(parent, view, nodeIndex, renderNode, sourceAst);
this.referenceTokens = {};
references.forEach(ref => this.referenceTokens[ref.name] = ref.value);
@ -72,6 +77,9 @@ export class CompileElement extends CompileNode {
if (this.hasViewContainer || this.hasEmbeddedView || isPresent(this.component)) {
this._createAppElement();
}
if (this.component) {
this._createComponentFactoryResolver();
}
}
private _createAppElement() {
@ -92,7 +100,13 @@ export class CompileElement extends CompileNode {
this.instances.set(resolveIdentifierToken(Identifiers.AppElement).reference, this.appElement);
}
public createComponentFactoryResolver(entryComponents: CompileIdentifierMetadata[]) {
private _createComponentFactoryResolver() {
let entryComponents =
this.component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => {
var id = new CompileIdentifierMetadata({name: entryComponent.name});
this._targetDependencies.push(new ComponentFactoryDependency(entryComponent, id));
return id;
});
if (!entryComponents || entryComponents.length === 0) {
return;
}
@ -155,6 +169,8 @@ export class CompileElement extends CompileNode {
// create all the provider instances, some in the view constructor,
// some as getters. We rely on the fact that they are already sorted topologically.
MapWrapper.values(this._resolvedProviders).forEach((resolvedProvider) => {
const isDirectiveWrapper = resolvedProvider.providerType === ProviderAstType.Component ||
resolvedProvider.providerType === ProviderAstType.Directive;
var providerValueExpressions = resolvedProvider.providers.map((provider) => {
if (isPresent(provider.useExisting)) {
return this._getDependency(
@ -167,8 +183,17 @@ export class CompileElement extends CompileNode {
} else if (isPresent(provider.useClass)) {
var deps = provider.deps || provider.useClass.diDeps;
var depsExpr = deps.map((dep) => this._getDependency(resolvedProvider.providerType, dep));
return o.importExpr(provider.useClass)
.instantiate(depsExpr, o.importType(provider.useClass));
if (isDirectiveWrapper) {
const directiveWrapperIdentifier = new CompileIdentifierMetadata(
{name: DirectiveWrapperCompiler.dirWrapperClassName(provider.useClass)});
this._targetDependencies.push(
new DirectiveWrapperDependency(provider.useClass, directiveWrapperIdentifier));
return o.importExpr(directiveWrapperIdentifier)
.instantiate(depsExpr, o.importType(directiveWrapperIdentifier));
} else {
return o.importExpr(provider.useClass)
.instantiate(depsExpr, o.importType(provider.useClass));
}
} else {
return convertValueToOutputAst(provider.useValue);
}
@ -177,7 +202,12 @@ export class CompileElement extends CompileNode {
var instance = createProviderProperty(
propName, resolvedProvider, providerValueExpressions, resolvedProvider.multiProvider,
resolvedProvider.eager, this);
this.instances.set(resolvedProvider.token.reference, instance);
if (isDirectiveWrapper) {
this.directiveWrapperInstance.set(resolvedProvider.token.reference, instance);
this.instances.set(resolvedProvider.token.reference, instance.prop('context'));
} else {
this.instances.set(resolvedProvider.token.reference, instance);
}
});
for (var i = 0; i < this._directives.length; i++) {

View File

@ -0,0 +1,24 @@
/**
* @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 {CompileIdentifierMetadata} from '../compile_metadata';
export class ViewFactoryDependency {
constructor(
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
}
export class ComponentFactoryDependency {
constructor(
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
}
export class DirectiveWrapperDependency {
constructor(
public dir: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
}

View File

@ -20,27 +20,6 @@ import {DetectChangesVars} from './constants';
var STATE_IS_NEVER_CHECKED = o.THIS_EXPR.prop('numberOfChecks').identical(new o.LiteralExpr(0));
var NOT_THROW_ON_CHANGES = o.not(DetectChangesVars.throwOnChange);
export function bindDirectiveDetectChangesLifecycleCallbacks(
directiveAst: DirectiveAst, directiveInstance: o.Expression, compileElement: CompileElement) {
var view = compileElement.view;
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
var lifecycleHooks = directiveAst.directive.type.lifecycleHooks;
if (lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1 && directiveAst.inputs.length > 0) {
detectChangesInInputsMethod.addStmt(new o.IfStmt(
DetectChangesVars.changes.notIdentical(o.NULL_EXPR),
[directiveInstance.callMethod('ngOnChanges', [DetectChangesVars.changes]).toStmt()]));
}
if (lifecycleHooks.indexOf(LifecycleHooks.OnInit) !== -1) {
detectChangesInInputsMethod.addStmt(new o.IfStmt(
STATE_IS_NEVER_CHECKED.and(NOT_THROW_ON_CHANGES),
[directiveInstance.callMethod('ngOnInit', []).toStmt()]));
}
if (lifecycleHooks.indexOf(LifecycleHooks.DoCheck) !== -1) {
detectChangesInInputsMethod.addStmt(new o.IfStmt(
NOT_THROW_ON_CHANGES, [directiveInstance.callMethod('ngDoCheck', []).toStmt()]));
}
}
export function bindDirectiveAfterContentLifecycleCallbacks(
directiveMeta: CompileDirectiveMetadata, directiveInstance: o.Expression,
compileElement: CompileElement) {

View File

@ -32,15 +32,18 @@ function createCurrValueExpr(exprIndex: number): o.ReadVarExpr {
return o.variable(`currVal_${exprIndex}`); // fix syntax highlighting: `
}
function bind(
view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr,
parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[],
method: CompileMethod, bindingIndex: number) {
class EvalResult {
constructor(public forceUpdate: o.Expression) {}
}
function evalCdAst(
view: CompileView, currValExpr: o.ReadVarExpr, parsedExpression: cdAst.AST,
context: o.Expression, method: CompileMethod, bindingIndex: number): EvalResult {
var checkExpression = convertCdExpressionToIr(
view, context, parsedExpression, DetectChangesVars.valUnwrapper, bindingIndex);
if (!checkExpression.expression) {
// e.g. an empty expression was given
return;
return null;
}
if (checkExpression.temporaryCount) {
@ -49,24 +52,39 @@ function bind(
}
}
// private is fine here as no child view will reference the cached value...
view.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
view.createMethod.addStmt(o.THIS_EXPR.prop(fieldExpr.name)
.set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)))
.toStmt());
if (checkExpression.needsValueUnwrapper) {
var initValueUnwrapperStmt = DetectChangesVars.valUnwrapper.callMethod('reset', []).toStmt();
method.addStmt(initValueUnwrapperStmt);
}
method.addStmt(
currValExpr.set(checkExpression.expression).toDeclStmt(null, [o.StmtModifier.Final]));
if (checkExpression.needsValueUnwrapper) {
return new EvalResult(DetectChangesVars.valUnwrapper.prop('hasWrappedValue'));
} else {
return new EvalResult(null);
}
}
function bind(
view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr,
parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[],
method: CompileMethod, bindingIndex: number) {
const evalResult = evalCdAst(view, currValExpr, parsedExpression, context, method, bindingIndex);
if (!evalResult) {
return;
}
// private is fine here as no child view will reference the cached value...
view.fields.push(new o.ClassField(fieldExpr.name, null, [o.StmtModifier.Private]));
view.createMethod.addStmt(o.THIS_EXPR.prop(fieldExpr.name)
.set(o.importExpr(resolveIdentifier(Identifiers.UNINITIALIZED)))
.toStmt());
var condition: o.Expression = o.importExpr(resolveIdentifier(Identifiers.checkBinding)).callFn([
DetectChangesVars.throwOnChange, fieldExpr, currValExpr
]);
if (checkExpression.needsValueUnwrapper) {
condition = DetectChangesVars.valUnwrapper.prop('hasWrappedValue').or(condition);
if (evalResult.forceUpdate) {
condition = evalResult.forceUpdate.or(condition);
}
method.addStmt(new o.IfStmt(
condition,
@ -237,82 +255,48 @@ export function bindDirectiveHostProps(
}
export function bindDirectiveInputs(
directiveAst: DirectiveAst, directiveInstance: o.Expression, compileElement: CompileElement) {
if (directiveAst.inputs.length === 0) {
return;
}
directiveAst: DirectiveAst, directiveWrapperInstance: o.Expression,
compileElement: CompileElement) {
var view = compileElement.view;
var detectChangesInInputsMethod = view.detectChangesInInputsMethod;
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, compileElement.sourceAst);
var lifecycleHooks = directiveAst.directive.type.lifecycleHooks;
var calcChangesMap = lifecycleHooks.indexOf(LifecycleHooks.OnChanges) !== -1;
var isOnPushComp = directiveAst.directive.isComponent &&
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
if (calcChangesMap) {
detectChangesInInputsMethod.addStmt(DetectChangesVars.changes.set(o.NULL_EXPR).toStmt());
}
if (isOnPushComp) {
detectChangesInInputsMethod.addStmt(DetectChangesVars.changed.set(o.literal(false)).toStmt());
}
directiveAst.inputs.forEach((input) => {
var bindingIndex = view.bindings.length;
view.bindings.push(new CompileBinding(compileElement, input));
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
var fieldExpr = createBindFieldExpr(bindingIndex);
var currValExpr = createCurrValueExpr(bindingIndex);
var statements: o.Statement[] =
[directiveInstance.prop(input.directiveName).set(currValExpr).toStmt()];
if (calcChangesMap) {
statements.push(new o.IfStmt(
DetectChangesVars.changes.identical(o.NULL_EXPR),
[DetectChangesVars.changes
.set(o.literalMap(
[], new o.MapType(o.importType(resolveIdentifier(Identifiers.SimpleChange)))))
.toStmt()]));
statements.push(DetectChangesVars.changes.key(o.literal(input.directiveName))
.set(o.importExpr(resolveIdentifier(Identifiers.SimpleChange))
.instantiate([fieldExpr, currValExpr]))
.toStmt());
const evalResult = evalCdAst(
view, currValExpr, input.value, view.componentContext, detectChangesInInputsMethod,
bindingIndex);
if (!evalResult) {
return;
}
if (isOnPushComp) {
statements.push(DetectChangesVars.changed.set(o.literal(true)).toStmt());
}
if (view.genConfig.logBindingUpdate) {
statements.push(
logBindingUpdateStmt(compileElement.renderNode, input.directiveName, currValExpr));
}
bind(
view, currValExpr, fieldExpr, input.value, view.componentContext, statements,
detectChangesInInputsMethod, bindingIndex);
detectChangesInInputsMethod.addStmt(directiveWrapperInstance
.callMethod(
`check_${input.directiveName}`,
[
currValExpr, DetectChangesVars.throwOnChange,
evalResult.forceUpdate || o.literal(false)
])
.toStmt());
});
if (isOnPushComp) {
detectChangesInInputsMethod.addStmt(new o.IfStmt(DetectChangesVars.changed, [
compileElement.appElement.prop('componentView').callMethod('markAsCheckOnce', []).toStmt()
]));
}
var isOnPushComp = directiveAst.directive.isComponent &&
!isDefaultChangeDetectionStrategy(directiveAst.directive.changeDetection);
let directiveDetectChangesExpr = directiveWrapperInstance.callMethod(
'detectChangesInternal',
[o.THIS_EXPR, compileElement.renderNode, DetectChangesVars.throwOnChange]);
const directiveDetectChangesStmt = isOnPushComp ?
new o.IfStmt(directiveDetectChangesExpr, [compileElement.appElement.prop('componentView')
.callMethod('markAsCheckOnce', [])
.toStmt()]) :
directiveDetectChangesExpr.toStmt();
detectChangesInInputsMethod.addStmt(directiveDetectChangesStmt);
}
function logBindingUpdateStmt(
renderNode: o.Expression, propName: string, value: o.Expression): o.Statement {
const tryStmt =
o.THIS_EXPR.prop('renderer')
.callMethod(
'setBindingDebugInfo',
[
renderNode, o.literal(`ng-reflect-${camelCaseToDashCase(propName)}`),
value.isBlank().conditional(o.NULL_EXPR, value.callMethod('toString', []))
])
.toStmt();
const catchStmt = o.THIS_EXPR.prop('renderer')
.callMethod(
'setBindingDebugInfo',
[
renderNode, o.literal(`ng-reflect-${camelCaseToDashCase(propName)}`),
o.literal('[ERROR] Exception while trying to serialize the value')
])
.toStmt();
return new o.TryCatchStmt([tryStmt], [catchStmt]);
return o.importExpr(resolveIdentifier(Identifiers.setBindingDebugInfo))
.callFn([o.THIS_EXPR.prop('renderer'), renderNode, o.literal(propName), value])
.toStmt();
}

View File

@ -7,7 +7,7 @@
*/
import {CompileDirectiveMetadata, CompileTokenMetadata} from '../compile_metadata';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata} from '../compile_metadata';
import {isPresent} from '../facade/lang';
import {Identifiers, resolveIdentifier} from '../identifiers';
import * as o from '../output/output_ast';
@ -30,15 +30,28 @@ export function getPropertyInView(
throw new Error(
`Internal error: Could not calculate a property in a parent view: ${property}`);
}
if (property instanceof o.ReadPropExpr) {
let readPropExpr: o.ReadPropExpr = 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 (definedView.fields.some((field) => field.name == readPropExpr.name) ||
definedView.getters.some((field) => field.name == readPropExpr.name)) {
viewProp = viewProp.cast(definedView.classType);
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 o.replaceVarInExpression(o.THIS_EXPR.name, viewProp, property);
return super.visitReadPropExpr(ast, context);
}
}

View File

@ -11,7 +11,7 @@ import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventA
import {CompileElement} from './compile_element';
import {CompileView} from './compile_view';
import {CompileEventListener, bindDirectiveOutputs, bindRenderOutputs, collectEventListeners} from './event_binder';
import {bindDirectiveAfterContentLifecycleCallbacks, bindDirectiveAfterViewLifecycleCallbacks, bindDirectiveDetectChangesLifecycleCallbacks, bindInjectableDestroyLifecycleCallbacks, bindPipeDestroyLifecycleCallbacks} from './lifecycle_binder';
import {bindDirectiveAfterContentLifecycleCallbacks, bindDirectiveAfterViewLifecycleCallbacks, bindInjectableDestroyLifecycleCallbacks, bindPipeDestroyLifecycleCallbacks} from './lifecycle_binder';
import {bindDirectiveHostProps, bindDirectiveInputs, bindRenderInputs, bindRenderText} from './property_binder';
export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void {
@ -48,8 +48,9 @@ class ViewBinderVisitor implements TemplateAstVisitor {
bindRenderOutputs(eventListeners);
ast.directives.forEach((directiveAst) => {
var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
bindDirectiveInputs(directiveAst, directiveInstance, compileElement);
bindDirectiveDetectChangesLifecycleCallbacks(directiveAst, directiveInstance, compileElement);
var directiveWrapperInstance =
compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference);
bindDirectiveInputs(directiveAst, directiveWrapperInstance, compileElement);
bindDirectiveHostProps(directiveAst, directiveInstance, compileElement, eventListeners);
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
@ -76,8 +77,10 @@ class ViewBinderVisitor implements TemplateAstVisitor {
var eventListeners = collectEventListeners(ast.outputs, ast.directives, compileElement);
ast.directives.forEach((directiveAst) => {
var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
bindDirectiveInputs(directiveAst, directiveInstance, compileElement);
bindDirectiveDetectChangesLifecycleCallbacks(directiveAst, directiveInstance, compileElement);
var directiveWrapperInstance =
compileElement.directiveWrapperInstance.get(directiveAst.directive.type.reference);
bindDirectiveInputs(directiveAst, directiveWrapperInstance, compileElement);
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
bindDirectiveAfterContentLifecycleCallbacks(
directiveAst.directive, directiveInstance, compileElement);

View File

@ -20,6 +20,7 @@ import {createDiTokenExpression} from '../util';
import {CompileElement, CompileNode} from './compile_element';
import {CompileView} from './compile_view';
import {ChangeDetectorStatusEnum, DetectChangesVars, InjectMethodVars, ViewConstructorVars, ViewEncapsulationEnum, ViewProperties, ViewTypeEnum} from './constants';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewFactoryDependency} from './deps';
import {createFlatArray, getViewFactoryName} from './util';
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
@ -30,20 +31,11 @@ const NG_CONTAINER_TAG = 'ng-container';
var parentRenderNodeVar = o.variable('parentRenderNode');
var rootSelectorVar = o.variable('rootSelector');
export class ViewFactoryDependency {
constructor(
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
}
export class ComponentFactoryDependency {
constructor(
public comp: CompileIdentifierMetadata, public placeholder: CompileIdentifierMetadata) {}
}
export function buildView(
view: CompileView, template: TemplateAst[],
targetDependencies: Array<ViewFactoryDependency|ComponentFactoryDependency>): number {
targetDependencies:
Array<ViewFactoryDependency|ComponentFactoryDependency|DirectiveWrapperDependency>):
number {
var builderVisitor = new ViewBuilderVisitor(view, targetDependencies);
templateVisitAll(
builderVisitor, template,
@ -66,7 +58,8 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
constructor(
public view: CompileView,
public targetDependencies: Array<ViewFactoryDependency|ComponentFactoryDependency>) {}
public targetDependencies:
Array<ViewFactoryDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
@ -204,7 +197,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
}
var compileElement = new CompileElement(
parent, this.view, nodeIndex, renderNode, ast, component, directives, ast.providers,
ast.hasViewContainer, false, ast.references);
ast.hasViewContainer, false, ast.references, this.targetDependencies);
this.view.nodes.push(compileElement);
var compViewExpr: o.ReadVarExpr = null;
if (isPresent(component)) {
@ -212,13 +205,6 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
new CompileIdentifierMetadata({name: getViewFactoryName(component, 0)});
this.targetDependencies.push(
new ViewFactoryDependency(component.type, nestedComponentIdentifier));
let entryComponentIdentifiers =
component.entryComponents.map((entryComponent: CompileIdentifierMetadata) => {
var id = new CompileIdentifierMetadata({name: entryComponent.name});
this.targetDependencies.push(new ComponentFactoryDependency(entryComponent, id));
return id;
});
compileElement.createComponentFactoryResolver(entryComponentIdentifiers);
compViewExpr = o.variable(`compView_${nodeIndex}`); // fix highlighting: `
compileElement.setComponentView(compViewExpr);
@ -273,7 +259,7 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
var directives = ast.directives.map(directiveAst => directiveAst.directive);
var compileElement = new CompileElement(
parent, this.view, nodeIndex, renderNode, ast, null, directives, ast.providers,
ast.hasViewContainer, true, ast.references);
ast.hasViewContainer, true, ast.references, this.targetDependencies);
this.view.nodes.push(compileElement);
this.nestedViewCount++;

View File

@ -16,15 +16,17 @@ import {TemplateAst} from '../template_parser/template_ast';
import {CompileElement} from './compile_element';
import {CompileView} from './compile_view';
import {ComponentFactoryDependency, DirectiveWrapperDependency, ViewFactoryDependency} from './deps';
import {bindView} from './view_binder';
import {ComponentFactoryDependency, ViewFactoryDependency, buildView, finishView} from './view_builder';
import {buildView, finishView} from './view_builder';
export {ComponentFactoryDependency, ViewFactoryDependency} from './view_builder';
export {ComponentFactoryDependency, DirectiveWrapperDependency, ViewFactoryDependency} from './deps';
export class ViewCompileResult {
constructor(
public statements: o.Statement[], public viewFactoryVar: string,
public dependencies: Array<ViewFactoryDependency|ComponentFactoryDependency>) {}
public dependencies:
Array<ViewFactoryDependency|ComponentFactoryDependency|DirectiveWrapperDependency>) {}
}
@Injectable()
@ -35,7 +37,8 @@ export class ViewCompiler {
component: CompileDirectiveMetadata, template: TemplateAst[], styles: o.Expression,
pipes: CompilePipeMetadata[],
compiledAnimations: AnimationEntryCompileResult[]): ViewCompileResult {
const dependencies: Array<ViewFactoryDependency|ComponentFactoryDependency> = [];
const dependencies:
Array<ViewFactoryDependency|ComponentFactoryDependency|DirectiveWrapperDependency> = [];
const view = new CompileView(
component, this._genConfig, pipes, styles, compiledAnimations, 0,
CompileElement.createNull(), []);

View File

@ -0,0 +1,65 @@
/**
* @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 {CompileIdentifierMetadata} from '../../src/compile_metadata';
import * as o from '../../src/output/output_ast';
import {CompileView} from '../../src/view_compiler/compile_view';
import {getPropertyInView} from '../../src/view_compiler/util';
export function main() {
describe('getPropertyInView', () => {
it('should return the expression if it is the same view', () => {
const expr = o.THIS_EXPR.prop('someProp');
const callingView = createCompileView({className: 'view'});
expect(getPropertyInView(expr, callingView, callingView)).toBe(expr);
});
it('should access an unknown property in a parent view', () => {
const expr = o.THIS_EXPR.prop('someProp');
const definedView = createCompileView({className: 'parentView'});
const callingView = createCompileView({className: 'childView', parent: definedView});
expect(getPropertyInView(expr, callingView, definedView))
.toEqual(o.THIS_EXPR.prop('parent').prop('someProp'));
});
it('should access a known property in a parent view with cast', () => {
const expr = o.THIS_EXPR.prop('someProp');
const definedView = createCompileView({className: 'parentView', fields: ['someProp']});
const callingView = createCompileView({className: 'childView', parent: definedView});
expect(getPropertyInView(expr, callingView, definedView))
.toEqual(o.THIS_EXPR.prop('parent').cast(definedView.classType).prop('someProp'));
});
it('should access a known property in a parent view with cast also for property chain expressions',
() => {
const expr = o.THIS_EXPR.prop('someProp').prop('context');
const definedView = createCompileView({className: 'parentView', fields: ['someProp']});
const callingView = createCompileView({className: 'childView', parent: definedView});
expect(getPropertyInView(expr, callingView, definedView))
.toEqual(o.THIS_EXPR.prop('parent')
.cast(definedView.classType)
.prop('someProp')
.prop('context'));
});
});
}
function createCompileView(config: {className: string, parent?: CompileView, fields?: string[]}):
CompileView {
const declarationElement: any = config.parent ? {view: config.parent} : {view: null};
const fields: o.ClassField[] = [];
if (config.fields) {
config.fields.forEach((fieldName) => { fields.push(new o.ClassField(fieldName)); });
}
return <any>{
classType: o.importType(new CompileIdentifierMetadata({name: config.className})),
fields: fields,
getters: [],
declarationElement: declarationElement
};
}

View File

@ -64,11 +64,6 @@ export var __core_private__: {
_NgModuleInjector?: ng_module_factory.NgModuleInjector<any>,
registerModuleFactory: typeof ng_module_factory_loader.registerModuleFactory,
ViewType: typeof view_type.ViewType, _ViewType?: view_type.ViewType,
MAX_INTERPOLATION_VALUES: typeof view_utils.MAX_INTERPOLATION_VALUES,
checkBinding: typeof view_utils.checkBinding,
flattenNestedViewRenderNodes: typeof view_utils.flattenNestedViewRenderNodes,
interpolate: typeof view_utils.interpolate,
ViewUtils: typeof view_utils.ViewUtils, _ViewUtils?: view_utils.ViewUtils,
ViewMetadata: typeof metadata_view.ViewMetadata, _ViewMetadata?: metadata_view.ViewMetadata,
DebugContext: typeof debug_context.DebugContext, _DebugContext?: debug_context.DebugContext,
StaticNodeDebugInfo: typeof debug_context.StaticNodeDebugInfo,
@ -84,19 +79,6 @@ export var __core_private__: {
makeDecorator: typeof decorators.makeDecorator,
DebugDomRootRenderer: typeof debug.DebugDomRootRenderer,
_DebugDomRootRenderer?: debug.DebugDomRootRenderer,
EMPTY_ARRAY: typeof view_utils.EMPTY_ARRAY,
EMPTY_MAP: typeof view_utils.EMPTY_MAP,
pureProxy1: typeof view_utils.pureProxy1,
pureProxy2: typeof view_utils.pureProxy2,
pureProxy3: typeof view_utils.pureProxy3,
pureProxy4: typeof view_utils.pureProxy4,
pureProxy5: typeof view_utils.pureProxy5,
pureProxy6: typeof view_utils.pureProxy6,
pureProxy7: typeof view_utils.pureProxy7,
pureProxy8: typeof view_utils.pureProxy8,
pureProxy9: typeof view_utils.pureProxy9,
pureProxy10: typeof view_utils.pureProxy10,
castByValue: typeof view_utils.castByValue,
Console: typeof console.Console, _Console?: console.Console,
reflector: typeof reflection.reflector,
Reflector: typeof reflection.Reflector, _Reflector?: reflection.Reflector,
@ -121,6 +103,7 @@ export var __core_private__: {
ComponentStillLoadingError: typeof ComponentStillLoadingError,
isPromise: typeof isPromise,
AnimationTransition: typeof AnimationTransition
view_utils: typeof view_utils,
} = {
isDefaultChangeDetectionStrategy: constants.isDefaultChangeDetectionStrategy,
ChangeDetectorStatus: constants.ChangeDetectorStatus,
@ -135,11 +118,7 @@ export var __core_private__: {
NgModuleInjector: ng_module_factory.NgModuleInjector,
registerModuleFactory: ng_module_factory_loader.registerModuleFactory,
ViewType: view_type.ViewType,
MAX_INTERPOLATION_VALUES: view_utils.MAX_INTERPOLATION_VALUES,
checkBinding: view_utils.checkBinding,
flattenNestedViewRenderNodes: view_utils.flattenNestedViewRenderNodes,
interpolate: view_utils.interpolate,
ViewUtils: view_utils.ViewUtils,
view_utils: view_utils,
ViewMetadata: metadata_view.ViewMetadata,
DebugContext: debug_context.DebugContext,
StaticNodeDebugInfo: debug_context.StaticNodeDebugInfo,
@ -151,19 +130,6 @@ export var __core_private__: {
ReflectionCapabilities: reflection_capabilities.ReflectionCapabilities,
makeDecorator: decorators.makeDecorator,
DebugDomRootRenderer: debug.DebugDomRootRenderer,
EMPTY_ARRAY: view_utils.EMPTY_ARRAY,
EMPTY_MAP: view_utils.EMPTY_MAP,
pureProxy1: view_utils.pureProxy1,
pureProxy2: view_utils.pureProxy2,
pureProxy3: view_utils.pureProxy3,
pureProxy4: view_utils.pureProxy4,
pureProxy5: view_utils.pureProxy5,
pureProxy6: view_utils.pureProxy6,
pureProxy7: view_utils.pureProxy7,
pureProxy8: view_utils.pureProxy8,
pureProxy9: view_utils.pureProxy9,
pureProxy10: view_utils.pureProxy10,
castByValue: view_utils.castByValue,
Console: console.Console,
reflector: reflection.reflector,
Reflector: reflection.Reflector,

View File

@ -7,13 +7,14 @@
*/
import {APP_ID} from '../application_tokens';
import {devModeEqual} from '../change_detection/change_detection';
import {SimpleChange, devModeEqual} from '../change_detection/change_detection';
import {UNINITIALIZED} from '../change_detection/change_detection_util';
import {Inject, Injectable} from '../di';
import {isPresent, looseIdentical} from '../facade/lang';
import {ViewEncapsulation} from '../metadata/view';
import {RenderComponentType, Renderer, RootRenderer} from '../render/api';
import {Sanitizer} from '../security';
import {AppElement} from './element';
import {ExpressionChangedAfterItHasBeenCheckedError} from './errors';
@ -352,3 +353,27 @@ export function pureProxy10<P0, P1, P2, P3, P4, P5, P6, P7, P8, P9, R>(
return result;
};
}
export function setBindingDebugInfoForChanges(
renderer: Renderer, el: any, changes: {[key: string]: SimpleChange}) {
Object.keys(changes).forEach((propName) => {
setBindingDebugInfo(renderer, el, propName, changes[propName].currentValue);
});
}
export function setBindingDebugInfo(renderer: Renderer, el: any, propName: string, value: any) {
try {
renderer.setBindingDebugInfo(
el, `ng-reflect-${camelCaseToDashCase(propName)}`, value ? value.toString() : null);
} catch (e) {
renderer.setBindingDebugInfo(
el, `ng-reflect-${camelCaseToDashCase(propName)}`,
'[ERROR] Exception while trying to serialize the value');
}
}
const CAMEL_CASE_REGEXP = /([A-Z])/g;
function camelCaseToDashCase(input: string): string {
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
}