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 {
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
*/
import {MissingTranslationStrategy, ViewEncapsulation, isDevMode} from '@angular/core';
import {InjectionToken, MissingTranslationStrategy, ViewEncapsulation, isDevMode} from '@angular/core';
import {CompileIdentifierMetadata} from './compile_metadata';
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 {
public renderTypes: RenderTypes;
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,
`sub_${eventIdx}`);
const trueStmts = evalResult.stmts;
if (evalResult.preventDefault) {
trueStmts.push(resultVar.set(evalResult.preventDefault.and(resultVar)).toStmt());
if (evalResult.allowDefault) {
trueStmts.push(resultVar.set(evalResult.allowDefault.and(resultVar)).toStmt());
}
// TODO(tbosch): convert this into a `switch` once our OutputAst supports it.
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 {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 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 ANIMATION_STYLE_UTIL_ASSET_URL = assetUrl('core', 'animation/animation_style_util');
@ -363,6 +364,47 @@ export class Identifiers {
};
static 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 {

View File

@ -6,12 +6,12 @@
* 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 {AnimationParser} from '../animation/animation_parser';
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 {stringify} from '../facade/lang';
import {CompilerInjectable} from '../injectable';
@ -50,7 +50,8 @@ export class JitCompiler implements Compiler {
private _templateParser: TemplateParser, private _styleCompiler: StyleCompiler,
private _viewCompiler: ViewCompiler, private _ngModuleCompiler: NgModuleCompiler,
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; }
@ -244,6 +245,9 @@ export class JitCompiler implements Compiler {
private _compileDirectiveWrapper(
dirMeta: CompileDirectiveMetadata, moduleMeta: CompileNgModuleMetadata): void {
if (this._useViewEngine) {
return;
}
const compileResult = this._directiveWrapperCompiler.compile(dirMeta);
const statements = compileResult.statements;
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 {AnimationParser} from '../animation/animation_parser';
import {CompilerConfig} from '../config';
import {CompilerConfig, USE_VIEW_ENGINE} from '../config';
import {DirectiveNormalizer} from '../directive_normalizer';
import {DirectiveResolver} from '../directive_resolver';
import {DirectiveWrapperCompiler} from '../directive_wrapper_compiler';
@ -31,6 +31,7 @@ import {SummaryResolver} from '../summary_resolver';
import {TemplateParser} from '../template_parser/template_parser';
import {DEFAULT_PACKAGE_URL_PROVIDER, UrlResolver} from '../url_resolver';
import {ViewCompiler} from '../view_compiler/view_compiler';
import {ViewCompilerNext} from '../view_compiler_next/view_compiler';
import {JitCompiler} from './compiler';
@ -42,6 +43,11 @@ const _NO_RESOURCE_LOADER: ResourceLoader = {
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
* template compilation.
@ -81,7 +87,12 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
CompileMetadataResolver,
DEFAULT_PACKAGE_URL_PROVIDER,
StyleCompiler,
ViewCompiler,
{provide: USE_VIEW_ENGINE, useValue: false},
{
provide: ViewCompiler,
useFactory: viewCompilerFactory,
deps: [USE_VIEW_ENGINE, CompilerConfig, ElementSchemaRegistry]
},
NgModuleCompiler,
DirectiveWrapperCompiler,
{provide: CompilerConfig, useValue: new CompilerConfig()},

View File

@ -12,6 +12,7 @@ import {StaticSymbol, StaticSymbolCache} from './aot/static_symbol';
import {ngfactoryFilePath} from './aot/util';
import {assertArrayOfStrings, assertInterpolationSymbols} from './assertions';
import * as cpl from './compile_metadata';
import {USE_VIEW_ENGINE} from './config';
import {DirectiveNormalizer} from './directive_normalizer';
import {DirectiveResolver} from './directive_resolver';
import {stringify} from './facade/lang';
@ -20,7 +21,7 @@ import {CompilerInjectable} from './injectable';
import {hasLifecycleHook} from './lifecycle_reflector';
import {NgModuleResolver} from './ng_module_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 {SummaryResolver} from './summary_resolver';
import {getUrlScheme} from './url_resolver';
@ -53,7 +54,8 @@ export class CompileMetadataResolver {
private _directiveNormalizer: DirectiveNormalizer,
@Optional() private _staticSymbolCache: StaticSymbolCache,
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>) {
const dirMeta = this._directiveCache.get(type);
@ -135,7 +137,11 @@ export class CompileMetadataResolver {
ngfactoryFilePath(dirType.filePath), cpl.componentFactoryName(dirType));
} else {
const hostView = this.getHostComponentViewClass(dirType);
return new ComponentFactory(selector, <any>hostView, dirType);
if (this._useViewEngine) {
return viewEngine.createComponentFactory(selector, dirType, <any>hostView);
} else {
return new ComponentFactory(selector, <any>hostView, dirType);
}
}
}

View File

@ -29,6 +29,7 @@ export const registerModuleFactory: typeof r.registerModuleFactory = r.registerM
export type ViewType = typeof r._ViewType;
export const ViewType: typeof r.ViewType = r.ViewType;
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 StaticNodeDebugInfo: typeof r.StaticNodeDebugInfo = r.StaticNodeDebugInfo;
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 {isBlank, isPresent} from './facade/lang';
import {Identifiers, resolveIdentifier} from './identifiers';
import {Identifiers, createIdentifierToken, resolveIdentifier} from './identifiers';
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 {
constructor(message: string, span: ParseSourceSpan) { super(span, message); }
}
export interface QueryWithId {
meta: CompileQueryMetadata;
id: QueryId;
}
export class ProviderViewContext {
/**
* @internal
*/
viewQueries: Map<any, CompileQueryMetadata[]>;
viewQueries: Map<any, QueryWithId[]>;
/**
* @internal
*/
@ -40,13 +45,14 @@ export class ProviderViewContext {
}
export class ProviderElementContext {
private _contentQueries: Map<any, CompileQueryMetadata[]>;
private _contentQueries: Map<any, QueryWithId[]>;
private _transformedProviders = new Map<any, ProviderAst>();
private _seenProviders = new Map<any, boolean>();
private _allProviders: Map<any, ProviderAst>;
private _attrs: {[key: string]: string};
private _hasViewContainer: boolean = false;
private _queriedTokens = new Map<any, QueryMatch[]>();
constructor(
public viewContext: ProviderViewContext, private _parent: ProviderElementContext,
@ -57,19 +63,21 @@ export class ProviderElementContext {
const directivesMeta = _directiveAsts.map(directiveAst => directiveAst.directive);
this._allProviders =
_resolveProvidersFromDirectives(directivesMeta, _sourceSpan, viewContext.errors);
this._contentQueries = _getContentQueries(directivesMeta);
const queriedTokens = new Map<any, boolean>();
this._contentQueries = _getContentQueries(this.depth, directivesMeta);
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); });
if (isPresent(queriedTokens.get(resolveIdentifier(Identifiers.ViewContainerRef)))) {
refs.forEach((refAst) => {
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;
}
// create the providers that we know are eager first
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) {
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[] {
return Array.from(this._transformedProviders.values());
}
@ -98,24 +116,36 @@ export class ProviderElementContext {
get transformedHasViewContainer(): boolean { return this._hasViewContainer; }
private _addQueryReadsTo(token: CompileTokenMetadata, queryReadTokens: Map<any, boolean>) {
get queryMatches(): QueryMatch[] {
const allMatches: QueryMatch[] = [];
this._queriedTokens.forEach((matches: QueryMatch[]) => { allMatches.push(...matches); });
return allMatches;
}
private _addQueryReadsTo(
token: CompileTokenMetadata, defaultValue: CompileTokenMetadata,
queryReadTokens: Map<any, QueryMatch[]>) {
this._getQueriesFor(token).forEach((query) => {
const queryReadToken = query.read || token;
if (isBlank(queryReadTokens.get(tokenReference(queryReadToken)))) {
queryReadTokens.set(tokenReference(queryReadToken), true);
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[] {
const result: CompileQueryMetadata[] = [];
private _getQueriesFor(token: CompileTokenMetadata): QueryWithId[] {
const result: QueryWithId[] = [];
let currentEl: ProviderElementContext = this;
let distance = 0;
let queries: CompileQueryMetadata[];
let queries: QueryWithId[];
while (currentEl !== null) {
queries = currentEl._contentQueries.get(tokenReference(token));
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) {
distance++;
@ -452,27 +482,32 @@ function _resolveProviders(
}
function _getViewQueries(component: CompileDirectiveMetadata): Map<any, CompileQueryMetadata[]> {
const viewQueries = new Map<any, CompileQueryMetadata[]>();
function _getViewQueries(component: CompileDirectiveMetadata): Map<any, QueryWithId[]> {
const viewQueries = new Map<any, QueryWithId[]>();
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;
}
function _getContentQueries(directives: CompileDirectiveSummary[]):
Map<any, CompileQueryMetadata[]> {
const contentQueries = new Map<any, CompileQueryMetadata[]>();
directives.forEach(directive => {
function _getContentQueries(
elementDepth: number, directives: CompileDirectiveSummary[]): Map<any, QueryWithId[]> {
const contentQueries = new Map<any, QueryWithId[]>();
directives.forEach((directive, directiveIndex) => {
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;
}
function _addQueryToTokenMap(map: Map<any, CompileQueryMetadata[]>, query: CompileQueryMetadata) {
query.selectors.forEach((token: CompileTokenMetadata) => {
function _addQueryToTokenMap(map: Map<any, QueryWithId[]>, query: QueryWithId) {
query.meta.selectors.forEach((token: CompileTokenMetadata) => {
let entry = map.get(tokenReference(token));
if (!entry) {
entry = [];

View File

@ -126,9 +126,9 @@ export class ElementAst implements TemplateAst {
public name: string, public attrs: AttrAst[], public inputs: BoundElementPropertyAst[],
public outputs: BoundEventAst[], public references: ReferenceAst[],
public directives: DirectiveAst[], public providers: ProviderAst[],
public hasViewContainer: boolean, public children: TemplateAst[],
public ngContentIndex: number, public sourceSpan: ParseSourceSpan,
public endSourceSpan: ParseSourceSpan) {}
public hasViewContainer: boolean, public queryMatches: QueryMatch[],
public children: TemplateAst[], public ngContentIndex: number,
public sourceSpan: ParseSourceSpan, public endSourceSpan: ParseSourceSpan) {}
visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitElement(this, context);
@ -143,8 +143,8 @@ export class EmbeddedTemplateAst implements TemplateAst {
public attrs: AttrAst[], public outputs: BoundEventAst[], public references: ReferenceAst[],
public variables: VariableAst[], public directives: DirectiveAst[],
public providers: ProviderAst[], public hasViewContainer: boolean,
public children: TemplateAst[], public ngContentIndex: number,
public sourceSpan: ParseSourceSpan) {}
public queryMatches: QueryMatch[], public children: TemplateAst[],
public ngContentIndex: number, public sourceSpan: ParseSourceSpan) {}
visit(visitor: TemplateAstVisitor, context: any): any {
return visitor.visitEmbeddedTemplate(this, context);
@ -241,6 +241,20 @@ export enum PropertyBindingType {
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.
*/

View File

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

View File

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

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

View File

@ -7,7 +7,7 @@
*/
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 {APP_ID_RANDOM_PROVIDER} from './application_tokens';
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 {ViewUtils} from './linker/view_utils';
import {NgModule} from './metadata';
import {initServicesIfNeeded} from './view/index';
export function _iterableDiffersFactory() {
return defaultIterableDiffers;
@ -29,6 +30,10 @@ export function _localeFactory(locale?: string): string {
return locale || 'en-US';
}
export function _initViewEngine() {
initServicesIfNeeded();
}
/**
* This module includes the providers of @angular/core that are needed
* to bootstrap components via `ApplicationRef`.
@ -51,6 +56,7 @@ export function _localeFactory(locale?: string): string {
useFactory: _localeFactory,
deps: [[new Inject(LOCALE_ID), new Optional(), new SkipSelf()]]
},
{provide: APP_INITIALIZER, useValue: _initViewEngine, multi: true},
]
})
export class ApplicationModule {

View File

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

View File

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

View File

@ -14,7 +14,7 @@ export {queryDef} from './query';
export {createComponentFactory} from './refs';
export {initServicesIfNeeded} from './services';
export {textDef} from './text';
export {rootRenderNodes} from './util';
export {elementEventFullName, nodeValue, rootRenderNodes} from './util';
export {viewDef} from './view';
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 {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 ElementRefTokenKey = tokenKey(ElementRef);
@ -278,6 +278,9 @@ function callFactory(
export function resolveDep(
view: ViewData, requestNodeIndex: number, elIndex: number, depDef: DepDef,
notFoundValue = Injector.THROW_IF_NOT_FOUND): any {
if (depDef.flags & DepFlags.Value) {
return depDef.token;
}
const startView = view;
if (depDef.flags & DepFlags.Optional) {
notFoundValue = null;
@ -288,7 +291,7 @@ export function resolveDep(
requestNodeIndex = null;
elIndex = view.def.nodes[elIndex].parent;
while (elIndex == null && view) {
elIndex = parentDiIndex(view);
elIndex = viewParentDiIndex(view);
view = view.parent;
}
}
@ -337,7 +340,7 @@ export function resolveDep(
}
}
requestNodeIndex = null;
elIndex = parentDiIndex(view);
elIndex = viewParentDiIndex(view);
view = view.parent;
}
return startView.root.injector.get(depDef.token, notFoundValue);

View File

@ -20,7 +20,7 @@ import {Type} from '../type';
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 {findElementDef, isComponentView, parentDiIndex, renderNode, resolveViewDefinition, rootRenderNodes, tokenKey} from './util';
import {isComponentView, renderNode, resolveViewDefinition, rootRenderNodes, tokenKey, viewParentDiIndex} from './util';
const EMPTY_CONTEXT = new Object();
@ -99,7 +99,7 @@ class ViewContainerRef_ implements ViewContainerRef {
let view = this._view;
let elIndex = view.def.nodes[this._elIndex].parent;
while (elIndex == null && view) {
elIndex = parentDiIndex(view);
elIndex = viewParentDiIndex(view);
view = view.parent;
}
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):
EmbeddedViewRef<C> {
const viewRef = templateRef.createEmbeddedView(context);
const viewRef = templateRef.createEmbeddedView(context || <any>{});
this.insert(viewRef, index);
return viewRef;
}

View File

@ -40,6 +40,8 @@ export class DirectDomRenderer implements RendererV2 {
nextSibling(node: any): any { return node.nextSiblibng; }
setAttribute(el: any, name: string, value: string): void { return el.setAttribute(name, value); }
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); }
removeClass(el: any, name: string): void { el.classList.remove(name); }
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]); }
insertBefore(parent: any, newChild: any, refChild: any): void {
const beforeSibling = refChild.nextSiblingOf ? refChild.nextSiblingOf : refChild;
this._delegate.attachViewAfter(beforeSibling, [newChild]);
if (refChild) {
this._delegate.attachViewAfter(refChild.previousSibling, [newChild]);
} else {
this.appendChild(parent, newChild);
}
}
removeChild(parent: any, oldChild: any): void {
if (parent) {
@ -108,14 +113,20 @@ export class LegacyRendererAdapter implements RendererV2 {
selectRootElement(selectorOrNode: any, debugInfo?: DebugContext): any {
return this._delegate.selectRootElement(selectorOrNode, debugInfo);
}
parentNode(node: any): any { return {parentOf: node}; }
nextSibling(node: any): any { return {nextSiblingOf: node}; }
parentNode(node: any): any { return node.parentNode; }
nextSibling(node: any): any { return node.nextSibling; }
setAttribute(el: any, name: string, value: string): void {
this._delegate.setElementAttribute(el, name, value);
}
removeAttribute(el: any, name: string): void {
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); }
removeClass(el: any, name: string): void { this._delegate.setElementClass(el, name, false); }
setStyle(el: any, style: string, value: any): void {

View File

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

View File

@ -72,8 +72,7 @@ export enum ArgumentType {
*/
export enum ViewFlags {
None = 0,
DirectDom = 1 << 1,
OnPush = 1 << 2
OnPush = 1 << 1
}
export interface ComponentDefinition {
@ -225,7 +224,8 @@ export interface DepDef {
export enum DepFlags {
None = 0,
SkipSelf = 1 << 0,
Optional = 1 << 1
Optional = 1 << 1,
Value = 2 << 2
}
export interface ProviderOutputDef {
@ -407,7 +407,7 @@ export function asQueryList(view: ViewData, index: number): QueryList<any> {
export interface RootData {
injector: Injector;
projectableNodes: any[][];
element: any;
selectorOrNode: any;
renderer: RendererV2;
sanitizer: Sanitizer;
}
@ -436,6 +436,14 @@ export interface RendererV2 {
* the caller can't rely on checking whether this is null or not.
*/
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;
removeAttribute(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
* that contains the view container.
*/
export function parentDiIndex(view: ViewData): number {
if (view.parent) {
const parentNodeDef = view.def.nodes[view.parentIndex];
return parentNodeDef.element && parentNodeDef.element.template ? parentNodeDef.parent :
parentNodeDef.index;
export function viewParentDiIndex(view: ViewData): number {
if (view.parent && view.context !== view.component) {
const parentNodeDef = view.parent.def.nodes[view.parentIndex];
return parentNodeDef.parent;
}
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 {
switch (def.type) {
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 {
return view.component === view.context && !!view.parent;
}

View File

@ -7,6 +7,7 @@
*/
import {ViewEncapsulation} from '../metadata/view';
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
import {appendNgContent} from './ng_content';
@ -15,7 +16,7 @@ import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline,
import {checkAndUpdateQuery, createQuery, queryDef} from './query';
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 {checkBindingNoChanges, isComponentView, resolveViewDefinition} from './util';
import {checkBindingNoChanges, isComponentView, queryIdIsReference, resolveViewDefinition} from './util';
const NOOP = (): any => undefined;
@ -41,7 +42,7 @@ export function viewDef(
const newParent = nodes[currentParent.parent];
if (newParent) {
newParent.childFlags |= currentParent.childFlags;
copyInto(currentParent.childMatchedQueries, newParent.childMatchedQueries);
copyQueryMatchesInto(currentParent.childMatchedQueries, newParent.childMatchedQueries);
}
currentParent = newParent;
}
@ -64,17 +65,18 @@ export function viewDef(
}
nodes[i] = node;
reverseChildNodes[reverseChildIndex] = node;
validateNode(currentParent, node);
validateNode(currentParent, node, nodesWithoutIndices.length);
viewNodeFlags |= node.flags;
copyInto(node.matchedQueries, viewMatchedQueries);
copyQueryMatchesInto(node.matchedQueries, viewMatchedQueries);
viewBindingCount += node.bindings.length;
viewDisposableCount += node.disposableCount;
if (currentParent) {
currentParent.childFlags |= node.flags;
copyInto(node.matchedQueries, currentParent.childMatchedQueries);
copyQueryMatchesInto(node.matchedQueries, currentParent.childMatchedQueries);
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];
if (newParent) {
newParent.childFlags |= currentParent.childFlags;
copyInto(currentParent.childMatchedQueries, newParent.childMatchedQueries);
copyQueryMatchesInto(currentParent.childMatchedQueries, newParent.childMatchedQueries);
}
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) {
target[prop] = source[prop];
if (!queryIdIsReference(prop)) {
target[prop] = true;
}
}
}
@ -159,7 +164,7 @@ function calculateReverseChildIndex(
return parentEndIndexInReverseChildOrder - lastChildOffsetRelativeToParentInDfsOrder;
}
function validateNode(parent: NodeDef, node: NodeDef) {
function validateNode(parent: NodeDef, node: NodeDef, nodeCount: number) {
const template = node.element && node.element.template;
if (template) {
if (template.lastRootNode && template.lastRootNode.flags & NodeFlags.HasEmbeddedViews) {
@ -182,12 +187,10 @@ function validateNode(parent: NodeDef, node: NodeDef) {
}
}
if (node.childCount) {
if (parent) {
const parentEnd = parent.index + parent.childCount;
if (node.index <= parentEnd && node.index + node.childCount > parentEnd) {
throw new Error(
`Illegal State: childCount of node leads outside of parent, at index ${node.index}!`);
}
const parentEnd = parent ? parent.index + parent.childCount : nodeCount - 1;
if (node.index <= parentEnd && node.index + node.childCount > parentEnd) {
throw new Error(
`Illegal State: childCount of node leads outside of parent, at index ${node.index}!`);
}
}
}

View File

@ -7,6 +7,7 @@
*/
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 {ChangeDetectionStrategy, ChangeDetectorRef, PipeTransform} from '@angular/core/src/change_detection/change_detection';
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');
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() {
beforeEach(() => { TestBed.configureCompiler({useJit: useJit}); });
@ -255,7 +265,7 @@ function declareTests({useJit}: {useJit: boolean}) {
});
describe('pipes', () => {
it('should support pipes in bindings', () => {
viewEngine || it('should support pipes in bindings', () => {
TestBed.configureTestingModule({declarations: [MyComp, MyDir, DoublePipe]});
const template = '<div my-dir #dir="mydir" [elprop]="ctxProp | double"></div>';
TestBed.overrideComponent(MyComp, {set: {template}});
@ -520,7 +530,7 @@ function declareTests({useJit}: {useJit: boolean}) {
const fixture = TestBed.createComponent(MyComp);
const value = fixture.debugElement.childNodes[0].references['alice'];
expect(value).toBeAnInstanceOf(TemplateRef_);
expect(value.createEmbeddedView).toBeTruthy();
});
it('should preserve case', () => {
@ -535,7 +545,7 @@ function declareTests({useJit}: {useJit: boolean}) {
});
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]});
const template =
'<template ngFor [ngForOf]="[1]" let-i><child-cmp-no-template #cmp></child-cmp-no-template>{{i}}-{{cmp.ctxProp}}</template>';
@ -660,29 +670,31 @@ function declareTests({useJit}: {useJit: boolean}) {
});
if (getDOM().supportsDOMEvents()) {
it('should be checked when an async pipe requests a check', fakeAsync(() => {
TestBed.configureTestingModule(
{declarations: [MyComp, PushCmpWithAsyncPipe], imports: [CommonModule]});
const template = '<push-cmp-with-async #cmp></push-cmp-with-async>';
TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp);
viewEngine ||
it('should be checked when an async pipe requests a check', fakeAsync(() => {
TestBed.configureTestingModule(
{declarations: [MyComp, PushCmpWithAsyncPipe], imports: [CommonModule]});
const template = '<push-cmp-with-async #cmp></push-cmp-with-async>';
TestBed.overrideComponent(MyComp, {set: {template}});
const fixture = TestBed.createComponent(MyComp);
tick();
tick();
const cmp: PushCmpWithAsyncPipe = fixture.debugElement.children[0].references['cmp'];
fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(1);
const cmp: PushCmpWithAsyncPipe =
fixture.debugElement.children[0].references['cmp'];
fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(1);
fixture.detectChanges();
fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(1);
fixture.detectChanges();
fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(1);
cmp.resolve(2);
tick();
cmp.resolve(2);
tick();
fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(2);
}));
fixture.detectChanges();
expect(cmp.numberOfChecks).toEqual(2);
}));
}
});
@ -885,24 +897,26 @@ function declareTests({useJit}: {useJit: boolean}) {
expect(tc.nativeElement.id).toEqual('newId');
});
it('should not use template variables for expressions in hostProperties', () => {
@Directive({selector: '[host-properties]', host: {'[id]': 'id', '[title]': 'unknownProp'}})
class DirectiveWithHostProps {
id = 'one';
}
viewEngine ||
it('should not use template variables for expressions in hostProperties', () => {
@Directive(
{selector: '[host-properties]', host: {'[id]': 'id', '[title]': 'unknownProp'}})
class DirectiveWithHostProps {
id = 'one';
}
const fixture =
TestBed.configureTestingModule({declarations: [MyComp, DirectiveWithHostProps]})
.overrideComponent(
MyComp,
{set: {template: `<div *ngFor="let id of ['forId']" host-properties></div>`}})
.createComponent(MyComp);
fixture.detectChanges();
const fixture =
TestBed.configureTestingModule({declarations: [MyComp, DirectiveWithHostProps]})
.overrideComponent(MyComp, {
set: {template: `<div *ngFor="let id of ['forId']" host-properties></div>`}
})
.createComponent(MyComp);
fixture.detectChanges();
const tc = fixture.debugElement.children[0];
expect(tc.properties['id']).toBe('one');
expect(tc.properties['title']).toBe(undefined);
});
const tc = fixture.debugElement.children[0];
expect(tc.properties['id']).toBe('one');
expect(tc.properties['title']).toBe(undefined);
});
it('should not allow pipes in hostProperties', () => {
@Directive({selector: '[host-properties]', host: {'[id]': 'id | uppercase'}})
@ -916,7 +930,8 @@ function declareTests({useJit}: {useJit: boolean}) {
.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)'}})
class DirectiveWithHostListener {
id = 'one';
@ -1232,7 +1247,7 @@ function declareTests({useJit}: {useJit: boolean}) {
.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>;
@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', () => {
TestBed.configureTestingModule({declarations: [MyComp, SomeDirectiveMissingAnnotation]});
@ -1721,7 +1736,7 @@ class MyService {
class SimpleImperativeViewComponent {
done: any;
constructor(self: ElementRef, renderer: Renderer) {
constructor(self: ElementRef) {
const hostElement = self.nativeElement;
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', () => {
const view = createRootView(
compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div'),
elementDef(NodeFlags.None, null, null, 0, 'div'),
]),
{}, [], 'root');
const rootNodes = rootRenderNodes(view);
@ -73,7 +73,7 @@ export function main() {
it('should select root elements based on a node', () => {
const view = createRootView(
compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div'),
elementDef(NodeFlags.None, null, null, 0, 'div'),
]),
{}, [], rootNode);
const rootNodes = rootRenderNodes(view);
@ -83,7 +83,7 @@ export function main() {
it('should set attributes on the root node', () => {
const view = createRootView(
compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}),
elementDef(NodeFlags.None, null, null, 0, 'div', {'a': 'b'}),
]),
{}, [], rootNode);
expect(rootNode.getAttribute('a')).toBe('b');
@ -93,7 +93,7 @@ export function main() {
rootNode.appendChild(document.createElement('div'));
const view = createRootView(
compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div', {'a': 'b'}),
elementDef(NodeFlags.None, null, null, 0, 'div', {'a': 'b'}),
]),
{}, [], rootNode);
expect(rootNode.childNodes.length).toBe(0);

View File

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

View File

@ -7,7 +7,7 @@
*/
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 {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -21,8 +21,8 @@ export function main() {
return viewDef(viewFlags, nodes, update, handleEvent);
}
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
return viewDef(ViewFlags.None, nodes, update);
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
return () => viewDef(ViewFlags.None, nodes, update);
}
function createAndGetRootNodes(
@ -38,7 +38,7 @@ export function main() {
const {view: parentView, rootNodes} = createAndGetRootNodes(
compViewDef([
elementDef(NodeFlags.None, null, null, 2, 'div'),
elementDef(NodeFlags.None, null, null, 1, 'div'),
anchorDef(
NodeFlags.HasEmbeddedViews, null, null, 0,
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 {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 {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -21,8 +21,8 @@ export function main() {
return viewDef(viewFlags, nodes, update, handleEvent);
}
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
return viewDef(ViewFlags.None, nodes, update);
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
return () => viewDef(ViewFlags.None, nodes, update);
}
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 {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 {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -23,8 +23,8 @@ export function main() {
viewFlags, nodes, update, handleEvent, 'someCompId', ViewEncapsulation.None, []);
}
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
return viewDef(ViewFlags.None, nodes, update);
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
return () => viewDef(ViewFlags.None, nodes, update);
}
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 {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 {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
@ -22,8 +22,8 @@ export function main() {
return viewDef(viewFlags, nodes, update, handleEvent);
}
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinition {
return viewDef(ViewFlags.None, nodes, update);
function embeddedViewDef(nodes: NodeDef[], update?: ViewUpdateFn): ViewDefinitionFactory {
return () => viewDef(ViewFlags.None, nodes, update);
}
function createAndGetRootNodes(
@ -118,7 +118,7 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 3, 'div'),
...viewQueryProviders(compViewDef([
elementDef(NodeFlags.None, null, null, 1, 'span'),
elementDef(NodeFlags.None, null, null, 0, 'span'),
])),
aServiceProvider(),
]));
@ -134,14 +134,10 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 5, 'div'),
...contentQueryProviders(),
anchorDef(
NodeFlags.HasEmbeddedViews, null, null, 2,
viewDef(
ViewFlags.None,
[
elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(),
])),
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 2, embeddedViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(),
])),
...contentQueryProviders(),
]));
@ -164,14 +160,10 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 3, 'div'),
...contentQueryProviders(),
anchorDef(
NodeFlags.HasEmbeddedViews, null, null, 0,
viewDef(
ViewFlags.None,
[
elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(),
])),
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, embeddedViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(),
])),
elementDef(NodeFlags.None, null, null, 3, 'div'),
...contentQueryProviders(),
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0),
@ -197,14 +189,10 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 3, 'div'),
...contentQueryProviders(),
anchorDef(
NodeFlags.HasEmbeddedViews, null, null, 0,
viewDef(
ViewFlags.None,
[
elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(),
])),
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, embeddedViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(),
])),
]));
Services.checkAndUpdateView(view);
@ -228,14 +216,10 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 2, 'div'),
...viewQueryProviders(compViewDef([
anchorDef(
NodeFlags.HasEmbeddedViews, null, null, 0,
viewDef(
ViewFlags.None,
[
elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(),
])),
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, embeddedViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(),
])),
])),
]));
@ -328,7 +312,7 @@ export function main() {
const {view} = createAndGetRootNodes(compViewDef([
anchorDef(
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, []),
queryDef(NodeFlags.HasContentQuery, 'query1', {'a': QueryBindingType.First}),
]));
@ -360,16 +344,12 @@ export function main() {
describe('general binding behavior', () => {
it('should checkNoChanges', () => {
const {view} = createAndGetRootNodes(compViewDef([
elementDef(NodeFlags.None, null, null, 4, 'div'),
elementDef(NodeFlags.None, null, null, 3, 'div'),
...contentQueryProviders(),
anchorDef(
NodeFlags.HasEmbeddedViews, null, null, 1,
viewDef(
ViewFlags.None,
[
elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(),
])),
anchorDef(NodeFlags.HasEmbeddedViews, null, null, 0, embeddedViewDef([
elementDef(NodeFlags.None, null, null, 1, 'div'),
aServiceProvider(),
])),
]));
Services.checkAndUpdateView(view);

View File

@ -228,10 +228,10 @@ export function main() {
elementDef(NodeFlags.None, null, null, 1, 'span'),
anchorDef(
NodeFlags.None, null, null, 0,
viewDef(
() => viewDef(
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 {
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(
viewFlags,
[