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:
parent
c951822c35
commit
b0a03fcab3
|
@ -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[];
|
||||
}
|
||||
|
|
|
@ -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}!`);
|
||||
|
|
|
@ -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';
|
||||
|
|
|
@ -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},
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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',
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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]}`;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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++) {
|
||||
|
|
|
@ -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) {}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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++;
|
||||
|
|
|
@ -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(), []);
|
||||
|
|
|
@ -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
|
||||
};
|
||||
}
|
|
@ -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,
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue