feat(compiler): integrate compiler with view engine - main integration tests work (#14284)

Part of #14013

PR Close #14284
This commit is contained in:
Tobias Bosch 2017-02-02 15:01:35 -08:00 committed by Miško Hevery
parent dfe29934b6
commit baa654a234
35 changed files with 1232 additions and 277 deletions

View File

@ -71,7 +71,7 @@ export function convertPropertyBinding(
} }
export class ConvertActionBindingResult { export class ConvertActionBindingResult {
constructor(public stmts: o.Statement[], public preventDefault: o.ReadVarExpr) {} constructor(public stmts: o.Statement[], public allowDefault: o.ReadVarExpr) {}
} }
/** /**

View File

@ -6,11 +6,20 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {MissingTranslationStrategy, ViewEncapsulation, isDevMode} from '@angular/core'; import {InjectionToken, MissingTranslationStrategy, ViewEncapsulation, isDevMode} from '@angular/core';
import {CompileIdentifierMetadata} from './compile_metadata'; import {CompileIdentifierMetadata} from './compile_metadata';
import {Identifiers, createIdentifier} from './identifiers'; import {Identifiers, createIdentifier} from './identifiers';
/**
* Temporal switch for the compiler to use the new view engine,
* until it is fully integrated.
*
* Only works in Jit for now.
*/
export const USE_VIEW_ENGINE = new InjectionToken<boolean>('UseViewEngine');
export class CompilerConfig { export class CompilerConfig {
public renderTypes: RenderTypes; public renderTypes: RenderTypes;
public defaultEncapsulation: ViewEncapsulation; public defaultEncapsulation: ViewEncapsulation;

View File

@ -288,8 +288,8 @@ function addHandleEventMethod(hostListeners: BoundEventAst[], builder: Directive
builder, null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostListener.handler, builder, null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostListener.handler,
`sub_${eventIdx}`); `sub_${eventIdx}`);
const trueStmts = evalResult.stmts; const trueStmts = evalResult.stmts;
if (evalResult.preventDefault) { if (evalResult.allowDefault) {
trueStmts.push(resultVar.set(evalResult.preventDefault.and(resultVar)).toStmt()); trueStmts.push(resultVar.set(evalResult.allowDefault.and(resultVar)).toStmt());
} }
// TODO(tbosch): convert this into a `switch` once our OutputAst supports it. // TODO(tbosch): convert this into a `switch` once our OutputAst supports it.
actionStmts.push( actionStmts.push(

View File

@ -9,10 +9,11 @@
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, ElementRef, Injector, LOCALE_ID, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core'; import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ComponentRef, 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 {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, ComponentRef_, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, ValueUnwrapper, ViewContainer, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, view_utils} from './private_import_core'; import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, ComponentRef_, DebugAppView, DebugContext, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, ValueUnwrapper, ViewContainer, ViewType, balanceAnimationKeyframes, clearStyles, collectAndResolveStyles, devModeEqual, prepareFinalAnimationStyles, reflector, registerModuleFactory, renderStyles, viewEngine, view_utils} from './private_import_core';
const APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view'); const APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
const VIEW_UTILS_MODULE_URL = assetUrl('core', 'linker/view_utils'); const VIEW_UTILS_MODULE_URL = assetUrl('core', 'linker/view_utils');
const VIEW_ENGINE_MODULE_URL = assetUrl('core', 'view/index');
const CD_MODULE_URL = assetUrl('core', 'change_detection/change_detection'); const CD_MODULE_URL = assetUrl('core', 'change_detection/change_detection');
const ANIMATION_STYLE_UTIL_ASSET_URL = assetUrl('core', 'animation/animation_style_util'); const ANIMATION_STYLE_UTIL_ASSET_URL = assetUrl('core', 'animation/animation_style_util');
@ -363,6 +364,47 @@ export class Identifiers {
}; };
static noop: static noop:
IdentifierSpec = {name: 'noop', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.noop}; IdentifierSpec = {name: 'noop', moduleUrl: VIEW_UTILS_MODULE_URL, runtime: view_utils.noop};
static viewDef: IdentifierSpec = {
name: 'viewDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.viewDef
};
static elementDef: IdentifierSpec = {
name: 'elementDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.elementDef
};
static anchorDef: IdentifierSpec = {
name: 'anchorDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.anchorDef
};
static textDef: IdentifierSpec = {
name: 'textDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.textDef
};
static directiveDef: IdentifierSpec = {
name: 'directiveDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.directiveDef
};
static providerDef: IdentifierSpec = {
name: 'providerDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.providerDef
};
static queryDef: IdentifierSpec = {
name: 'queryDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.queryDef
};
static nodeValue: IdentifierSpec = {
name: 'nodeValue',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.nodeValue
};
} }
export function assetUrl(pkg: string, path: string = null, type: string = 'src'): string { export function assetUrl(pkg: string, path: string = null, type: string = 'src'): string {

View File

@ -6,12 +6,12 @@
* found in the LICENSE file at https://angular.io/license * found in the LICENSE file at https://angular.io/license
*/ */
import {Compiler, ComponentFactory, Injector, ModuleWithComponentFactories, NgModuleFactory, Type} from '@angular/core'; import {Compiler, ComponentFactory, Inject, Injector, ModuleWithComponentFactories, NgModuleFactory, Type} from '@angular/core';
import {AnimationCompiler} from '../animation/animation_compiler'; import {AnimationCompiler} from '../animation/animation_compiler';
import {AnimationParser} from '../animation/animation_parser'; import {AnimationParser} from '../animation/animation_parser';
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, ProviderMeta, ProxyClass, createHostComponentMeta, identifierName} from '../compile_metadata'; import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileNgModuleMetadata, ProviderMeta, ProxyClass, createHostComponentMeta, identifierName} from '../compile_metadata';
import {CompilerConfig} from '../config'; import {CompilerConfig, USE_VIEW_ENGINE} from '../config';
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler'; import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
import {stringify} from '../facade/lang'; import {stringify} from '../facade/lang';
import {CompilerInjectable} from '../injectable'; import {CompilerInjectable} from '../injectable';
@ -50,7 +50,8 @@ export class JitCompiler implements Compiler {
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler, private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _viewCompiler: ViewCompiler, private _ngModuleCompiler: NgModuleCompiler, private _viewCompiler: ViewCompiler, private _ngModuleCompiler: NgModuleCompiler,
private _directiveWrapperCompiler: DirectiveWrapperCompiler, private _directiveWrapperCompiler: DirectiveWrapperCompiler,
private _compilerConfig: CompilerConfig, private _animationParser: AnimationParser) {} private _compilerConfig: CompilerConfig, private _animationParser: AnimationParser,
@Inject(USE_VIEW_ENGINE) private _useViewEngine: boolean) {}
get injector(): Injector { return this._injector; } get injector(): Injector { return this._injector; }
@ -244,6 +245,9 @@ export class JitCompiler implements Compiler {
private _compileDirectiveWrapper( private _compileDirectiveWrapper(
dirMeta: CompileDirectiveMetadata, moduleMeta: CompileNgModuleMetadata): void { dirMeta: CompileDirectiveMetadata, moduleMeta: CompileNgModuleMetadata): void {
if (this._useViewEngine) {
return;
}
const compileResult = this._directiveWrapperCompiler.compile(dirMeta); const compileResult = this._directiveWrapperCompiler.compile(dirMeta);
const statements = compileResult.statements; const statements = compileResult.statements;
let directiveWrapperClass: any; let directiveWrapperClass: any;

View File

@ -9,7 +9,7 @@
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, InjectionToken, MissingTranslationStrategy, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core'; import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, InjectionToken, MissingTranslationStrategy, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
import {AnimationParser} from '../animation/animation_parser'; import {AnimationParser} from '../animation/animation_parser';
import {CompilerConfig} from '../config'; import {CompilerConfig, USE_VIEW_ENGINE} from '../config';
import {DirectiveNormalizer} from '../directive_normalizer'; import {DirectiveNormalizer} from '../directive_normalizer';
import {DirectiveResolver} from '../directive_resolver'; import {DirectiveResolver} from '../directive_resolver';
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler'; import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
@ -31,6 +31,7 @@ import {SummaryResolver} from '../summary_resolver';
import {TemplateParser} from '../template_parser/template_parser'; import {TemplateParser} from '../template_parser/template_parser';
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from '../url_resolver'; import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from '../url_resolver';
import {ViewCompiler} from '../view_compiler/view_compiler'; import {ViewCompiler} from '../view_compiler/view_compiler';
import {ViewCompilerNext} from '../view_compiler_next/view_compiler';
import {JitCompiler} from './compiler'; import {JitCompiler} from './compiler';
@ -42,6 +43,11 @@ const _NO_RESOURCE_LOADER: ResourceLoader = {
const baseHtmlParser = new InjectionToken('HtmlParser'); const baseHtmlParser = new InjectionToken('HtmlParser');
function viewCompilerFactory(
useViewEngine: boolean, cc: CompilerConfig, sr: ElementSchemaRegistry) {
return useViewEngine ? new ViewCompilerNext(cc, sr) : new ViewCompiler(cc, sr);
}
/** /**
* A set of providers that provide `JitCompiler` and its dependencies to use for * A set of providers that provide `JitCompiler` and its dependencies to use for
* template compilation. * template compilation.
@ -81,7 +87,12 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
CompileMetadataResolver, CompileMetadataResolver,
DEFAULT_PACKAGE_URL_PROVIDER, DEFAULT_PACKAGE_URL_PROVIDER,
StyleCompiler, StyleCompiler,
ViewCompiler, {provide: USE_VIEW_ENGINE, useValue: false},
{
provide: ViewCompiler,
useFactory: viewCompilerFactory,
deps: [USE_VIEW_ENGINE, CompilerConfig, ElementSchemaRegistry]
},
NgModuleCompiler, NgModuleCompiler,
DirectiveWrapperCompiler, DirectiveWrapperCompiler,
{provide: CompilerConfig, useValue: new CompilerConfig()}, {provide: CompilerConfig, useValue: new CompilerConfig()},

View File

@ -12,6 +12,7 @@ import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol';
import {ngfactoryFilePath} from './aot/util'; import {ngfactoryFilePath} from './aot/util';
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions'; import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
import * as cpl from './compile_metadata'; import * as cpl from './compile_metadata';
import {USE_VIEW_ENGINE} from './config';
import {DirectiveNormalizer} from './directive_normalizer'; import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveResolver} from './directive_resolver'; import {DirectiveResolver} from './directive_resolver';
import {stringify} from './facade/lang'; import {stringify} from './facade/lang';
@ -20,7 +21,7 @@ import {CompilerInjectable} from './injectable';
import {hasLifecycleHook} from './lifecycle_reflector'; import {hasLifecycleHook} from './lifecycle_reflector';
import {NgModuleResolver} from './ng_module_resolver'; import {NgModuleResolver} from './ng_module_resolver';
import {PipeResolver} from './pipe_resolver'; import {PipeResolver} from './pipe_resolver';
import {ERROR_COMPONENT_TYPE, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector} from './private_import_core'; import {ERROR_COMPONENT_TYPE, LIFECYCLE_HOOKS_VALUES, ReflectorReader, reflector, viewEngine} from './private_import_core';
import {ElementSchemaRegistry} from './schema/element_schema_registry'; import {ElementSchemaRegistry} from './schema/element_schema_registry';
import {SummaryResolver} from './summary_resolver'; import {SummaryResolver} from './summary_resolver';
import {getUrlScheme} from './url_resolver'; import {getUrlScheme} from './url_resolver';
@ -53,7 +54,8 @@ export class CompileMetadataResolver {
private _directiveNormalizer: DirectiveNormalizer, private _directiveNormalizer: DirectiveNormalizer,
@Optional() private _staticSymbolCache: StaticSymbolCache, @Optional() private _staticSymbolCache: StaticSymbolCache,
private _reflector: ReflectorReader = reflector, private _reflector: ReflectorReader = reflector,
@Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector) {} @Optional() @Inject(ERROR_COLLECTOR_TOKEN) private _errorCollector?: ErrorCollector,
@Optional() @Inject(USE_VIEW_ENGINE) private _useViewEngine?: boolean) {}
clearCacheFor(type: Type<any>) { clearCacheFor(type: Type<any>) {
const dirMeta = this._directiveCache.get(type); const dirMeta = this._directiveCache.get(type);
@ -135,9 +137,13 @@ export class CompileMetadataResolver {
ngfactoryFilePath(dirType.filePath), cpl.componentFactoryName(dirType)); ngfactoryFilePath(dirType.filePath), cpl.componentFactoryName(dirType));
} else { } else {
const hostView = this.getHostComponentViewClass(dirType); const hostView = this.getHostComponentViewClass(dirType);
if (this._useViewEngine) {
return viewEngine.createComponentFactory(selector, dirType, <any>hostView);
} else {
return new ComponentFactory(selector, <any>hostView, dirType); return new ComponentFactory(selector, <any>hostView, dirType);
} }
} }
}
getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata { getAnimationEntryMetadata(entry: AnimationEntryMetadata): cpl.CompileAnimationEntryMetadata {
const defs = entry.definitions.map(def => this._getAnimationStateMetadata(def)); const defs = entry.definitions.map(def => this._getAnimationStateMetadata(def));

View File

@ -29,6 +29,7 @@ export const registerModuleFactory: typeof r.registerModuleFactory = r.registerM
export type ViewType = typeof r._ViewType; export type ViewType = typeof r._ViewType;
export const ViewType: typeof r.ViewType = r.ViewType; export const ViewType: typeof r.ViewType = r.ViewType;
export const view_utils: typeof r.view_utils = r.view_utils; export const view_utils: typeof r.view_utils = r.view_utils;
export const viewEngine: typeof r.viewEngine = r.viewEngine;
export const DebugContext: typeof r.DebugContext = r.DebugContext; export const DebugContext: typeof r.DebugContext = r.DebugContext;
export const StaticNodeDebugInfo: typeof r.StaticNodeDebugInfo = r.StaticNodeDebugInfo; export const StaticNodeDebugInfo: typeof r.StaticNodeDebugInfo = r.StaticNodeDebugInfo;
export const devModeEqual: typeof r.devModeEqual = r.devModeEqual; export const devModeEqual: typeof r.devModeEqual = r.devModeEqual;

View File

@ -9,19 +9,24 @@
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenName, tokenReference} from './compile_metadata'; import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompileNgModuleMetadata, CompileProviderMetadata, CompileQueryMetadata, CompileTokenMetadata, CompileTypeMetadata, tokenName, tokenReference} from './compile_metadata';
import {isBlank, isPresent} from './facade/lang'; import {isBlank, isPresent} from './facade/lang';
import {Identifiers, resolveIdentifier} from './identifiers'; import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
import {ParseError, ParseSourceSpan} from './parse_util'; import {ParseError, ParseSourceSpan} from './parse_util';
import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, ReferenceAst} from './template_parser/template_ast'; import {AttrAst, DirectiveAst, ProviderAst, ProviderAstType, QueryId, QueryMatch, ReferenceAst} from './template_parser/template_ast';
export class ProviderError extends ParseError { export class ProviderError extends ParseError {
constructor(message: string, span: ParseSourceSpan) { super(span, message); } constructor(message: string, span: ParseSourceSpan) { super(span, message); }
} }
export interface QueryWithId {
meta: CompileQueryMetadata;
id: QueryId;
}
export class ProviderViewContext { export class ProviderViewContext {
/** /**
* @internal * @internal
*/ */
viewQueries: Map<any, CompileQueryMetadata[]>; viewQueries: Map<any, QueryWithId[]>;
/** /**
* @internal * @internal
*/ */
@ -40,13 +45,14 @@ export class ProviderViewContext {
} }
export class ProviderElementContext { export class ProviderElementContext {
private _contentQueries: Map<any, CompileQueryMetadata[]>; private _contentQueries: Map<any, QueryWithId[]>;
private _transformedProviders = new Map<any, ProviderAst>(); private _transformedProviders = new Map<any, ProviderAst>();
private _seenProviders = new Map<any, boolean>(); private _seenProviders = new Map<any, boolean>();
private _allProviders: Map<any, ProviderAst>; private _allProviders: Map<any, ProviderAst>;
private _attrs: {[key: string]: string}; private _attrs: {[key: string]: string};
private _hasViewContainer: boolean = false; private _hasViewContainer: boolean = false;
private _queriedTokens = new Map<any, QueryMatch[]>();
constructor( constructor(
public viewContext: ProviderViewContext, private _parent: ProviderElementContext, public viewContext: ProviderViewContext, private _parent: ProviderElementContext,
@ -57,19 +63,21 @@ export class ProviderElementContext {
const directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive); const directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive);
this._allProviders = this._allProviders =
_resolveProvidersFromDirectives(directivesMeta, _sourceSpan, viewContext.errors); _resolveProvidersFromDirectives(directivesMeta, _sourceSpan, viewContext.errors);
this._contentQueries = _getContentQueries(directivesMeta); this._contentQueries = _getContentQueries(this.depth, directivesMeta);
const queriedTokens = new Map<any, boolean>();
Array.from(this._allProviders.values()).forEach((provider) => { Array.from(this._allProviders.values()).forEach((provider) => {
this._addQueryReadsTo(provider.token, queriedTokens); this._addQueryReadsTo(provider.token, provider.token, this._queriedTokens);
}); });
refs.forEach((refAst) => { this._addQueryReadsTo({value: refAst.name}, queriedTokens); }); refs.forEach((refAst) => {
if (isPresent(queriedTokens.get(resolveIdentifier(Identifiers.ViewContainerRef)))) { let defaultQueryValue = refAst.value || createIdentifierToken(Identifiers.ElementRef);
this._addQueryReadsTo({value: refAst.name}, defaultQueryValue, this._queriedTokens);
});
if (this._queriedTokens.get(resolveIdentifier(Identifiers.ViewContainerRef))) {
this._hasViewContainer = true; this._hasViewContainer = true;
} }
// create the providers that we know are eager first // create the providers that we know are eager first
Array.from(this._allProviders.values()).forEach((provider) => { Array.from(this._allProviders.values()).forEach((provider) => {
const eager = provider.eager || isPresent(queriedTokens.get(tokenReference(provider.token))); const eager = provider.eager || this._queriedTokens.get(tokenReference(provider.token));
if (eager) { if (eager) {
this._getOrCreateLocalProvider(provider.providerType, provider.token, true); this._getOrCreateLocalProvider(provider.providerType, provider.token, true);
} }
@ -83,6 +91,16 @@ export class ProviderElementContext {
}); });
} }
get depth(): number {
let d = 0;
let current: ProviderElementContext = this;
while (current._parent) {
d++;
current = current._parent;
}
return d;
}
get transformProviders(): ProviderAst[] { get transformProviders(): ProviderAst[] {
return Array.from(this._transformedProviders.values()); return Array.from(this._transformedProviders.values());
} }
@ -98,24 +116,36 @@ export class ProviderElementContext {
get transformedHasViewContainer(): boolean { return this._hasViewContainer; } get transformedHasViewContainer(): boolean { return this._hasViewContainer; }
private _addQueryReadsTo(token: CompileTokenMetadata, queryReadTokens: Map<any, boolean>) { get queryMatches(): QueryMatch[] {
this._getQueriesFor(token).forEach((query) => { const allMatches: QueryMatch[] = [];
const queryReadToken = query.read || token; this._queriedTokens.forEach((matches: QueryMatch[]) => { allMatches.push(...matches); });
if (isBlank(queryReadTokens.get(tokenReference(queryReadToken)))) { return allMatches;
queryReadTokens.set(tokenReference(queryReadToken), true);
} }
private _addQueryReadsTo(
token: CompileTokenMetadata, defaultValue: CompileTokenMetadata,
queryReadTokens: Map<any, QueryMatch[]>) {
this._getQueriesFor(token).forEach((query) => {
const queryValue = query.meta.read || defaultValue;
const tokenRef = tokenReference(queryValue);
let queryMatches = queryReadTokens.get(tokenRef);
if (!queryMatches) {
queryMatches = [];
queryReadTokens.set(tokenRef, queryMatches);
}
queryMatches.push({query: query.id, value: queryValue});
}); });
} }
private _getQueriesFor(token: CompileTokenMetadata): CompileQueryMetadata[] { private _getQueriesFor(token: CompileTokenMetadata): QueryWithId[] {
const result: CompileQueryMetadata[] = []; const result: QueryWithId[] = [];
let currentEl: ProviderElementContext = this; let currentEl: ProviderElementContext = this;
let distance = 0; let distance = 0;
let queries: CompileQueryMetadata[]; let queries: QueryWithId[];
while (currentEl !== null) { while (currentEl !== null) {
queries = currentEl._contentQueries.get(tokenReference(token)); queries = currentEl._contentQueries.get(tokenReference(token));
if (queries) { if (queries) {
result.push(...queries.filter((query) => query.descendants || distance <= 1)); result.push(...queries.filter((query) => query.meta.descendants || distance <= 1));
} }
if (currentEl._directiveAsts.length > 0) { if (currentEl._directiveAsts.length > 0) {
distance++; distance++;
@ -452,27 +482,32 @@ function _resolveProviders(
} }
function _getViewQueries(component: CompileDirectiveMetadata): Map<any, CompileQueryMetadata[]> { function _getViewQueries(component: CompileDirectiveMetadata): Map<any, QueryWithId[]> {
const viewQueries = new Map<any, CompileQueryMetadata[]>(); const viewQueries = new Map<any, QueryWithId[]>();
if (component.viewQueries) { if (component.viewQueries) {
component.viewQueries.forEach((query) => _addQueryToTokenMap(viewQueries, query)); component.viewQueries.forEach(
(query, queryIndex) => _addQueryToTokenMap(
viewQueries,
{meta: query, id: {elementDepth: null, directiveIndex: null, queryIndex: queryIndex}}));
} }
return viewQueries; return viewQueries;
} }
function _getContentQueries(directives: CompileDirectiveSummary[]): function _getContentQueries(
Map<any, CompileQueryMetadata[]> { elementDepth: number, directives: CompileDirectiveSummary[]): Map<any, QueryWithId[]> {
const contentQueries = new Map<any, CompileQueryMetadata[]>(); const contentQueries = new Map<any, QueryWithId[]>();
directives.forEach(directive => { directives.forEach((directive, directiveIndex) => {
if (directive.queries) { if (directive.queries) {
directive.queries.forEach((query) => _addQueryToTokenMap(contentQueries, query)); directive.queries.forEach(
(query, queryIndex) => _addQueryToTokenMap(
contentQueries, {meta: query, id: {elementDepth, directiveIndex, queryIndex}}));
} }
}); });
return contentQueries; return contentQueries;
} }
function _addQueryToTokenMap(map: Map<any, CompileQueryMetadata[]>, query: CompileQueryMetadata) { function _addQueryToTokenMap(map: Map<any, QueryWithId[]>, query: QueryWithId) {
query.selectors.forEach((token: CompileTokenMetadata) => { query.meta.selectors.forEach((token: CompileTokenMetadata) => {
let entry = map.get(tokenReference(token)); let entry = map.get(tokenReference(token));
if (!entry) { if (!entry) {
entry = []; entry = [];

View File

@ -126,9 +126,9 @@ export class ElementAst implements TemplateAst {
public name: string, public attrs: AttrAst[], public inputs: BoundElementPropertyAst[], public name: string, public attrs: AttrAst[], public inputs: BoundElementPropertyAst[],
public outputs: BoundEventAst[], public references: ReferenceAst[], public outputs: BoundEventAst[], public references: ReferenceAst[],
public directives: DirectiveAst[], public providers: ProviderAst[], public directives: DirectiveAst[], public providers: ProviderAst[],
public hasViewContainer: boolean, public children: TemplateAst[], public hasViewContainer: boolean, public queryMatches: QueryMatch[],
public ngContentIndex: number, public sourceSpan: ParseSourceSpan, public children: TemplateAst[], public ngContentIndex: number,
public endSourceSpan: ParseSourceSpan) {} public sourceSpan: ParseSourceSpan, public endSourceSpan: ParseSourceSpan) {}
visit(visitor: TemplateAstVisitor, context: any): any { visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitElement(this, context); return visitor.visitElement(this, context);
@ -143,8 +143,8 @@ export class EmbeddedTemplateAst implements TemplateAst {
public attrs: AttrAst[], public outputs: BoundEventAst[], public references: ReferenceAst[], public attrs: AttrAst[], public outputs: BoundEventAst[], public references: ReferenceAst[],
public variables: VariableAst[], public directives: DirectiveAst[], public variables: VariableAst[], public directives: DirectiveAst[],
public providers: ProviderAst[], public hasViewContainer: boolean, public providers: ProviderAst[], public hasViewContainer: boolean,
public children: TemplateAst[], public ngContentIndex: number, public queryMatches: QueryMatch[], public children: TemplateAst[],
public sourceSpan: ParseSourceSpan) {} public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {}
visit(visitor: TemplateAstVisitor, context: any): any { visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitEmbeddedTemplate(this, context); return visitor.visitEmbeddedTemplate(this, context);
@ -241,6 +241,20 @@ export enum PropertyBindingType {
Animation Animation
} }
/**
* This id differentiates a query on an element from any query on any child.
*/
export interface QueryId {
elementDepth: number;
directiveIndex: number;
queryIndex: number;
}
export interface QueryMatch {
query: QueryId;
value: CompileTokenMetadata;
}
/** /**
* A visitor for {@link TemplateAst} trees that will process each node. * A visitor for {@link TemplateAst} trees that will process each node.
*/ */

View File

@ -341,8 +341,9 @@ class TemplateParseVisitor implements html.Visitor {
parsedElement = new EmbeddedTemplateAst( parsedElement = new EmbeddedTemplateAst(
attrs, events, references, elementVars, providerContext.transformedDirectiveAsts, attrs, events, references, elementVars, providerContext.transformedDirectiveAsts,
providerContext.transformProviders, providerContext.transformedHasViewContainer, children, providerContext.transformProviders, providerContext.transformedHasViewContainer,
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan); providerContext.queryMatches, children, hasInlineTemplates ? null : ngContentIndex,
element.sourceSpan);
} else { } else {
this._assertElementExists(matchElement, element); this._assertElementExists(matchElement, element);
this._assertOnlyOneComponent(directiveAsts, element.sourceSpan); this._assertOnlyOneComponent(directiveAsts, element.sourceSpan);
@ -352,7 +353,7 @@ class TemplateParseVisitor implements html.Visitor {
parsedElement = new ElementAst( parsedElement = new ElementAst(
nodeName, attrs, elementProps, events, references, nodeName, attrs, elementProps, events, references,
providerContext.transformedDirectiveAsts, providerContext.transformProviders, providerContext.transformedDirectiveAsts, providerContext.transformProviders,
providerContext.transformedHasViewContainer, children, providerContext.transformedHasViewContainer, providerContext.queryMatches, children,
hasInlineTemplates ? null : ngContentIndex, element.sourceSpan, element.endSourceSpan); hasInlineTemplates ? null : ngContentIndex, element.sourceSpan, element.endSourceSpan);
this._findComponentDirectives(directiveAsts) this._findComponentDirectives(directiveAsts)
@ -386,8 +387,8 @@ class TemplateParseVisitor implements html.Visitor {
parsedElement = new EmbeddedTemplateAst( parsedElement = new EmbeddedTemplateAst(
[], [], [], templateElementVars, templateProviderContext.transformedDirectiveAsts, [], [], [], templateElementVars, templateProviderContext.transformedDirectiveAsts,
templateProviderContext.transformProviders, templateProviderContext.transformProviders,
templateProviderContext.transformedHasViewContainer, [parsedElement], ngContentIndex, templateProviderContext.transformedHasViewContainer, templateProviderContext.queryMatches,
element.sourceSpan); [parsedElement], ngContentIndex, element.sourceSpan);
} }
return parsedElement; return parsedElement;
@ -755,7 +756,7 @@ class NonBindableVisitor implements html.Visitor {
const ngContentIndex = parent.findNgContentIndex(selector); const ngContentIndex = parent.findNgContentIndex(selector);
const children = html.visitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT); const children = html.visitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT);
return new ElementAst( return new ElementAst(
ast.name, html.visitAll(this, ast.attrs), [], [], [], [], [], false, children, ast.name, html.visitAll(this, ast.attrs), [], [], [], [], [], false, [], children,
ngContentIndex, ast.sourceSpan, ast.endSourceSpan); ngContentIndex, ast.sourceSpan, ast.endSourceSpan);
} }
visitComment(comment: html.Comment, context: any): any { return null; } visitComment(comment: html.Comment, context: any): any { return null; }

View File

@ -105,8 +105,8 @@ function generateHandleEventMethod(
compileElement.view, compileElement.view, compileElement.view.componentContext, compileElement.view, compileElement.view, compileElement.view.componentContext,
renderEvent.handler, `sub_${renderEventIdx}`); renderEvent.handler, `sub_${renderEventIdx}`);
const trueStmts = evalResult.stmts; const trueStmts = evalResult.stmts;
if (evalResult.preventDefault) { if (evalResult.allowDefault) {
trueStmts.push(resultVar.set(evalResult.preventDefault.and(resultVar)).toStmt()); trueStmts.push(resultVar.set(evalResult.allowDefault.and(resultVar)).toStmt());
} }
// TODO(tbosch): convert this into a `switch` once our OutputAst supports it. // TODO(tbosch): convert this into a `switch` once our OutputAst supports it.
handleEventStmts.push( handleEventStmts.push(

View File

@ -0,0 +1,5 @@
# Compiler Integration for new View Engine
This folder contains the code to integrate the compiler with the new view engine.
Note: This is work in progress, and once complete will replace the regular view_compiler folder.

View File

@ -0,0 +1,738 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {ChangeDetectionStrategy} from '@angular/core';
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
import {CompileDiDependencyMetadata, CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileProviderMetadata, CompileTokenMetadata, identifierModuleUrl, identifierName, tokenReference} from '../compile_metadata';
import {EventHandlerVars, NameResolver, convertActionBinding, convertPropertyBinding} from '../compiler_util/expression_converter';
import {CompilerConfig} from '../config';
import {AST, ASTWithSource, Interpolation} from '../expression_parser/ast';
import {Identifiers, createIdentifier, resolveIdentifier} from '../identifiers';
import {CompilerInjectable} from '../injectable';
import * as o from '../output/output_ast';
import {convertValueToOutputAst} from '../output/value_util';
import {LifecycleHooks, viewEngine} from '../private_import_core';
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ProviderAst, ProviderAstType, QueryId, QueryMatch, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
import {ViewEncapsulationEnum} from '../view_compiler/constants';
import {ComponentFactoryDependency, ComponentViewDependency, DirectiveWrapperDependency, ViewCompileResult, ViewCompiler} from '../view_compiler/view_compiler';
const CLASS_ATTR = 'class';
const STYLE_ATTR = 'style';
@CompilerInjectable()
export class ViewCompilerNext extends ViewCompiler {
constructor(
private _genConfigNext: CompilerConfig, private _schemaRegistryNext: ElementSchemaRegistry) {
super(_genConfigNext, _schemaRegistryNext);
}
compileComponent(
component: CompileDirectiveMetadata, template: TemplateAst[], styles: o.Expression,
pipes: CompilePipeSummary[],
compiledAnimations: AnimationEntryCompileResult[]): ViewCompileResult {
const compName = identifierName(component.type) + (component.isHost ? `_Host` : '');
let embeddedViewCount = 0;
const viewBuilderFactory = (parent: ViewBuilder): ViewBuilder => {
const embeddedViewIndex = embeddedViewCount++;
const viewName = `view_${compName}_${embeddedViewIndex}`;
return new ViewBuilder(parent, viewName, viewBuilderFactory);
};
const visitor = viewBuilderFactory(null);
visitor.visitAll([], template, 0);
const statements: o.Statement[] = [];
statements.push(...visitor.build(component));
return new ViewCompileResult(statements, visitor.viewName, []);
}
}
interface ViewBuilderFactory {
(parent: ViewBuilder): ViewBuilder;
}
interface UpdateExpression {
nodeIndex: number;
expressions: {context: o.Expression, value: AST}[];
}
interface HandleEventExpression {
nodeIndex: number;
context: o.Expression;
eventName: string;
expression: AST;
}
const VIEW_VAR = o.variable('view');
const CHECK_VAR = o.variable('check');
const COMP_VAR = o.variable('comp');
const NODE_INDEX_VAR = o.variable('nodeIndex');
const EVENT_NAME_VAR = o.variable('eventName');
const ALLOW_DEFAULT_VAR = o.variable(`allowDefault`);
class ViewBuilder implements TemplateAstVisitor, NameResolver {
private nodeDefs: o.Expression[] = [];
private refNodeIndices: {[refName: string]: number} = {};
private variables: VariableAst[] = [];
private children: ViewBuilder[] = [];
private updateExpressions: UpdateExpression[] = [];
private handleEventExpressions: HandleEventExpression[] = [];
constructor(
private parent: ViewBuilder, public viewName: string,
private viewBuilderFactory: ViewBuilderFactory) {}
visitAll(variables: VariableAst[], astNodes: TemplateAst[], elementDepth: number) {
this.variables = variables;
templateVisitAll(this, astNodes, {elementDepth});
if (astNodes.length === 0 || (this.parent && hasViewContainer(astNodes[astNodes.length - 1]))) {
// if the view is empty, or an embedded view has a view container as last root nde,
// create an additional root node.
this.nodeDefs.push(o.importExpr(createIdentifier(Identifiers.anchorDef)).callFn([
o.literal(viewEngine.NodeFlags.None), o.NULL_EXPR, o.NULL_EXPR, o.literal(0)
]));
}
}
build(component: CompileDirectiveMetadata, targetStatements: o.Statement[] = []): o.Statement[] {
const compType = o.importType(component.type);
this.children.forEach((child) => { child.build(component, targetStatements); });
const updateStmts: o.Statement[] = [];
let updateBindingCount = 0;
this.updateExpressions
.forEach(
({expressions, nodeIndex}) => {
const exprs = expressions.map(({context, value}) => {
const bindingId = `${updateBindingCount++}`;
const {stmts, currValExpr} =
convertPropertyBinding(null, this, context, value, bindingId);
updateStmts.push(...stmts);
return currValExpr;
});
if (exprs.length > 10) {
updateStmts.push(
CHECK_VAR
.callFn([
VIEW_VAR, o.literal(nodeIndex),
o.literal(viewEngine.ArgumentType.Dynamic), o.literalArr(exprs)
])
.toStmt());
} else {
updateStmts.push(
CHECK_VAR.callFn((<o.Expression[]>[VIEW_VAR, o.literal(nodeIndex), o.literal(viewEngine.ArgumentType.Inline)]).concat(exprs)).toStmt());
}
});
let updateFn: o.Expression;
if (updateStmts.length > 0) {
updateFn = o.fn(
[new o.FnParam(CHECK_VAR.name), new o.FnParam(VIEW_VAR.name)],
[COMP_VAR.set(VIEW_VAR.prop('component')).toDeclStmt(compType), ...updateStmts]);
} else {
updateFn = o.NULL_EXPR;
}
const handleEventStmts: o.Statement[] = [];
let handleEventBindingCount = 0;
this.handleEventExpressions.forEach(({expression, context, nodeIndex, eventName}) => {
const bindingId = `${handleEventBindingCount++}`;
const {stmts, allowDefault} =
convertActionBinding(null, this, context, expression, bindingId);
const trueStmts = stmts;
if (allowDefault) {
trueStmts.push(ALLOW_DEFAULT_VAR.set(allowDefault.and(ALLOW_DEFAULT_VAR)).toStmt());
}
handleEventStmts.push(new o.IfStmt(
o.literal(nodeIndex)
.identical(NODE_INDEX_VAR)
.and(o.literal(eventName).identical(EVENT_NAME_VAR)),
trueStmts));
});
let handleEventFn: o.Expression;
if (handleEventStmts.length > 0) {
handleEventFn = o.fn(
[
new o.FnParam(VIEW_VAR.name), new o.FnParam(NODE_INDEX_VAR.name),
new o.FnParam(EVENT_NAME_VAR.name), new o.FnParam(EventHandlerVars.event.name)
],
[
ALLOW_DEFAULT_VAR.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE),
COMP_VAR.set(VIEW_VAR.prop('component')).toDeclStmt(compType), ...handleEventStmts,
new o.ReturnStatement(ALLOW_DEFAULT_VAR)
]);
} else {
handleEventFn = o.NULL_EXPR;
}
let viewFlags = viewEngine.ViewFlags.None;
if (!this.parent && component.changeDetection === ChangeDetectionStrategy.OnPush) {
viewFlags |= viewEngine.ViewFlags.OnPush;
}
const viewFactory = new o.DeclareFunctionStmt(
this.viewName, [],
[new o.ReturnStatement(o.importExpr(createIdentifier(Identifiers.viewDef)).callFn([
o.literal(viewFlags), o.literalArr(this.nodeDefs), updateFn, handleEventFn
]))]);
targetStatements.push(viewFactory);
return targetStatements;
}
visitNgContent(ast: NgContentAst, context: any): any {}
visitText(ast: TextAst, context: any): any {
// textDef(ngContentIndex: number, constants: string[]): NodeDef;
this.nodeDefs.push(o.importExpr(createIdentifier(Identifiers.textDef)).callFn([
o.NULL_EXPR, o.literalArr([o.literal(ast.value)])
]));
}
visitBoundText(ast: BoundTextAst, context: any): any {
const nodeIndex = this.nodeDefs.length;
const astWithSource = <ASTWithSource>ast.value;
const inter = <Interpolation>astWithSource.ast;
this.updateExpressions.push({
nodeIndex,
expressions: inter.expressions.map((expr) => { return {context: COMP_VAR, value: expr}; })
});
// textDef(ngContentIndex: number, constants: string[]): NodeDef;
this.nodeDefs.push(o.importExpr(createIdentifier(Identifiers.textDef)).callFn([
o.NULL_EXPR, o.literalArr(inter.strings.map(s => o.literal(s)))
]));
}
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: {elementDepth: number}): any {
const nodeIndex = this.nodeDefs.length;
// reserve the space in the nodeDefs array
this.nodeDefs.push(null);
const {flags, queryMatchesExpr} = this._visitElementOrTemplate(nodeIndex, ast, context);
const childCount = this.nodeDefs.length - nodeIndex - 1;
const childVisitor = this.viewBuilderFactory(this);
this.children.push(childVisitor);
childVisitor.visitAll(ast.variables, ast.children, context.elementDepth + 1);
// anchorDef(
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
// childCount: number, templateFactory?: ViewDefinitionFactory): NodeDef;
this.nodeDefs[nodeIndex] = o.importExpr(createIdentifier(Identifiers.anchorDef)).callFn([
o.literal(flags), queryMatchesExpr, o.NULL_EXPR, o.literal(childCount),
o.variable(childVisitor.viewName)
]);
}
visitElement(ast: ElementAst, context: {elementDepth: number}): any {
const nodeIndex = this.nodeDefs.length;
// reserve the space in the nodeDefs array so we can add children
this.nodeDefs.push(null);
const {flags, usedEvents, queryMatchesExpr, hostBindings} =
this._visitElementOrTemplate(nodeIndex, ast, context);
templateVisitAll(this, ast.children, {elementDepth: context.elementDepth + 1});
const childCount = this.nodeDefs.length - nodeIndex - 1;
ast.inputs.forEach((inputAst) => {
hostBindings.push({context: COMP_VAR, value: (<ASTWithSource>inputAst.value).ast});
});
this.updateExpressions.push({nodeIndex, expressions: hostBindings});
const inputDefs = elementBindingDefs(ast.inputs);
ast.directives.forEach(
(dirAst, dirIndex) => { inputDefs.push(...elementBindingDefs(dirAst.hostProperties)); });
const outputDefs = usedEvents.map(([target, eventName]) => {
return target ? o.literalArr([o.literal(target), o.literal(eventName)]) :
o.literal(eventName);
});
// elementDef(
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
// childCount: number, name: string, fixedAttrs: {[name: string]: string} = {},
// bindings?:
// ([BindingType.ElementClass, string] | [BindingType.ElementStyle, string, string] |
// [BindingType.ElementAttribute | BindingType.ElementProperty, string,
// SecurityContext])[],
// outputs?: (string | [string, string])[]): NodeDef;
this.nodeDefs[nodeIndex] = o.importExpr(createIdentifier(Identifiers.elementDef)).callFn([
o.literal(flags), queryMatchesExpr, o.NULL_EXPR, o.literal(childCount), o.literal(ast.name),
fixedAttrsDef(ast), inputDefs.length ? o.literalArr(inputDefs) : o.NULL_EXPR,
outputDefs.length ? o.literalArr(outputDefs) : o.NULL_EXPR
]);
}
private _visitElementOrTemplate(
nodeIndex: number, ast: {
hasViewContainer: boolean,
outputs: BoundEventAst[],
directives: DirectiveAst[],
providers: ProviderAst[],
references: ReferenceAst[],
queryMatches: QueryMatch[]
},
context: {elementDepth: number}): {
flags: number,
usedEvents: [string, string][],
queryMatchesExpr: o.Expression,
hostBindings: {value: AST, context: o.Expression}[],
} {
let flags = viewEngine.NodeFlags.None;
if (ast.hasViewContainer) {
flags |= viewEngine.NodeFlags.HasEmbeddedViews;
}
const usedEvents = new Map<string, [string, string]>();
ast.outputs.forEach((event) => {
usedEvents.set(
viewEngine.elementEventFullName(event.target, event.name), [event.target, event.name]);
});
ast.directives.forEach((dirAst) => {
dirAst.hostEvents.forEach((event) => {
usedEvents.set(
viewEngine.elementEventFullName(event.target, event.name), [event.target, event.name]);
});
});
const hostBindings: {value: AST, context: o.Expression}[] = [];
const hostEvents: {context: o.Expression, eventAst: BoundEventAst}[] = [];
ast.providers.forEach((providerAst, providerIndex) => {
let dirAst: DirectiveAst;
let dirIndex: number;
ast.directives.forEach((localDirAst, i) => {
if (localDirAst.directive.type.reference === providerAst.token.identifier.reference) {
dirAst = localDirAst;
dirIndex = i;
}
});
if (dirAst) {
const {hostBindings: dirHostBindings, hostEvents: dirHostEvents} = this._visitDirective(
providerAst, dirAst, dirIndex, nodeIndex, context.elementDepth, ast.references,
ast.queryMatches, usedEvents);
hostBindings.push(...dirHostBindings);
hostEvents.push(...dirHostEvents);
} else {
this._visitProvider(providerAst, ast.queryMatches);
}
});
let queryMatchExprs: o.Expression[] = [];
ast.queryMatches.forEach((match) => {
let valueType: number;
if (tokenReference(match.value) === resolveIdentifier(Identifiers.ElementRef)) {
valueType = viewEngine.QueryValueType.ElementRef;
} else if (tokenReference(match.value) === resolveIdentifier(Identifiers.ViewContainerRef)) {
valueType = viewEngine.QueryValueType.ViewContainerRef;
} else if (tokenReference(match.value) === resolveIdentifier(Identifiers.TemplateRef)) {
valueType = viewEngine.QueryValueType.TemplateRef;
}
if (valueType != null) {
queryMatchExprs.push(
o.literalArr([o.literal(calcQueryId(match.query)), o.literal(valueType)]));
}
});
ast.references.forEach((ref) => {
let valueType: number;
if (!ref.value) {
valueType = viewEngine.QueryValueType.RenderElement;
} else if (tokenReference(ref.value) === resolveIdentifier(Identifiers.TemplateRef)) {
valueType = viewEngine.QueryValueType.TemplateRef;
}
if (valueType != null) {
this.refNodeIndices[ref.name] = nodeIndex;
queryMatchExprs.push(o.literalArr([o.literal(`#${ref.name}`), o.literal(valueType)]));
}
});
ast.outputs.forEach(
(outputAst) => { hostEvents.push({context: COMP_VAR, eventAst: outputAst}); });
hostEvents.forEach((hostEvent) => {
this.handleEventExpressions.push({
nodeIndex,
context: hostEvent.context,
eventName:
viewEngine.elementEventFullName(hostEvent.eventAst.target, hostEvent.eventAst.name),
expression: (<ASTWithSource>hostEvent.eventAst.handler).ast
});
});
return {
flags,
usedEvents: Array.from(usedEvents.values()),
queryMatchesExpr: queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
hostBindings,
};
}
private _visitDirective(
providerAst: ProviderAst, directiveAst: DirectiveAst, directiveIndex: number,
elementNodeIndex: number, elementDepth: number, refs: ReferenceAst[],
queryMatches: QueryMatch[], usedEvents: Map<string, any>): {
hostBindings: {value: AST, context: o.Expression}[],
hostEvents: {context: o.Expression, eventAst: BoundEventAst}[]
} {
const nodeIndex = this.nodeDefs.length;
// reserve the space in the nodeDefs array so we can add children
this.nodeDefs.push(null);
const {flags, queryMatchExprs, providerExpr, providerType, depsExpr} =
this._visitProviderOrDirective(providerAst, queryMatches);
refs.forEach((ref) => {
if (ref.value && tokenReference(ref.value) === tokenReference(providerAst.token)) {
this.refNodeIndices[ref.name] = nodeIndex;
queryMatchExprs.push(o.literalArr(
[o.literal(`#${ref.name}`), o.literal(viewEngine.QueryValueType.Provider)]));
}
});
let compView = o.NULL_EXPR;
if (directiveAst.directive.isComponent) {
compView = o.importExpr({reference: directiveAst.directive.componentViewType});
}
const inputDefs = directiveAst.inputs.map((inputAst, inputIndex) => {
const mapValue = o.literalArr([o.literal(inputIndex), o.literal(inputAst.directiveName)]);
// Note: it's important to not quote the key so that we can capture renames by minifiers!
return new o.LiteralMapEntry(inputAst.directiveName, mapValue, false);
});
const outputDefs: o.LiteralMapEntry[] = [];
const dirMeta = directiveAst.directive;
Object.keys(dirMeta.outputs).forEach((propName) => {
const eventName = dirMeta.outputs[propName];
if (usedEvents.has(eventName)) {
// Note: it's important to not quote the key so that we can capture renames by minifiers!
outputDefs.push(new o.LiteralMapEntry(propName, o.literal(eventName), false));
}
});
if (directiveAst.inputs.length) {
this.updateExpressions.push({
nodeIndex,
expressions: directiveAst.inputs.map(
input => { return {context: COMP_VAR, value: (<ASTWithSource>input.value).ast}; })
});
}
const dirContextExpr = o.importExpr(createIdentifier(Identifiers.nodeValue)).callFn([
VIEW_VAR, o.literal(nodeIndex)
]);
const hostBindings = directiveAst.hostProperties.map((hostBindingAst) => {
return {
value: (<ASTWithSource>hostBindingAst.value).ast,
context: dirContextExpr,
};
});
const hostEvents = directiveAst.hostEvents.map(
(hostEventAst) => { return {context: dirContextExpr, eventAst: hostEventAst}; });
const childCount = directiveAst.directive.queries.length;
directiveAst.directive.queries.forEach((query, queryIndex) => {
const queryId: QueryId = {elementDepth, directiveIndex, queryIndex};
const bindingType =
query.first ? viewEngine.QueryBindingType.First : viewEngine.QueryBindingType.All;
// queryDef(
// flags: NodeFlags, id: string, bindings: {[propName: string]: QueryBindingType}): NodeDef
// {
this.nodeDefs.push(o.importExpr(createIdentifier(Identifiers.queryDef)).callFn([
o.literal(viewEngine.NodeFlags.HasContentQuery), o.literal(calcQueryId(queryId)),
new o.LiteralMapExpr([new o.LiteralMapEntry(query.propertyName, o.literal(bindingType))])
]));
});
// directiveDef(
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], childCount: number, ctor:
// any,
// deps: ([DepFlags, any] | any)[], props?: {[name: string]: [number, string]},
// outputs?: {[name: string]: string}, component?: () => ViewDefinition): NodeDef;
const nodeDef = o.importExpr(createIdentifier(Identifiers.directiveDef)).callFn([
o.literal(flags), queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
o.literal(childCount), providerExpr, depsExpr,
inputDefs.length ? new o.LiteralMapExpr(inputDefs) : o.NULL_EXPR,
outputDefs.length ? new o.LiteralMapExpr(outputDefs) : o.NULL_EXPR, compView
]);
this.nodeDefs[nodeIndex] = nodeDef;
return {hostBindings, hostEvents};
}
private _visitProvider(providerAst: ProviderAst, queryMatches: QueryMatch[]): void {
const nodeIndex = this.nodeDefs.length;
// reserve the space in the nodeDefs array so we can add children
this.nodeDefs.push(null);
const {flags, queryMatchExprs, providerExpr, providerType, depsExpr} =
this._visitProviderOrDirective(providerAst, queryMatches);
// providerDef(
// flags: NodeFlags, matchedQueries: [string, QueryValueType][], type: ProviderType, token:
// any,
// value: any, deps: ([DepFlags, any] | any)[]): NodeDef;
const nodeDef = o.importExpr(createIdentifier(Identifiers.providerDef)).callFn([
o.literal(flags), queryMatchExprs.length ? o.literalArr(queryMatchExprs) : o.NULL_EXPR,
o.literal(providerType), tokenExpr(providerAst.token), providerExpr, depsExpr
]);
this.nodeDefs[nodeIndex] = nodeDef;
}
private _visitProviderOrDirective(providerAst: ProviderAst, queryMatches: QueryMatch[]): {
flags: number,
queryMatchExprs: o.Expression[],
providerExpr: o.Expression,
providerType: number,
depsExpr: o.Expression
} {
let flags = viewEngine.NodeFlags.None;
if (!providerAst.eager) {
flags |= viewEngine.NodeFlags.LazyProvider;
}
providerAst.lifecycleHooks.forEach((lifecycleHook) => {
// for regular providers, we only support ngOnDestroy
if (lifecycleHook === LifecycleHooks.OnDestroy ||
providerAst.providerType === ProviderAstType.Directive ||
providerAst.providerType === ProviderAstType.Component) {
flags |= lifecycleHookToNodeFlag(lifecycleHook);
}
});
let queryMatchExprs: o.Expression[] = [];
queryMatches.forEach((match) => {
if (tokenReference(match.value) === tokenReference(providerAst.token)) {
queryMatchExprs.push(o.literalArr(
[o.literal(calcQueryId(match.query)), o.literal(viewEngine.QueryValueType.Provider)]));
}
});
const {providerExpr, providerType, depsExpr} = providerDef(providerAst);
return {flags, queryMatchExprs, providerExpr, providerType, depsExpr};
}
callPipe(name: string, input: o.Expression, args: o.Expression[]): o.Expression {
throw new Error('Pipes are not yet supported!');
}
getLocal(name: string): o.Expression {
if (name == EventHandlerVars.event.name) {
return EventHandlerVars.event;
}
let currViewExpr: o.Expression = VIEW_VAR;
for (let currBuilder: ViewBuilder = this; currBuilder;
currBuilder = currBuilder.parent, currViewExpr = currViewExpr.prop('parent')) {
// check references
const refNodeIndex = currBuilder.refNodeIndices[name];
if (refNodeIndex != null) {
return o.importExpr(createIdentifier(Identifiers.nodeValue)).callFn([
currViewExpr, o.literal(refNodeIndex)
]);
}
// check variables
const varAst = currBuilder.variables.find((varAst) => varAst.name === name);
if (varAst) {
return currViewExpr.prop('context').prop(varAst.value);
}
}
return null;
}
visitDirective(ast: DirectiveAst, context: {usedEvents: Set<string>}): any {}
visitDirectiveProperty(ast: BoundDirectivePropertyAst, context: any): any {}
visitReference(ast: ReferenceAst, context: any): any {}
visitVariable(ast: VariableAst, context: any): any {}
visitEvent(ast: BoundEventAst, context: any): any {}
visitElementProperty(ast: BoundElementPropertyAst, context: any): any {}
visitAttr(ast: AttrAst, context: any): any {}
}
function providerDef(providerAst: ProviderAst):
{providerExpr: o.Expression, providerType: number, depsExpr: o.Expression} {
return providerAst.multiProvider ? multiProviderDef(providerAst.providers) :
singleProviderDef(providerAst.providers[0]);
}
function multiProviderDef(providers: CompileProviderMetadata[]):
{providerExpr: o.Expression, providerType: number, depsExpr: o.Expression} {
const allDepDefs: o.Expression[] = [];
const allParams: o.FnParam[] = [];
const exprs = providers.map((provider, providerIndex) => {
const depExprs = provider.deps.map((dep, depIndex) => {
const paramName = `p${providerIndex}_${depIndex}`;
allParams.push(new o.FnParam(paramName, o.DYNAMIC_TYPE));
allDepDefs.push(depDef(dep));
return o.variable(paramName);
});
let expr: o.Expression;
if (provider.useClass) {
expr = o.importExpr(provider.useClass).instantiate(depExprs);
} else if (provider.useFactory) {
expr = o.importExpr(provider.useFactory).callFn(depExprs);
} else if (provider.useExisting) {
expr = depExprs[0];
} else {
expr = convertValueToOutputAst(provider.useValue);
}
return expr;
});
const providerExpr = o.fn(allParams, [new o.ReturnStatement(o.literalArr(exprs))]);
return {
providerExpr,
providerType: viewEngine.ProviderType.Factory,
depsExpr: o.literalArr(allDepDefs)
};
}
function singleProviderDef(providerMeta: CompileProviderMetadata):
{providerExpr: o.Expression, providerType: number, depsExpr: o.Expression} {
let providerExpr: o.Expression;
let providerType: number;
let deps: CompileDiDependencyMetadata[];
if (providerMeta.useClass) {
providerExpr = o.importExpr(providerMeta.useClass);
providerType = viewEngine.ProviderType.Class;
deps = providerMeta.deps || providerMeta.useClass.diDeps;
} else if (providerMeta.useFactory) {
providerExpr = o.importExpr(providerMeta.useFactory);
providerType = viewEngine.ProviderType.Factory;
deps = providerMeta.deps || providerMeta.useFactory.diDeps;
} else if (providerMeta.useExisting) {
providerExpr = o.NULL_EXPR;
providerType = viewEngine.ProviderType.UseExisting;
deps = [{token: providerMeta.useExisting}];
} else {
providerExpr = convertValueToOutputAst(providerMeta.useValue);
providerType = viewEngine.ProviderType.Value;
deps = [];
}
const depsExpr = o.literalArr(deps.map(dep => depDef(dep)));
return {providerExpr, providerType, depsExpr};
}
function tokenExpr(tokenMeta: CompileTokenMetadata): o.Expression {
return tokenMeta.identifier ? o.importExpr(tokenMeta.identifier) : o.literal(tokenMeta.value);
}
function depDef(dep: CompileDiDependencyMetadata): o.Expression {
// Note: the following fields have already been normalized out by provider_analyzer:
// - isAttribute, isSelf, isHost
const expr = dep.isValue ? convertValueToOutputAst(dep.value) : tokenExpr(dep.token);
let flags = viewEngine.DepFlags.None;
if (dep.isSkipSelf) {
flags |= viewEngine.DepFlags.SkipSelf;
}
if (dep.isOptional) {
flags |= viewEngine.DepFlags.Optional;
}
if (dep.isValue) {
flags |= viewEngine.DepFlags.Value;
}
return flags === viewEngine.DepFlags.None ? expr : o.literalArr([o.literal(flags), expr]);
}
function hasViewContainer(ast: TemplateAst): boolean {
if (ast instanceof EmbeddedTemplateAst) {
return ast.hasViewContainer;
} else if (ast instanceof ElementAst) {
return ast.hasViewContainer;
}
return false;
}
function calcQueryId(queryId: QueryId): string {
if (queryId.directiveIndex == null) {
// view query
return `v${queryId.queryIndex}`;
} else {
return `c${queryId.elementDepth}_${queryId.directiveIndex}_${queryId.queryIndex}`;
}
}
function lifecycleHookToNodeFlag(lifecycleHook: LifecycleHooks): number {
let nodeFlag = viewEngine.NodeFlags.None;
switch (lifecycleHook) {
case LifecycleHooks.AfterContentChecked:
nodeFlag = viewEngine.NodeFlags.AfterContentChecked;
break;
case LifecycleHooks.AfterContentInit:
nodeFlag = viewEngine.NodeFlags.AfterContentInit;
break;
case LifecycleHooks.AfterViewChecked:
nodeFlag = viewEngine.NodeFlags.AfterViewChecked;
break;
case LifecycleHooks.AfterViewInit:
nodeFlag = viewEngine.NodeFlags.AfterViewInit;
break;
case LifecycleHooks.DoCheck:
nodeFlag = viewEngine.NodeFlags.DoCheck;
break;
case LifecycleHooks.OnChanges:
nodeFlag = viewEngine.NodeFlags.OnChanges;
break;
case LifecycleHooks.OnDestroy:
nodeFlag = viewEngine.NodeFlags.OnDestroy;
break;
case LifecycleHooks.OnInit:
nodeFlag = viewEngine.NodeFlags.OnInit;
break;
}
return nodeFlag;
}
function elementBindingDefs(inputAsts: BoundElementPropertyAst[]): o.Expression[] {
return inputAsts.map((inputAst) => {
switch (inputAst.type) {
case PropertyBindingType.Attribute:
return o.literalArr([
o.literal(viewEngine.BindingType.ElementAttribute), o.literal(inputAst.name),
o.literal(inputAst.securityContext)
]);
case PropertyBindingType.Property:
return o.literalArr([
o.literal(viewEngine.BindingType.ElementProperty), o.literal(inputAst.name),
o.literal(inputAst.securityContext)
]);
case PropertyBindingType.Class:
return o.literalArr(
[o.literal(viewEngine.BindingType.ElementClass), o.literal(inputAst.name)]);
case PropertyBindingType.Style:
return o.literalArr([
o.literal(viewEngine.BindingType.ElementStyle), o.literal(inputAst.name),
o.literal(inputAst.unit)
]);
}
});
}
function fixedAttrsDef(elementAst: ElementAst): o.LiteralMapExpr {
const mapResult: {[key: string]: string} = {};
elementAst.attrs.forEach(attrAst => { mapResult[attrAst.name] = attrAst.value; });
elementAst.directives.forEach(dirAst => {
Object.keys(dirAst.directive.hostAttributes).forEach(name => {
const value = dirAst.directive.hostAttributes[name];
const prevValue = mapResult[name];
mapResult[name] = prevValue != null ? mergeAttributeValue(name, prevValue, value) : value;
});
});
const mapEntries: o.LiteralMapEntry[] = [];
// Note: We need to sort to get a defined output order
// for tests and for caching generated artifacts...
Object.keys(mapResult).sort().forEach((attrName) => {
mapEntries.push(new o.LiteralMapEntry(attrName, o.literal(mapResult[attrName]), true));
});
return new o.LiteralMapExpr(mapEntries);
}
function mergeAttributeValue(attrName: string, attrValue1: string, attrValue2: string): string {
if (attrName == CLASS_ATTR || attrName == STYLE_ATTR) {
return `${attrValue1} ${attrValue2}`;
} else {
return attrValue2;
}
}

View File

@ -92,14 +92,14 @@ export function main() {
new class extends NullVisitor{ new class extends NullVisitor{
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any) { return ast; } visitEmbeddedTemplate(ast: EmbeddedTemplateAst, context: any) { return ast; }
}, },
new EmbeddedTemplateAst([], [], [], [], [], [], false, [], 0, null)); new EmbeddedTemplateAst([], [], [], [], [], [], false, [], [], 0, null));
}); });
it('should visit ElementAst', () => { it('should visit ElementAst', () => {
expectVisitedNode( expectVisitedNode(
new class extends new class extends
NullVisitor{visitElement(ast: ElementAst, context: any) { return ast; }}, NullVisitor{visitElement(ast: ElementAst, context: any) { return ast; }},
new ElementAst('foo', [], [], [], [], [], [], false, [], 0, null, null)); new ElementAst('foo', [], [], [], [], [], [], false, [], [], 0, null, null));
}); });
it('should visit RefererenceAst', () => { it('should visit RefererenceAst', () => {
@ -171,8 +171,8 @@ export function main() {
}; };
const nodes: TemplateAst[] = [ const nodes: TemplateAst[] = [
new NgContentAst(0, 0, null), new NgContentAst(0, 0, null),
new EmbeddedTemplateAst([], [], [], [], [], [], false, [], 0, null), new EmbeddedTemplateAst([], [], [], [], [], [], false, [], [], 0, null),
new ElementAst('foo', [], [], [], [], [], [], false, [], 0, null, null), new ElementAst('foo', [], [], [], [], [], [], false, [], [], 0, null, null),
new ReferenceAst('foo', null, null), new VariableAst('foo', 'bar', null), new ReferenceAst('foo', null, null), new VariableAst('foo', 'bar', null),
new BoundEventAst('foo', 'bar', 'goo', null, null), new BoundEventAst('foo', 'bar', 'goo', null, null),
new BoundElementPropertyAst('foo', null, null, false, null, 'bar', null), new BoundElementPropertyAst('foo', null, null, false, null, 'bar', null),
@ -2088,7 +2088,7 @@ class FooAstTransformer extends ThrowingVisitor {
visitElement(ast: ElementAst, context: any): any { visitElement(ast: ElementAst, context: any): any {
if (ast.name != 'div') return ast; if (ast.name != 'div') return ast;
return new ElementAst( return new ElementAst(
'foo', [], [], [], [], [], [], false, [], ast.ngContentIndex, ast.sourceSpan, 'foo', [], [], [], [], [], [], false, [], [], ast.ngContentIndex, ast.sourceSpan,
ast.endSourceSpan); ast.endSourceSpan);
} }
} }
@ -2097,7 +2097,7 @@ class BarAstTransformer extends FooAstTransformer {
visitElement(ast: ElementAst, context: any): any { visitElement(ast: ElementAst, context: any): any {
if (ast.name != 'foo') return ast; if (ast.name != 'foo') return ast;
return new ElementAst( return new ElementAst(
'bar', [], [], [], [], [], [], false, [], ast.ngContentIndex, ast.sourceSpan, 'bar', [], [], [], [], [], [], false, [], [], ast.ngContentIndex, ast.sourceSpan,
ast.endSourceSpan); ast.endSourceSpan);
} }
} }

View File

@ -7,7 +7,7 @@
*/ */
import {AnimationQueue} from './animation/animation_queue'; import {AnimationQueue} from './animation/animation_queue';
import {ApplicationInitStatus} from './application_init'; import {APP_INITIALIZER, ApplicationInitStatus} from './application_init';
import {ApplicationRef, ApplicationRef_} from './application_ref'; import {ApplicationRef, ApplicationRef_} from './application_ref';
import {APP_ID_RANDOM_PROVIDER} from './application_tokens'; import {APP_ID_RANDOM_PROVIDER} from './application_tokens';
import {IterableDiffers, KeyValueDiffers, defaultIterableDiffers, defaultKeyValueDiffers} from './change_detection/change_detection'; import {IterableDiffers, KeyValueDiffers, defaultIterableDiffers, defaultKeyValueDiffers} from './change_detection/change_detection';
@ -16,6 +16,7 @@ import {LOCALE_ID} from './i18n/tokens';
import {Compiler} from './linker/compiler'; import {Compiler} from './linker/compiler';
import {ViewUtils} from './linker/view_utils'; import {ViewUtils} from './linker/view_utils';
import {NgModule} from './metadata'; import {NgModule} from './metadata';
import {initServicesIfNeeded} from './view/index';
export function _iterableDiffersFactory() { export function _iterableDiffersFactory() {
return defaultIterableDiffers; return defaultIterableDiffers;
@ -29,6 +30,10 @@ export function _localeFactory(locale?: string): string {
return locale || 'en-US'; return locale || 'en-US';
} }
export function _initViewEngine() {
initServicesIfNeeded();
}
/** /**
* This module includes the providers of @angular/core that are needed * This module includes the providers of @angular/core that are needed
* to bootstrap components via `ApplicationRef`. * to bootstrap components via `ApplicationRef`.
@ -51,6 +56,7 @@ export function _localeFactory(locale?: string): string {
useFactory: _localeFactory, useFactory: _localeFactory,
deps: [[new Inject(LOCALE_ID), new Optional(), new SkipSelf()]] deps: [[new Inject(LOCALE_ID), new Optional(), new SkipSelf()]]
}, },
{provide: APP_INITIALIZER, useValue: _initViewEngine, multi: true},
] ]
}) })
export class ApplicationModule { export class ApplicationModule {

View File

@ -42,6 +42,7 @@ import * as reflection_types from './reflection/types';
import * as api from './render/api'; import * as api from './render/api';
import * as decorators from './util/decorators'; import * as decorators from './util/decorators';
import {isObservable, isPromise} from './util/lang'; import {isObservable, isPromise} from './util/lang';
import * as viewEngine from './view/index';
export const __core_private__: { export const __core_private__: {
isDefaultChangeDetectionStrategy: typeof constants.isDefaultChangeDetectionStrategy, isDefaultChangeDetectionStrategy: typeof constants.isDefaultChangeDetectionStrategy,
@ -109,6 +110,7 @@ export const __core_private__: {
AnimationTransition: typeof AnimationTransition AnimationTransition: typeof AnimationTransition
view_utils: typeof view_utils, view_utils: typeof view_utils,
ERROR_COMPONENT_TYPE: typeof ERROR_COMPONENT_TYPE, ERROR_COMPONENT_TYPE: typeof ERROR_COMPONENT_TYPE,
viewEngine: typeof viewEngine,
} = { } = {
isDefaultChangeDetectionStrategy: constants.isDefaultChangeDetectionStrategy, isDefaultChangeDetectionStrategy: constants.isDefaultChangeDetectionStrategy,
ChangeDetectorStatus: constants.ChangeDetectorStatus, ChangeDetectorStatus: constants.ChangeDetectorStatus,
@ -125,6 +127,7 @@ export const __core_private__: {
registerModuleFactory: ng_module_factory_loader.registerModuleFactory, registerModuleFactory: ng_module_factory_loader.registerModuleFactory,
ViewType: view_type.ViewType, ViewType: view_type.ViewType,
view_utils: view_utils, view_utils: view_utils,
viewEngine: viewEngine,
ViewMetadata: metadata_view.ViewMetadata, ViewMetadata: metadata_view.ViewMetadata,
DebugContext: debug_context.DebugContext, DebugContext: debug_context.DebugContext,
StaticNodeDebugInfo: debug_context.StaticNodeDebugInfo, StaticNodeDebugInfo: debug_context.StaticNodeDebugInfo,

View File

@ -9,18 +9,19 @@
import {isDevMode} from '../application_ref'; import {isDevMode} from '../application_ref';
import {SecurityContext} from '../security'; import {SecurityContext} from '../security';
import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementOutputDef, NodeData, NodeDef, NodeFlags, NodeType, QueryValueType, Services, ViewData, ViewDefinition, ViewFlags, asElementData} from './types'; import {BindingDef, BindingType, DebugContext, DisposableFn, ElementData, ElementOutputDef, NodeData, NodeDef, NodeFlags, NodeType, QueryValueType, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, asElementData} from './types';
import {checkAndUpdateBinding, dispatchEvent, sliceErrorStack, unwrapValue} from './util'; import {checkAndUpdateBinding, dispatchEvent, elementEventFullName, resolveViewDefinition, sliceErrorStack, unwrapValue} from './util';
export function anchorDef( export function anchorDef(
flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number, flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
childCount: number, template?: ViewDefinition): NodeDef { childCount: number, templateFactory?: ViewDefinitionFactory): NodeDef {
const matchedQueryDefs: {[queryId: string]: QueryValueType} = {}; const matchedQueryDefs: {[queryId: string]: QueryValueType} = {};
if (matchedQueries) { if (matchedQueries) {
matchedQueries.forEach(([queryId, valueType]) => { matchedQueryDefs[queryId] = valueType; }); matchedQueries.forEach(([queryId, valueType]) => { matchedQueryDefs[queryId] = valueType; });
} }
// skip the call to sliceErrorStack itself + the call to this function. // skip the call to sliceErrorStack itself + the call to this function.
const source = isDevMode() ? sliceErrorStack(2, 3) : ''; const source = isDevMode() ? sliceErrorStack(2, 3) : '';
const template = templateFactory ? resolveViewDefinition(templateFactory) : null;
return { return {
type: NodeType.Element, type: NodeType.Element,
// will bet set by the view definition // will bet set by the view definition
@ -130,10 +131,10 @@ export function elementDef(
export function createElement(view: ViewData, renderHost: any, def: NodeDef): ElementData { export function createElement(view: ViewData, renderHost: any, def: NodeDef): ElementData {
const elDef = def.element; const elDef = def.element;
const rootElement = view.root.element; const rootSelectorOrNode = view.root.selectorOrNode;
const renderer = view.root.renderer; const renderer = view.root.renderer;
let el: any; let el: any;
if (view.parent || !rootElement) { if (view.parent || !rootSelectorOrNode) {
const parentNode = const parentNode =
def.parent != null ? asElementData(view, def.parent).renderElement : renderHost; def.parent != null ? asElementData(view, def.parent).renderElement : renderHost;
el = elDef.name ? renderer.createElement(elDef.name) : renderer.createComment(''); el = elDef.name ? renderer.createElement(elDef.name) : renderer.createComment('');
@ -141,7 +142,7 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): El
renderer.appendChild(parentNode, el); renderer.appendChild(parentNode, el);
} }
} else { } else {
el = rootElement; el = renderer.selectRootElement(rootSelectorOrNode);
} }
if (elDef.attrs) { if (elDef.attrs) {
for (let attrName in elDef.attrs) { for (let attrName in elDef.attrs) {
@ -151,7 +152,8 @@ export function createElement(view: ViewData, renderHost: any, def: NodeDef): El
if (elDef.outputs.length) { if (elDef.outputs.length) {
for (let i = 0; i < elDef.outputs.length; i++) { for (let i = 0; i < elDef.outputs.length; i++) {
const output = elDef.outputs[i]; const output = elDef.outputs[i];
const handleEventClosure = renderEventHandlerClosure(view, def.index, output.eventName); const handleEventClosure = renderEventHandlerClosure(
view, def.index, elementEventFullName(output.target, output.eventName));
const disposable = const disposable =
<any>renderer.listen(output.target || el, output.eventName, handleEventClosure); <any>renderer.listen(output.target || el, output.eventName, handleEventClosure);
view.disposables[def.disposableIndex + i] = disposable; view.disposables[def.disposableIndex + i] = disposable;

View File

@ -14,7 +14,7 @@ export {queryDef} from './query';
export {createComponentFactory} from './refs'; export {createComponentFactory} from './refs';
export {initServicesIfNeeded} from './services'; export {initServicesIfNeeded} from './services';
export {textDef} from './text'; export {textDef} from './text';
export {rootRenderNodes} from './util'; export {elementEventFullName, nodeValue, rootRenderNodes} from './util';
export {viewDef} from './view'; export {viewDef} from './view';
export {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach'; export {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';

View File

@ -16,7 +16,7 @@ import {Type} from '../type';
import {createChangeDetectorRef, createInjector, createTemplateRef, createViewContainerRef} from './refs'; import {createChangeDetectorRef, createInjector, createTemplateRef, createViewContainerRef} from './refs';
import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderOutputDef, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types'; import {BindingDef, BindingType, DepDef, DepFlags, DisposableFn, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderOutputDef, ProviderType, QueryBindingType, QueryDef, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewState, asElementData, asProviderData} from './types';
import {checkAndUpdateBinding, dispatchEvent, findElementDef, isComponentView, parentDiIndex, tokenKey, unwrapValue} from './util'; import {checkAndUpdateBinding, dispatchEvent, isComponentView, tokenKey, unwrapValue, viewParentDiIndex} from './util';
const RendererV1TokenKey = tokenKey(v1renderer.Renderer); const RendererV1TokenKey = tokenKey(v1renderer.Renderer);
const ElementRefTokenKey = tokenKey(ElementRef); const ElementRefTokenKey = tokenKey(ElementRef);
@ -278,6 +278,9 @@ function callFactory(
export function resolveDep( export function resolveDep(
view: ViewData, requestNodeIndex: number, elIndex: number, depDef: DepDef, view: ViewData, requestNodeIndex: number, elIndex: number, depDef: DepDef,
notFoundValue = Injector.THROW_IF_NOT_FOUND): any { notFoundValue = Injector.THROW_IF_NOT_FOUND): any {
if (depDef.flags & DepFlags.Value) {
return depDef.token;
}
const startView = view; const startView = view;
if (depDef.flags & DepFlags.Optional) { if (depDef.flags & DepFlags.Optional) {
notFoundValue = null; notFoundValue = null;
@ -288,7 +291,7 @@ export function resolveDep(
requestNodeIndex = null; requestNodeIndex = null;
elIndex = view.def.nodes[elIndex].parent; elIndex = view.def.nodes[elIndex].parent;
while (elIndex == null && view) { while (elIndex == null && view) {
elIndex = parentDiIndex(view); elIndex = viewParentDiIndex(view);
view = view.parent; view = view.parent;
} }
} }
@ -337,7 +340,7 @@ export function resolveDep(
} }
} }
requestNodeIndex = null; requestNodeIndex = null;
elIndex = parentDiIndex(view); elIndex = viewParentDiIndex(view);
view = view.parent; view = view.parent;
} }
return startView.root.injector.get(depDef.token, notFoundValue); return startView.root.injector.get(depDef.token, notFoundValue);

View File

@ -20,7 +20,7 @@ import {Type} from '../type';
import {DirectDomRenderer, LegacyRendererAdapter} from './renderer'; import {DirectDomRenderer, LegacyRendererAdapter} from './renderer';
import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types'; import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types';
import {findElementDef, isComponentView, parentDiIndex, renderNode, resolveViewDefinition, rootRenderNodes, tokenKey} from './util'; import {isComponentView, renderNode, resolveViewDefinition, rootRenderNodes, tokenKey, viewParentDiIndex} from './util';
const EMPTY_CONTEXT = new Object(); const EMPTY_CONTEXT = new Object();
@ -99,7 +99,7 @@ class ViewContainerRef_ implements ViewContainerRef {
let view = this._view; let view = this._view;
let elIndex = view.def.nodes[this._elIndex].parent; let elIndex = view.def.nodes[this._elIndex].parent;
while (elIndex == null && view) { while (elIndex == null && view) {
elIndex = parentDiIndex(view); elIndex = viewParentDiIndex(view);
view = view.parent; view = view.parent;
} }
return view ? new Injector_(view, elIndex) : this._view.root.injector; return view ? new Injector_(view, elIndex) : this._view.root.injector;
@ -119,7 +119,7 @@ class ViewContainerRef_ implements ViewContainerRef {
createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number): createEmbeddedView<C>(templateRef: TemplateRef<C>, context?: C, index?: number):
EmbeddedViewRef<C> { EmbeddedViewRef<C> {
const viewRef = templateRef.createEmbeddedView(context); const viewRef = templateRef.createEmbeddedView(context || <any>{});
this.insert(viewRef, index); this.insert(viewRef, index);
return viewRef; return viewRef;
} }

View File

@ -40,6 +40,8 @@ export class DirectDomRenderer implements RendererV2 {
nextSibling(node: any): any { return node.nextSiblibng; } nextSibling(node: any): any { return node.nextSiblibng; }
setAttribute(el: any, name: string, value: string): void { return el.setAttribute(name, value); } setAttribute(el: any, name: string, value: string): void { return el.setAttribute(name, value); }
removeAttribute(el: any, name: string): void { el.removeAttribute(name); } removeAttribute(el: any, name: string): void { el.removeAttribute(name); }
setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void {}
removeBindingDebugInfo(el: any, propertyName: string): void {}
addClass(el: any, name: string): void { el.classList.add(name); } addClass(el: any, name: string): void { el.classList.add(name); }
removeClass(el: any, name: string): void { el.classList.remove(name); } removeClass(el: any, name: string): void { el.classList.remove(name); }
setStyle(el: any, style: string, value: any): void { el.style[style] = value; } setStyle(el: any, style: string, value: any): void { el.style[style] = value; }
@ -97,8 +99,11 @@ export class LegacyRendererAdapter implements RendererV2 {
} }
appendChild(parent: any, newChild: any): void { this._delegate.projectNodes(parent, [newChild]); } appendChild(parent: any, newChild: any): void { this._delegate.projectNodes(parent, [newChild]); }
insertBefore(parent: any, newChild: any, refChild: any): void { insertBefore(parent: any, newChild: any, refChild: any): void {
const beforeSibling = refChild.nextSiblingOf ? refChild.nextSiblingOf : refChild; if (refChild) {
this._delegate.attachViewAfter(beforeSibling, [newChild]); this._delegate.attachViewAfter(refChild.previousSibling, [newChild]);
} else {
this.appendChild(parent, newChild);
}
} }
removeChild(parent: any, oldChild: any): void { removeChild(parent: any, oldChild: any): void {
if (parent) { if (parent) {
@ -108,14 +113,20 @@ export class LegacyRendererAdapter implements RendererV2 {
selectRootElement(selectorOrNode: any, debugInfo?: DebugContext): any { selectRootElement(selectorOrNode: any, debugInfo?: DebugContext): any {
return this._delegate.selectRootElement(selectorOrNode, debugInfo); return this._delegate.selectRootElement(selectorOrNode, debugInfo);
} }
parentNode(node: any): any { return {parentOf: node}; } parentNode(node: any): any { return node.parentNode; }
nextSibling(node: any): any { return {nextSiblingOf: node}; } nextSibling(node: any): any { return node.nextSibling; }
setAttribute(el: any, name: string, value: string): void { setAttribute(el: any, name: string, value: string): void {
this._delegate.setElementAttribute(el, name, value); this._delegate.setElementAttribute(el, name, value);
} }
removeAttribute(el: any, name: string): void { removeAttribute(el: any, name: string): void {
this._delegate.setElementAttribute(el, name, null); this._delegate.setElementAttribute(el, name, null);
} }
setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void {
this._delegate.setBindingDebugInfo(el, propertyName, propertyValue);
}
removeBindingDebugInfo(el: any, propertyName: string): void {
this._delegate.setBindingDebugInfo(el, propertyName, null);
}
addClass(el: any, name: string): void { this._delegate.setElementClass(el, name, true); } addClass(el: any, name: string): void { this._delegate.setElementClass(el, name, true); }
removeClass(el: any, name: string): void { this._delegate.setElementClass(el, name, false); } removeClass(el: any, name: string): void { this._delegate.setElementClass(el, name, false); }
setStyle(el: any, style: string, value: any): void { setStyle(el: any, style: string, value: any): void {

View File

@ -19,8 +19,8 @@ import {resolveDep} from './provider';
import {getQueryValue} from './query'; import {getQueryValue} from './query';
import {createInjector} from './refs'; import {createInjector} from './refs';
import {DirectDomRenderer, LegacyRendererAdapter} from './renderer'; import {DirectDomRenderer, LegacyRendererAdapter} from './renderer';
import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types'; import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types';
import {checkBinding, findElementDef, isComponentView, parentDiIndex, renderNode, resolveViewDefinition, rootRenderNodes} from './util'; import {checkBinding, isComponentView, queryIdIsReference, renderNode, resolveViewDefinition, rootRenderNodes, viewParentDiIndex} from './util';
import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view'; import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view';
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach'; import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
@ -98,7 +98,7 @@ function debugCreateRootView(
const debugRoot: RootData = { const debugRoot: RootData = {
injector: root.injector, injector: root.injector,
projectableNodes: root.projectableNodes, projectableNodes: root.projectableNodes,
element: root.element, selectorOrNode: root.selectorOrNode,
renderer: new DebugRenderer(root.renderer), renderer: new DebugRenderer(root.renderer),
sanitizer: root.sanitizer sanitizer: root.sanitizer
}; };
@ -114,7 +114,7 @@ function createRootData(
new DirectDomRenderer(); new DirectDomRenderer();
const rootElement = const rootElement =
rootSelectorOrNode ? renderer.selectRootElement(rootSelectorOrNode) : undefined; rootSelectorOrNode ? renderer.selectRootElement(rootSelectorOrNode) : undefined;
return {injector, projectableNodes, element: rootElement, sanitizer, renderer}; return {injector, projectableNodes, selectorOrNode: rootSelectorOrNode, sanitizer, renderer};
} }
function debugCreateEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData { function debugCreateEmbeddedView(parent: ViewData, anchorDef: NodeDef, context?: any): ViewData {
@ -184,13 +184,16 @@ function debugUpdateView(check: NodeCheckFn, view: ViewData) {
} }
function setBindingDebugInfo(renderer: RendererV2, renderNode: any, propName: string, value: any) { function setBindingDebugInfo(renderer: RendererV2, renderNode: any, propName: string, value: any) {
const renderName = `ng-reflect-${camelCaseToDashCase(propName)}`;
if (value) {
try { try {
renderer.setAttribute( renderer.setBindingDebugInfo(renderNode, renderName, value.toString());
renderNode, `ng-reflect-${camelCaseToDashCase(propName)}`, value ? value.toString() : null);
} catch (e) { } catch (e) {
renderer.setAttribute( renderer.setBindingDebugInfo(
renderNode, `ng-reflect-${camelCaseToDashCase(propName)}`, renderNode, renderName, '[ERROR] Exception while trying to serialize the value');
'[ERROR] Exception while trying to serialize the value'); }
} else {
renderer.removeBindingDebugInfo(renderNode, renderName);
} }
} }
@ -240,6 +243,12 @@ class DebugRenderer implements RendererV2 {
return this._delegate.setAttribute(el, name, value); return this._delegate.setAttribute(el, name, value);
} }
removeAttribute(el: any, name: string): void { return this._delegate.removeAttribute(el, name); } removeAttribute(el: any, name: string): void { return this._delegate.removeAttribute(el, name); }
setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void {
this._delegate.setBindingDebugInfo(el, propertyName, propertyValue);
}
removeBindingDebugInfo(el: any, propertyName: string): void {
this._delegate.removeBindingDebugInfo(el, propertyName);
}
addClass(el: any, name: string): void { return this._delegate.addClass(el, name); } addClass(el: any, name: string): void { return this._delegate.addClass(el, name); }
removeClass(el: any, name: string): void { return this._delegate.removeClass(el, name); } removeClass(el: any, name: string): void { return this._delegate.removeClass(el, name); }
setStyle(el: any, style: string, value: any): void { setStyle(el: any, style: string, value: any): void {
@ -258,27 +267,63 @@ class DebugRenderer implements RendererV2 {
class DebugContext_ implements DebugContext { class DebugContext_ implements DebugContext {
private nodeDef: NodeDef; private nodeDef: NodeDef;
private elView: ViewData;
private elDef: NodeDef; private elDef: NodeDef;
private compProviderIndex: number;
constructor(public view: ViewData, public nodeIndex: number) { constructor(public view: ViewData, public nodeIndex: number) {
if (nodeIndex == null) { if (nodeIndex == null) {
this.nodeIndex = nodeIndex = view.parentIndex; this.nodeIndex = 0;
this.view = view = view.parent;
} }
this.nodeDef = view.def.nodes[nodeIndex]; this.nodeDef = view.def.nodes[nodeIndex];
this.elDef = findElementDef(view, nodeIndex); let elIndex = nodeIndex;
let elView = view;
while (elIndex != null && view.def.nodes[elIndex].type !== NodeType.Element) {
elIndex = view.def.nodes[elIndex].parent;
}
if (elIndex == null) {
while (elIndex == null && elView) {
elIndex = viewParentDiIndex(elView);
elView = elView.parent;
}
}
this.elView = elView;
if (elView) {
this.elDef = elView.def.nodes[elIndex];
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
const childDef = this.elView.def.nodes[i];
if (childDef.flags & NodeFlags.HasComponent) {
this.compProviderIndex = i;
break;
}
i += childDef.childCount;
}
} else {
this.elDef = null;
}
}
get injector(): Injector { return createInjector(this.elView, this.elDef.index); }
get component(): any {
if (this.compProviderIndex != null) {
return asProviderData(this.elView, this.compProviderIndex).instance;
}
return this.view.component;
}
get context(): any {
if (this.compProviderIndex != null) {
return asProviderData(this.elView, this.compProviderIndex).instance;
}
return this.view.context;
} }
get injector(): Injector { return createInjector(this.view, this.elDef.index); }
get component(): any { return this.view.component; }
get providerTokens(): any[] { get providerTokens(): any[] {
const tokens: any[] = []; const tokens: any[] = [];
if (this.elDef) { if (this.elDef) {
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) { for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
const childDef = this.view.def.nodes[i]; const childDef = this.elView.def.nodes[i];
if (childDef.type === NodeType.Provider) { if (childDef.type === NodeType.Provider) {
tokens.push(childDef.provider.token); tokens.push(childDef.provider.token);
} else {
i += childDef.childCount;
} }
i += childDef.childCount;
} }
} }
return tokens; return tokens;
@ -286,20 +331,18 @@ class DebugContext_ implements DebugContext {
get references(): {[key: string]: any} { get references(): {[key: string]: any} {
const references: {[key: string]: any} = {}; const references: {[key: string]: any} = {};
if (this.elDef) { if (this.elDef) {
collectReferences(this.view, this.elDef, references); collectReferences(this.elView, this.elDef, references);
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) { for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
const childDef = this.view.def.nodes[i]; const childDef = this.elView.def.nodes[i];
if (childDef.type === NodeType.Provider) { if (childDef.type === NodeType.Provider) {
collectReferences(this.view, childDef, references); collectReferences(this.elView, childDef, references);
} else {
i += childDef.childCount;
} }
i += childDef.childCount;
} }
} }
return references; return references;
} }
get context(): any { return this.view.context; }
get source(): string { get source(): string {
if (this.nodeDef.type === NodeType.Text) { if (this.nodeDef.type === NodeType.Text) {
return this.nodeDef.text.source; return this.nodeDef.text.source;
@ -308,12 +351,15 @@ class DebugContext_ implements DebugContext {
} }
} }
get componentRenderElement() { get componentRenderElement() {
const elData = findHostElement(this.view); const view = this.compProviderIndex != null ?
asProviderData(this.elView, this.compProviderIndex).componentView :
this.view;
const elData = findHostElement(view);
return elData ? elData.renderElement : undefined; return elData ? elData.renderElement : undefined;
} }
get renderNode(): any { get renderNode(): any {
let nodeDef = this.nodeDef.type === NodeType.Text ? this.nodeDef : this.elDef; return this.nodeDef.type === NodeType.Text ? renderNode(this.view, this.nodeDef) :
return renderNode(this.view, nodeDef); renderNode(this.elView, this.elDef);
} }
} }
@ -330,7 +376,7 @@ function findHostElement(view: ViewData): ElementData {
function collectReferences(view: ViewData, nodeDef: NodeDef, references: {[key: string]: any}) { function collectReferences(view: ViewData, nodeDef: NodeDef, references: {[key: string]: any}) {
for (let queryId in nodeDef.matchedQueries) { for (let queryId in nodeDef.matchedQueries) {
if (queryId.startsWith('#')) { if (queryIdIsReference(queryId)) {
references[queryId.slice(1)] = getQueryValue(view, nodeDef, queryId); references[queryId.slice(1)] = getQueryValue(view, nodeDef, queryId);
} }
} }

View File

@ -72,8 +72,7 @@ export enum ArgumentType {
*/ */
export enum ViewFlags { export enum ViewFlags {
None = 0, None = 0,
DirectDom = 1 << 1, OnPush = 1 << 1
OnPush = 1 << 2
} }
export interface ComponentDefinition { export interface ComponentDefinition {
@ -225,7 +224,8 @@ export interface DepDef {
export enum DepFlags { export enum DepFlags {
None = 0, None = 0,
SkipSelf = 1 << 0, SkipSelf = 1 << 0,
Optional = 1 << 1 Optional = 1 << 1,
Value = 2 << 2
} }
export interface ProviderOutputDef { export interface ProviderOutputDef {
@ -407,7 +407,7 @@ export function asQueryList(view: ViewData, index: number): QueryList<any> {
export interface RootData { export interface RootData {
injector: Injector; injector: Injector;
projectableNodes: any[][]; projectableNodes: any[][];
element: any; selectorOrNode: any;
renderer: RendererV2; renderer: RendererV2;
sanitizer: Sanitizer; sanitizer: Sanitizer;
} }
@ -436,6 +436,14 @@ export interface RendererV2 {
* the caller can't rely on checking whether this is null or not. * the caller can't rely on checking whether this is null or not.
*/ */
nextSibling(node: any): any; nextSibling(node: any): any;
/**
* Used only in debug mode to serialize property changes to dom nodes as attributes.
*/
setBindingDebugInfo(el: any, propertyName: string, propertyValue: string): void;
/**
* Used only in debug mode to serialize property changes to dom nodes as attributes.
*/
removeBindingDebugInfo(el: any, propertyName: string): void;
setAttribute(el: any, name: string, value: string): void; setAttribute(el: any, name: string, value: string): void;
removeAttribute(el: any, name: string): void; removeAttribute(el: any, name: string): void;
addClass(el: any, name: string): void; addClass(el: any, name: string): void;

View File

@ -95,27 +95,14 @@ export function declaredViewContainer(view: ViewData): ElementData {
* for embedded views, this is the index of the parent node * for embedded views, this is the index of the parent node
* that contains the view container. * that contains the view container.
*/ */
export function parentDiIndex(view: ViewData): number { export function viewParentDiIndex(view: ViewData): number {
if (view.parent) { if (view.parent && view.context !== view.component) {
const parentNodeDef = view.def.nodes[view.parentIndex]; const parentNodeDef = view.parent.def.nodes[view.parentIndex];
return parentNodeDef.element && parentNodeDef.element.template ? parentNodeDef.parent : return parentNodeDef.parent;
parentNodeDef.index;
} }
return view.parentIndex; return view.parentIndex;
} }
export function findElementDef(view: ViewData, nodeIndex: number): NodeDef {
const viewDef = view.def;
let nodeDef = viewDef.nodes[nodeIndex];
while (nodeDef) {
if (nodeDef.type === NodeType.Element) {
return nodeDef;
}
nodeDef = nodeDef.parent != null ? viewDef.nodes[nodeDef.parent] : undefined;
}
return undefined;
}
export function renderNode(view: ViewData, def: NodeDef): any { export function renderNode(view: ViewData, def: NodeDef): any {
switch (def.type) { switch (def.type) {
case NodeType.Element: case NodeType.Element:
@ -125,6 +112,27 @@ export function renderNode(view: ViewData, def: NodeDef): any {
} }
} }
export function nodeValue(view: ViewData, index: number): any {
const def = view.def.nodes[index];
switch (def.type) {
case NodeType.Element:
return asElementData(view, def.index).renderElement;
case NodeType.Text:
return asTextData(view, def.index).renderText;
case NodeType.Provider:
return asProviderData(view, def.index).instance;
}
return undefined;
}
export function queryIdIsReference(queryId: string): boolean {
return queryId.startsWith('#');
}
export function elementEventFullName(target: string, name: string): string {
return target ? `${target}:${name}` : name;
}
export function isComponentView(view: ViewData): boolean { export function isComponentView(view: ViewData): boolean {
return view.component === view.context && !!view.parent; return view.component === view.context && !!view.parent;
} }

View File

@ -7,6 +7,7 @@
*/ */
import {ViewEncapsulation} from '../metadata/view'; import {ViewEncapsulation} from '../metadata/view';
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element'; import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
import {expressionChangedAfterItHasBeenCheckedError} from './errors'; import {expressionChangedAfterItHasBeenCheckedError} from './errors';
import {appendNgContent} from './ng_content'; import {appendNgContent} from './ng_content';
@ -15,7 +16,7 @@ import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline,
import {checkAndUpdateQuery, createQuery, queryDef} from './query'; import {checkAndUpdateQuery, createQuery, queryDef} from './query';
import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text'; import {checkAndUpdateTextDynamic, checkAndUpdateTextInline, createText} from './text';
import {ArgumentType, ComponentDefinition, ElementDef, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, RootData, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList} from './types'; import {ArgumentType, ComponentDefinition, ElementDef, NodeData, NodeDef, NodeFlags, NodeType, ProviderData, ProviderDef, RootData, Services, TextDef, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewState, ViewUpdateFn, asElementData, asProviderData, asPureExpressionData, asQueryList} from './types';
import {checkBindingNoChanges, isComponentView, resolveViewDefinition} from './util'; import {checkBindingNoChanges, isComponentView, queryIdIsReference, resolveViewDefinition} from './util';
const NOOP = (): any => undefined; const NOOP = (): any => undefined;
@ -41,7 +42,7 @@ export function viewDef(
const newParent = nodes[currentParent.parent]; const newParent = nodes[currentParent.parent];
if (newParent) { if (newParent) {
newParent.childFlags |= currentParent.childFlags; newParent.childFlags |= currentParent.childFlags;
copyInto(currentParent.childMatchedQueries, newParent.childMatchedQueries); copyQueryMatchesInto(currentParent.childMatchedQueries, newParent.childMatchedQueries);
} }
currentParent = newParent; currentParent = newParent;
} }
@ -64,17 +65,18 @@ export function viewDef(
} }
nodes[i] = node; nodes[i] = node;
reverseChildNodes[reverseChildIndex] = node; reverseChildNodes[reverseChildIndex] = node;
validateNode(currentParent, node); validateNode(currentParent, node, nodesWithoutIndices.length);
viewNodeFlags |= node.flags; viewNodeFlags |= node.flags;
copyInto(node.matchedQueries, viewMatchedQueries); copyQueryMatchesInto(node.matchedQueries, viewMatchedQueries);
viewBindingCount += node.bindings.length; viewBindingCount += node.bindings.length;
viewDisposableCount += node.disposableCount; viewDisposableCount += node.disposableCount;
if (currentParent) { if (currentParent) {
currentParent.childFlags |= node.flags; currentParent.childFlags |= node.flags;
copyInto(node.matchedQueries, currentParent.childMatchedQueries); copyQueryMatchesInto(node.matchedQueries, currentParent.childMatchedQueries);
if (node.element && node.element.template) { if (node.element && node.element.template) {
copyInto(node.element.template.nodeMatchedQueries, currentParent.childMatchedQueries); copyQueryMatchesInto(
node.element.template.nodeMatchedQueries, currentParent.childMatchedQueries);
} }
} }
@ -96,7 +98,7 @@ export function viewDef(
const newParent = nodes[currentParent.parent]; const newParent = nodes[currentParent.parent];
if (newParent) { if (newParent) {
newParent.childFlags |= currentParent.childFlags; newParent.childFlags |= currentParent.childFlags;
copyInto(currentParent.childMatchedQueries, newParent.childMatchedQueries); copyQueryMatchesInto(currentParent.childMatchedQueries, newParent.childMatchedQueries);
} }
currentParent = newParent; currentParent = newParent;
} }
@ -114,9 +116,12 @@ export function viewDef(
}; };
} }
function copyInto(source: any, target: any) { function copyQueryMatchesInto(
source: {[queryId: string]: any}, target: {[queryId: string]: boolean}) {
for (let prop in source) { for (let prop in source) {
target[prop] = source[prop]; if (!queryIdIsReference(prop)) {
target[prop] = true;
}
} }
} }
@ -159,7 +164,7 @@ function calculateReverseChildIndex(
return parentEndIndexInReverseChildOrder - lastChildOffsetRelativeToParentInDfsOrder; return parentEndIndexInReverseChildOrder - lastChildOffsetRelativeToParentInDfsOrder;
} }
function validateNode(parent: NodeDef, node: NodeDef) { function validateNode(parent: NodeDef, node: NodeDef, nodeCount: number) {
const template = node.element && node.element.template; const template = node.element && node.element.template;
if (template) { if (template) {
if (template.lastRootNode && template.lastRootNode.flags & NodeFlags.HasEmbeddedViews) { if (template.lastRootNode && template.lastRootNode.flags & NodeFlags.HasEmbeddedViews) {
@ -182,15 +187,13 @@ function validateNode(parent: NodeDef, node: NodeDef) {
} }
} }
if (node.childCount) { if (node.childCount) {
if (parent) { const parentEnd = parent ? parent.index + parent.childCount : nodeCount - 1;
const parentEnd = parent.index + parent.childCount;
if (node.index <= parentEnd && node.index + node.childCount > parentEnd) { if (node.index <= parentEnd && node.index + node.childCount > parentEnd) {
throw new Error( throw new Error(
`Illegal State: childCount of node leads outside of parent, at index ${node.index}!`); `Illegal State: childCount of node leads outside of parent, at index ${node.index}!`);
} }
} }
} }
}
function cloneAndModifyNode(nodeDef: NodeDef, values: { function cloneAndModifyNode(nodeDef: NodeDef, values: {
index: number, index: number,

View File

@ -7,6 +7,7 @@
*/ */
import {CommonModule} from '@angular/common'; import {CommonModule} from '@angular/common';
import {USE_VIEW_ENGINE} from '@angular/compiler/src/config';
import {ComponentFactory, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, OnDestroy, ReflectiveInjector, SkipSelf} from '@angular/core'; import {ComponentFactory, Host, Inject, Injectable, InjectionToken, Injector, NO_ERRORS_SCHEMA, NgModule, OnDestroy, ReflectiveInjector, SkipSelf} from '@angular/core';
import {ChangeDetectionStrategy, ChangeDetectorRef, PipeTransform} from '@angular/core/src/change_detection/change_detection'; import {ChangeDetectionStrategy, ChangeDetectorRef, PipeTransform} from '@angular/core/src/change_detection/change_detection';
import {getDebugContext} from '@angular/core/src/errors'; import {getDebugContext} from '@angular/core/src/errors';
@ -29,12 +30,21 @@ import {isBlank, isPresent, stringify} from '../../src/facade/lang';
const ANCHOR_ELEMENT = new InjectionToken('AnchorElement'); const ANCHOR_ELEMENT = new InjectionToken('AnchorElement');
export function main() { export function main() {
describe('jit', () => { declareTests({useJit: true}); }); describe('jit', () => { declareTests({useJit: true, viewEngine: false}); });
describe('no jit', () => { declareTests({useJit: false}); }); describe('no jit', () => { declareTests({useJit: false, viewEngine: false}); });
describe('view engine', () => {
beforeEach(() => {
TestBed.configureCompiler(
{useJit: true, providers: [{provide: USE_VIEW_ENGINE, useValue: true}]});
});
declareTests({useJit: true, viewEngine: true});
});
} }
function declareTests({useJit}: {useJit: boolean}) { function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolean}) {
describe('integration tests', function() { describe('integration tests', function() {
beforeEach(() => { TestBed.configureCompiler({useJit: useJit}); }); beforeEach(() => { TestBed.configureCompiler({useJit: useJit}); });
@ -255,7 +265,7 @@ function declareTests({useJit}: {useJit: boolean}) {
}); });
describe('pipes', () => { describe('pipes', () => {
it('should support pipes in bindings', () => { viewEngine || it('should support pipes in bindings', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir, DoublePipe]}); TestBed.configureTestingModule({declarations: [MyComp, MyDir, DoublePipe]});
const template = '<div my-dir #dir="mydir" [elprop]="ctxProp | double"></div>'; const template = '<div my-dir #dir="mydir" [elprop]="ctxProp | double"></div>';
TestBed.overrideComponent(MyComp, {set: {template}}); TestBed.overrideComponent(MyComp, {set: {template}});
@ -520,7 +530,7 @@ function declareTests({useJit}: {useJit: boolean}) {
const fixture = TestBed.createComponent(MyComp); const fixture = TestBed.createComponent(MyComp);
const value = fixture.debugElement.childNodes[0].references['alice']; const value = fixture.debugElement.childNodes[0].references['alice'];
expect(value).toBeAnInstanceOf(TemplateRef_); expect(value.createEmbeddedView).toBeTruthy();
}); });
it('should preserve case', () => { it('should preserve case', () => {
@ -535,7 +545,7 @@ function declareTests({useJit}: {useJit: boolean}) {
}); });
describe('variables', () => { describe('variables', () => {
it('should allow to use variables in a for loop', () => { viewEngine || it('should allow to use variables in a for loop', () => {
TestBed.configureTestingModule({declarations: [MyComp, ChildCompNoTemplate]}); TestBed.configureTestingModule({declarations: [MyComp, ChildCompNoTemplate]});
const template = const template =
'<template ngFor [ngForOf]="[1]" let-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</template>'; '<template ngFor [ngForOf]="[1]" let-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</template>';
@ -660,6 +670,7 @@ function declareTests({useJit}: {useJit: boolean}) {
}); });
if (getDOM().supportsDOMEvents()) { if (getDOM().supportsDOMEvents()) {
viewEngine ||
it('should be checked when an async pipe requests a check', fakeAsync(() => { it('should be checked when an async pipe requests a check', fakeAsync(() => {
TestBed.configureTestingModule( TestBed.configureTestingModule(
{declarations: [MyComp, PushCmpWithAsyncPipe], imports: [CommonModule]}); {declarations: [MyComp, PushCmpWithAsyncPipe], imports: [CommonModule]});
@ -669,7 +680,8 @@ function declareTests({useJit}: {useJit: boolean}) {
tick(); tick();
const cmp: PushCmpWithAsyncPipe = fixture.debugElement.children[0].references['cmp']; const cmp: PushCmpWithAsyncPipe =
fixture.debugElement.children[0].references['cmp'];
fixture.detectChanges(); fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(1); expect(cmp.numberOfChecks).toEqual(1);
@ -885,17 +897,19 @@ function declareTests({useJit}: {useJit: boolean}) {
expect(tc.nativeElement.id).toEqual('newId'); expect(tc.nativeElement.id).toEqual('newId');
}); });
viewEngine ||
it('should not use template variables for expressions in hostProperties', () => { it('should not use template variables for expressions in hostProperties', () => {
@Directive({selector: '[host-properties]', host: {'[id]': 'id', '[title]': 'unknownProp'}}) @Directive(
{selector: '[host-properties]', host: {'[id]': 'id', '[title]': 'unknownProp'}})
class DirectiveWithHostProps { class DirectiveWithHostProps {
id = 'one'; id = 'one';
} }
const fixture = const fixture =
TestBed.configureTestingModule({declarations: [MyComp, DirectiveWithHostProps]}) TestBed.configureTestingModule({declarations: [MyComp, DirectiveWithHostProps]})
.overrideComponent( .overrideComponent(MyComp, {
MyComp, set: {template: `<div *ngFor="let id of ['forId']" host-properties></div>`}
{set: {template: `<div *ngFor="let id of ['forId']" host-properties></div>`}}) })
.createComponent(MyComp); .createComponent(MyComp);
fixture.detectChanges(); fixture.detectChanges();
@ -916,7 +930,8 @@ function declareTests({useJit}: {useJit: boolean}) {
.toThrowError(/Host binding expression cannot contain pipes/); .toThrowError(/Host binding expression cannot contain pipes/);
}); });
it('should not use template variables for expressions in hostListeners', () => { // TODO: literal array
viewEngine || it('should not use template variables for expressions in hostListeners', () => {
@Directive({selector: '[host-listener]', host: {'(click)': 'doIt(id, unknownProp)'}}) @Directive({selector: '[host-listener]', host: {'(click)': 'doIt(id, unknownProp)'}})
class DirectiveWithHostListener { class DirectiveWithHostListener {
id = 'one'; id = 'one';
@ -1232,7 +1247,7 @@ function declareTests({useJit}: {useJit: boolean}) {
.toThrowError(`Directive ${stringify(SomeDirective)} has no selector, please add it!`); .toThrowError(`Directive ${stringify(SomeDirective)} has no selector, please add it!`);
}); });
it('should use a default element name for components without selectors', () => { viewEngine || it('should use a default element name for components without selectors', () => {
let noSelectorComponentFactory: ComponentFactory<SomeComponent>; let noSelectorComponentFactory: ComponentFactory<SomeComponent>;
@Component({template: '----'}) @Component({template: '----'})
@ -1264,7 +1279,7 @@ function declareTests({useJit}: {useJit: boolean}) {
}); });
}); });
describe('error handling', () => { viewEngine || describe('error handling', () => {
it('should report a meaningful error when a directive is missing annotation', () => { it('should report a meaningful error when a directive is missing annotation', () => {
TestBed.configureTestingModule({declarations: [MyComp, SomeDirectiveMissingAnnotation]}); TestBed.configureTestingModule({declarations: [MyComp, SomeDirectiveMissingAnnotation]});
@ -1721,7 +1736,7 @@ class MyService {
class SimpleImperativeViewComponent { class SimpleImperativeViewComponent {
done: any; done: any;
constructor(self: ElementRef, renderer: Renderer) { constructor(self: ElementRef) {
const hostElement = self.nativeElement; const hostElement = self.nativeElement;
getDOM().appendChild(hostElement, el('hello imp view')); getDOM().appendChild(hostElement, el('hello imp view'));
} }

View File

@ -63,7 +63,7 @@ export function main() {
it('should select root elements based on a selector', () => { it('should select root elements based on a selector', () => {
const view = createRootView( const view = createRootView(
compViewDef([ compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div'), elementDef(NodeFlags.None, null, null, 0, 'div'),
]), ]),
{}, [], 'root'); {}, [], 'root');
const rootNodes = rootRenderNodes(view); const rootNodes = rootRenderNodes(view);
@ -73,7 +73,7 @@ export function main() {
it('should select root elements based on a node', () => { it('should select root elements based on a node', () => {
const view = createRootView( const view = createRootView(
compViewDef([ compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div'), elementDef(NodeFlags.None, null, null, 0, 'div'),
]), ]),
{}, [], rootNode); {}, [], rootNode);
const rootNodes = rootRenderNodes(view); const rootNodes = rootRenderNodes(view);
@ -83,7 +83,7 @@ export function main() {
it('should set attributes on the root node', () => { it('should set attributes on the root node', () => {
const view = createRootView( const view = createRootView(
compViewDef([ compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}), elementDef(NodeFlags.None, null, null, 0, 'div', {'a': 'b'}),
]), ]),
{}, [], rootNode); {}, [], rootNode);
expect(rootNode.getAttribute('a')).toBe('b'); expect(rootNode.getAttribute('a')).toBe('b');
@ -93,7 +93,7 @@ export function main() {
rootNode.appendChild(document.createElement('div')); rootNode.appendChild(document.createElement('div'));
const view = createRootView( const view = createRootView(
compViewDef([ compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}), elementDef(NodeFlags.None, null, null, 0, 'div', {'a': 'b'}),
]), ]),
{}, [], rootNode); {}, [], rootNode);
expect(rootNode.childNodes.length).toBe(0); expect(rootNode.childNodes.length).toBe(0);

View File

@ -262,7 +262,7 @@ export function main() {
const handleEventArgs = handleEventSpy.calls.mostRecent().args; const handleEventArgs = handleEventSpy.calls.mostRecent().args;
expect(handleEventArgs[0]).toBe(view); expect(handleEventArgs[0]).toBe(view);
expect(handleEventArgs[1]).toBe(0); expect(handleEventArgs[1]).toBe(0);
expect(handleEventArgs[2]).toBe('windowClick'); expect(handleEventArgs[2]).toBe('window:windowClick');
expect(handleEventArgs[3]).toBeTruthy(); expect(handleEventArgs[3]).toBeTruthy();
Services.destroyView(view); Services.destroyView(view);
@ -288,7 +288,7 @@ export function main() {
const handleEventArgs = handleEventSpy.calls.mostRecent().args; const handleEventArgs = handleEventSpy.calls.mostRecent().args;
expect(handleEventArgs[0]).toBe(view); expect(handleEventArgs[0]).toBe(view);
expect(handleEventArgs[1]).toBe(0); expect(handleEventArgs[1]).toBe(0);
expect(handleEventArgs[2]).toBe('documentClick'); expect(handleEventArgs[2]).toBe('document:documentClick');
expect(handleEventArgs[3]).toBeTruthy(); expect(handleEventArgs[3]).toBeTruthy();
Services.destroyView(view); Services.destroyView(view);

View File

@ -7,7 +7,7 @@
*/ */
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core'; import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, ViewEncapsulation} from '@angular/core';
import {ArgumentType, BindingType, NodeCheckFn, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, moveEmbeddedView, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index'; import {ArgumentType, BindingType, NodeCheckFn, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, moveEmbeddedView, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing'; import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -21,8 +21,8 @@ export function main() {
return viewDef(viewFlags, nodes, update, handleEvent); return viewDef(viewFlags, nodes, update, handleEvent);
} }
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition { function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
return viewDef(ViewFlags.None, nodes, update); return () => viewDef(ViewFlags.None, nodes, update);
} }
function createAndGetRootNodes( function createAndGetRootNodes(
@ -38,7 +38,7 @@ export function main() {
const {view: parentView, rootNodes} = createAndGetRootNodes( const {view: parentView, rootNodes} = createAndGetRootNodes(
compViewDef([ compViewDef([
elementDef(NodeFlags.None, null, null, 2, 'div'), elementDef(NodeFlags.None, null, null, 1, 'div'),
anchorDef( anchorDef(
NodeFlags.HasEmbeddedViews, null, null, 0, NodeFlags.HasEmbeddedViews, null, null, 0,
embeddedViewDef([elementDef(NodeFlags.None, null, null, 0, 'span')])), embeddedViewDef([elementDef(NodeFlags.None, null, null, 0, 'span')])),

View File

@ -7,7 +7,7 @@
*/ */
import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core'; import {Injector, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core';
import {DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, ngContentDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index'; import {DebugContext, NodeDef, NodeFlags, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, asTextData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, ngContentDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing'; import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -21,8 +21,8 @@ export function main() {
return viewDef(viewFlags, nodes, update, handleEvent); return viewDef(viewFlags, nodes, update, handleEvent);
} }
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition { function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
return viewDef(ViewFlags.None, nodes, update); return () => viewDef(ViewFlags.None, nodes, update);
} }
function hostElDef(contentNodes: NodeDef[], viewNodes: NodeDef[]): NodeDef[] { function hostElDef(contentNodes: NodeDef[], viewNodes: NodeDef[]): NodeDef[] {

View File

@ -8,7 +8,7 @@
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core'; import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectorRef, DoCheck, ElementRef, EventEmitter, Injector, OnChanges, OnDestroy, OnInit, RenderComponentType, Renderer, RootRenderer, Sanitizer, SecurityContext, SimpleChange, TemplateRef, ViewContainerRef, ViewEncapsulation, WrappedValue, getDebugNode} from '@angular/core';
import {getDebugContext} from '@angular/core/src/errors'; import {getDebugContext} from '@angular/core/src/errors';
import {ArgumentType, BindingType, DebugContext, DepFlags, NodeDef, NodeFlags, ProviderType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, directiveDef, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index'; import {ArgumentType, BindingType, DebugContext, DepFlags, NodeDef, NodeFlags, ProviderType, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, directiveDef, elementDef, providerDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {TestBed, inject, withModule} from '@angular/core/testing'; import {TestBed, inject, withModule} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -23,8 +23,8 @@ export function main() {
viewFlags, nodes, update, handleEvent, 'someCompId', ViewEncapsulation.None, []); viewFlags, nodes, update, handleEvent, 'someCompId', ViewEncapsulation.None, []);
} }
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition { function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
return viewDef(ViewFlags.None, nodes, update); return () => viewDef(ViewFlags.None, nodes, update);
} }
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} { function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {

View File

@ -8,7 +8,7 @@
import {ElementRef, Injector, QueryList, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core'; import {ElementRef, Injector, QueryList, RenderComponentType, RootRenderer, Sanitizer, SecurityContext, TemplateRef, ViewContainerRef, ViewEncapsulation, getDebugNode} from '@angular/core';
import {getDebugContext} from '@angular/core/src/errors'; import {getDebugContext} from '@angular/core/src/errors';
import {BindingType, DebugContext, NodeDef, NodeFlags, QueryBindingType, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, queryDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index'; import {BindingType, DebugContext, NodeDef, NodeFlags, QueryBindingType, QueryValueType, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewFlags, ViewHandleEventFn, ViewUpdateFn, anchorDef, asElementData, asProviderData, attachEmbeddedView, detachEmbeddedView, directiveDef, elementDef, queryDef, rootRenderNodes, textDef, viewDef} from '@angular/core/src/view/index';
import {inject} from '@angular/core/testing'; import {inject} from '@angular/core/testing';
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -22,8 +22,8 @@ export function main() {
return viewDef(viewFlags, nodes, update, handleEvent); return viewDef(viewFlags, nodes, update, handleEvent);
} }
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition { function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
return viewDef(ViewFlags.None, nodes, update); return () => viewDef(ViewFlags.None, nodes, update);
} }
function createAndGetRootNodes( function createAndGetRootNodes(
@ -118,7 +118,7 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 3, 'div'), elementDef(NodeFlags.None, null, null, 3, 'div'),
...viewQueryProviders(compViewDef([ ...viewQueryProviders(compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'span'), elementDef(NodeFlags.None, null, null, 0, 'span'),
])), ])),
aServiceProvider(), aServiceProvider(),
])); ]));
@ -134,11 +134,7 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 5, 'div'), elementDef(NodeFlags.None, null, null, 5, 'div'),
...contentQueryProviders(), ...contentQueryProviders(),
anchorDef( anchorDef(NodeFlags.HasEmbeddedViews, null, null, 2, embeddedViewDef([
NodeFlags.HasEmbeddedViews, null, null, 2,
viewDef(
ViewFlags.None,
[
elementDef(NodeFlags.None, null, null, 1, 'div'), elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(), aServiceProvider(),
])), ])),
@ -164,11 +160,7 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 3, 'div'), elementDef(NodeFlags.None, null, null, 3, 'div'),
...contentQueryProviders(), ...contentQueryProviders(),
anchorDef( anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, embeddedViewDef([
NodeFlags.HasEmbeddedViews, null, null, 0,
viewDef(
ViewFlags.None,
[
elementDef(NodeFlags.None, null, null, 1, 'div'), elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(), aServiceProvider(),
])), ])),
@ -197,11 +189,7 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 3, 'div'), elementDef(NodeFlags.None, null, null, 3, 'div'),
...contentQueryProviders(), ...contentQueryProviders(),
anchorDef( anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, embeddedViewDef([
NodeFlags.HasEmbeddedViews, null, null, 0,
viewDef(
ViewFlags.None,
[
elementDef(NodeFlags.None, null, null, 1, 'div'), elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(), aServiceProvider(),
])), ])),
@ -228,11 +216,7 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 2, 'div'), elementDef(NodeFlags.None, null, null, 2, 'div'),
...viewQueryProviders(compViewDef([ ...viewQueryProviders(compViewDef([
anchorDef( anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, embeddedViewDef([
NodeFlags.HasEmbeddedViews, null, null, 0,
viewDef(
ViewFlags.None,
[
elementDef(NodeFlags.None, null, null, 1, 'div'), elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(), aServiceProvider(),
])), ])),
@ -328,7 +312,7 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
anchorDef( anchorDef(
NodeFlags.None, [['query1', QueryValueType.TemplateRef]], null, 2, NodeFlags.None, [['query1', QueryValueType.TemplateRef]], null, 2,
viewDef(ViewFlags.None, [anchorDef(NodeFlags.None, null, null, 0)])), embeddedViewDef([anchorDef(NodeFlags.None, null, null, 0)])),
directiveDef(NodeFlags.None, null, 1, QueryService, []), directiveDef(NodeFlags.None, null, 1, QueryService, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}), queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
])); ]));
@ -360,13 +344,9 @@ export function main() {
describe('general binding behavior', () => { describe('general binding behavior', () => {
it('should checkNoChanges', () => { it('should checkNoChanges', () => {
const {view} = createAndGetRootNodes(compViewDef([ const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 4, 'div'), elementDef(NodeFlags.None, null, null, 3, 'div'),
...contentQueryProviders(), ...contentQueryProviders(),
anchorDef( anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, embeddedViewDef([
NodeFlags.HasEmbeddedViews, null, null, 1,
viewDef(
ViewFlags.None,
[
elementDef(NodeFlags.None, null, null, 1, 'div'), elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(), aServiceProvider(),
])), ])),

View File

@ -228,10 +228,10 @@ export function main() {
elementDef(NodeFlags.None, null, null, 1, 'span'), elementDef(NodeFlags.None, null, null, 1, 'span'),
anchorDef( anchorDef(
NodeFlags.None, null, null, 0, NodeFlags.None, null, null, 0,
viewDef( () => viewDef(
ViewFlags.None, ViewFlags.None,
[ [
elementDef(NodeFlags.None, [['q1', QueryValueType.Provider]], null, 1, 'span'), elementDef(NodeFlags.None, [['q1', QueryValueType.Provider]], null, 0, 'span'),
])) ]))
]); ]);

View File

@ -30,31 +30,35 @@ function TreeComponent_Host(): ViewDefinition {
]); ]);
} }
function TreeComponent_1() {
return viewDef(
viewFlags,
[
elementDef(NodeFlags.None, null, null, 1, 'tree'),
directiveDef(
NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null, TreeComponent_0),
],
(check, view) => {
const cmp = view.component;
check(view, 1, ArgumentType.Inline, cmp.data.left);
});
}
function TreeComponent_2() {
return viewDef(
viewFlags,
[
elementDef(NodeFlags.None, null, null, 1, 'tree'),
directiveDef(
NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null, TreeComponent_0),
],
(check, view) => {
const cmp = view.component;
check(view, 1, ArgumentType.Inline, cmp.data.left);
});
}
function TreeComponent_0(): ViewDefinition { function TreeComponent_0(): ViewDefinition {
const TreeComponent_1: ViewDefinition = viewDef(
viewFlags,
[
elementDef(NodeFlags.None, null, null, 1, 'tree'),
directiveDef(
NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null, TreeComponent_0),
],
(check, view) => {
const cmp = view.component;
check(view, 1, ArgumentType.Inline, cmp.data.left);
});
const TreeComponent_2: ViewDefinition = viewDef(
viewFlags,
[
elementDef(NodeFlags.None, null, null, 1, 'tree'),
directiveDef(
NodeFlags.None, null, 0, TreeComponent, [], {data: [0, 'data']}, null, TreeComponent_0),
],
(check, view) => {
const cmp = view.component;
check(view, 1, ArgumentType.Inline, cmp.data.left);
});
return viewDef( return viewDef(
viewFlags, viewFlags,
[ [