feat(compiler): integrate compiler with view engine - change detection tests work (#14412)

Included refactoring:
- make ViewData.parentIndex point to component provider index
- split NodeType.Provider into Provider / Directive / Pipe
- make purePipe take the real pipe as argument to detect changes
- order change detection:
  1) directive props
  2) renderer props

Part of #14013

PR Close #14412
This commit is contained in:
Tobias Bosch
2017-02-13 17:02:00 -06:00
committed by Miško Hevery
parent 1dc9be4b7d
commit e4e9dbe33d
39 changed files with 942 additions and 768 deletions
@@ -205,14 +205,14 @@ export class AotCompiler {
const pipes = ngModule.transitiveModule.pipes.map(
pipe => this._metadataResolver.getPipeSummary(pipe.reference));
const parsedTemplate = this._templateParser.parse(
const {template: parsedTemplate, pipes: usedPipes} = this._templateParser.parse(
compMeta, compMeta.template.template, directives, pipes, ngModule.schemas,
identifierName(compMeta.type));
const stylesExpr = componentStyles ? o.variable(componentStyles.stylesVar) : o.literalArr([]);
const compiledAnimations =
this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
const viewResult = this._viewCompiler.compileComponent(
compMeta, parsedTemplate, stylesExpr, pipes, compiledAnimations);
compMeta, parsedTemplate, stylesExpr, usedPipes, compiledAnimations);
if (componentStyles) {
targetStatements.push(
..._resolveStyleStatements(this._symbolResolver, componentStyles, fileSuffix));
File diff suppressed because it is too large Load Diff
@@ -13,12 +13,12 @@ import {EMPTY_STATE as EMPTY_ANIMATION_STATE} from '../private_import_core';
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType} from '../template_parser/template_ast';
import {isFirstViewCheck} from './binding_util';
import {ConvertPropertyBindingResult} from './expression_converter';
import {LegacyConvertPropertyBindingResult} from './expression_converter';
import {createEnumExpression} from './identifier_util';
export function createCheckRenderBindingStmt(
view: o.Expression, renderElement: o.Expression, boundProp: BoundElementPropertyAst,
oldValue: o.ReadPropExpr, evalResult: ConvertPropertyBindingResult,
oldValue: o.ReadPropExpr, evalResult: LegacyConvertPropertyBindingResult,
securityContextExpression?: o.Expression): o.Statement[] {
const checkStmts: o.Statement[] = [...evalResult.stmts];
const securityContext = calcSecurityContext(boundProp, securityContextExpression);
@@ -84,7 +84,7 @@ function calcSecurityContext(
export function createCheckAnimationBindingStmts(
view: o.Expression, componentView: o.Expression, boundProp: BoundElementPropertyAst,
boundOutputs: BoundEventAst[], eventListener: o.Expression, renderElement: o.Expression,
oldValue: o.ReadPropExpr, evalResult: ConvertPropertyBindingResult) {
oldValue: o.ReadPropExpr, evalResult: LegacyConvertPropertyBindingResult) {
const detachStmts: o.Statement[] = [];
const updateStmts: o.Statement[] = [];
@@ -8,7 +8,7 @@
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompileIdentifierMetadata, dirWrapperClassName, identifierModuleUrl, identifierName} from './compile_metadata';
import {createCheckBindingField, isFirstViewCheck} from './compiler_util/binding_util';
import {EventHandlerVars, convertActionBinding, convertPropertyBinding} from './compiler_util/expression_converter';
import {EventHandlerVars, convertActionBinding, legacyConvertPropertyBinding} from './compiler_util/expression_converter';
import {createCheckAnimationBindingStmts, createCheckRenderBindingStmt} from './compiler_util/render_util';
import {CompilerConfig} from './config';
import {Parser} from './expression_parser/parser';
@@ -253,7 +253,7 @@ function addCheckHostMethod(
];
hostProps.forEach((hostProp, hostPropIdx) => {
const field = createCheckBindingField(builder);
const evalResult = convertPropertyBinding(
const evalResult = legacyConvertPropertyBinding(
builder, null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostProp.value, field.bindingId);
if (!evalResult) {
return;
@@ -285,8 +285,7 @@ function addHandleEventMethod(hostListeners: BoundEventAst[], builder: Directive
const actionStmts: o.Statement[] = [resultVar.set(o.literal(true)).toDeclStmt(o.BOOL_TYPE)];
hostListeners.forEach((hostListener, eventIdx) => {
const evalResult = convertActionBinding(
builder, null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostListener.handler,
`sub_${eventIdx}`);
null, o.THIS_EXPR.prop(CONTEXT_FIELD_NAME), hostListener.handler, `sub_${eventIdx}`);
const trueStmts = evalResult.stmts;
if (evalResult.allowDefault) {
trueStmts.push(resultVar.set(evalResult.allowDefault.and(resultVar)).toStmt());
@@ -400,11 +400,36 @@ export class Identifiers {
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.queryDef
};
static pureArrayDef: IdentifierSpec = {
name: 'pureArrayDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.pureArrayDef
};
static pureObjectDef: IdentifierSpec = {
name: 'pureObjectDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.pureObjectDef
};
static purePipeDef: IdentifierSpec = {
name: 'purePipeDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.purePipeDef
};
static pipeDef: IdentifierSpec = {
name: 'pipeDef',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.pipeDef
};
static nodeValue: IdentifierSpec = {
name: 'nodeValue',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.nodeValue
};
static unwrapValue: IdentifierSpec = {
name: 'unwrapValue',
moduleUrl: VIEW_ENGINE_MODULE_URL,
runtime: viewEngine.unwrapValue
};
}
export function assetUrl(pkg: string, path: string = null, type: string = 'src'): string {
@@ -278,14 +278,14 @@ export class JitCompiler implements Compiler {
template.directives.map(dir => this._metadataResolver.getDirectiveSummary(dir.reference));
const pipes = template.ngModule.transitiveModule.pipes.map(
pipe => this._metadataResolver.getPipeSummary(pipe.reference));
const parsedTemplate = this._templateParser.parse(
const {template: parsedTemplate, pipes: usedPipes} = this._templateParser.parse(
compMeta, compMeta.template.template, directives, pipes, template.ngModule.schemas,
identifierName(compMeta.type));
const compiledAnimations =
this._animationCompiler.compile(identifierName(compMeta.type), parsedAnimations);
const compileResult = this._viewCompiler.compileComponent(
compMeta, parsedTemplate, ir.variable(stylesCompileResult.componentStylesheet.stylesVar),
pipes, compiledAnimations);
usedPipes, compiledAnimations);
const statements = stylesCompileResult.componentStylesheet.statements
.concat(...compiledAnimations.map(ca => ca.statements))
.concat(compileResult.statements);
@@ -51,6 +51,7 @@ export class BoundProperty {
*/
export class BindingParser {
pipesByName: Map<string, CompilePipeSummary> = new Map();
private _usedPipes: Map<string, CompilePipeSummary> = new Map();
constructor(
private _exprParser: Parser, private _interpolationConfig: InterpolationConfig,
@@ -59,6 +60,8 @@ export class BindingParser {
pipes.forEach(pipe => this.pipesByName.set(pipe.name, pipe));
}
getUsedPipes(): CompilePipeSummary[] { return Array.from(this._usedPipes.values()); }
createDirectiveHostPropertyAsts(dirMeta: CompileDirectiveSummary, sourceSpan: ParseSourceSpan):
BoundElementPropertyAst[] {
if (dirMeta.hostProperties) {
@@ -377,11 +380,14 @@ export class BindingParser {
const collector = new PipeCollector();
ast.visit(collector);
collector.pipes.forEach((ast, pipeName) => {
if (!this.pipesByName.has(pipeName)) {
const pipeMeta = this.pipesByName.get(pipeName);
if (!pipeMeta) {
this._reportError(
`The pipe '${pipeName}' could not be found`,
new ParseSourceSpan(
sourceSpan.start.moveBy(ast.span.start), sourceSpan.start.moveBy(ast.span.end)));
} else {
this._usedPipes.set(pipeName, pipeMeta);
}
});
}
@@ -7,7 +7,9 @@
*/
import {Inject, InjectionToken, Optional, SchemaMetadata} from '@angular/core';
import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, CompileTemplateSummary, CompileTokenMetadata, CompileTypeMetadata, identifierName} from '../compile_metadata';
import {AST, ASTWithSource, EmptyExpr} from '../expression_parser/ast';
import {Parser} from '../expression_parser/parser';
import {isPresent} from '../facade/lang';
import {I18NHtmlParser} from '../i18n/i18n_html_parser';
@@ -25,10 +27,12 @@ import {ElementSchemaRegistry} from '../schema/element_schema_registry';
import {CssSelector, SelectorMatcher} from '../selector';
import {isStyleUrlResolvable} from '../style_url_resolver';
import {syntaxError} from '../util';
import {BindingParser, BoundProperty} from './binding_parser';
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from './template_ast';
import {PreparsedElementType, preparseElement} from './template_preparser';
// Group 1 = "bind-"
// Group 2 = "let-"
// Group 3 = "ref-/#"
@@ -76,7 +80,9 @@ export class TemplateParseError extends ParseError {
}
export class TemplateParseResult {
constructor(public templateAst?: TemplateAst[], public errors?: ParseError[]) {}
constructor(
public templateAst?: TemplateAst[], public usedPipes?: CompilePipeSummary[],
public errors?: ParseError[]) {}
}
@CompilerInjectable()
@@ -88,7 +94,8 @@ export class TemplateParser {
parse(
component: CompileDirectiveMetadata, template: string, directives: CompileDirectiveSummary[],
pipes: CompilePipeSummary[], schemas: SchemaMetadata[], templateUrl: string): TemplateAst[] {
pipes: CompilePipeSummary[], schemas: SchemaMetadata[],
templateUrl: string): {template: TemplateAst[], pipes: CompilePipeSummary[]} {
const result = this.tryParse(component, template, directives, pipes, schemas, templateUrl);
const warnings = result.errors.filter(error => error.level === ParseErrorLevel.WARNING);
const errors = result.errors.filter(error => error.level === ParseErrorLevel.FATAL);
@@ -102,7 +109,7 @@ export class TemplateParser {
throw syntaxError(`Template parse errors:\n${errorString}`);
}
return result.templateAst;
return {template: result.templateAst, pipes: result.usedPipes};
}
tryParse(
@@ -121,6 +128,7 @@ export class TemplateParser {
templateUrl: string): TemplateParseResult {
let result: TemplateAst[];
const errors = htmlAstWithErrors.errors;
const usedPipes: CompilePipeSummary[] = [];
if (htmlAstWithErrors.rootNodes.length > 0) {
const uniqDirectives = removeSummaryDuplicates(directives);
const uniqPipes = removeSummaryDuplicates(pipes);
@@ -140,13 +148,14 @@ export class TemplateParser {
errors);
result = html.visitAll(parseVisitor, htmlAstWithErrors.rootNodes, EMPTY_ELEMENT_CONTEXT);
errors.push(...providerViewContext.errors);
usedPipes.push(...bindingParser.getUsedPipes());
} else {
result = [];
}
this._assertNoReferenceDuplicationOnTemplate(result, errors);
if (errors.length > 0) {
return new TemplateParseResult(result, errors);
return new TemplateParseResult(result, usedPipes, errors);
}
if (this.transforms) {
@@ -154,7 +163,7 @@ export class TemplateParser {
(transform: TemplateAstVisitor) => { result = templateVisitAll(transform, result); });
}
return new TemplateParseResult(result, errors);
return new TemplateParseResult(result, usedPipes, errors);
}
expandHtml(htmlAstWithErrors: ParseTreeResult, forced: boolean = false): ParseTreeResult {
@@ -303,11 +312,12 @@ class TemplateParseVisitor implements html.Visitor {
const {directives: directiveMetas, matchElement} =
this._parseDirectives(this.selectorMatcher, elementCssSelector);
const references: ReferenceAst[] = [];
const boundDirectivePropNames = new Set<string>();
const directiveAsts = this._createDirectiveAsts(
isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps,
elementOrDirectiveRefs, element.sourceSpan, references);
const elementProps: BoundElementPropertyAst[] =
this._createElementPropertyAsts(element.name, elementOrDirectiveProps, directiveAsts);
elementOrDirectiveRefs, element.sourceSpan, references, boundDirectivePropNames);
const elementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
element.name, elementOrDirectiveProps, boundDirectivePropNames);
const isViewRoot = parent.isTemplateElement || hasInlineTemplates;
const providerContext = new ProviderElementContext(
@@ -372,11 +382,12 @@ class TemplateParseVisitor implements html.Visitor {
createElementCssSelector(TEMPLATE_ELEMENT, templateMatchableAttrs);
const {directives: templateDirectiveMetas} =
this._parseDirectives(this.selectorMatcher, templateCssSelector);
const templateBoundDirectivePropNames = new Set<string>();
const templateDirectiveAsts = this._createDirectiveAsts(
true, element.name, templateDirectiveMetas, templateElementOrDirectiveProps, [],
element.sourceSpan, []);
element.sourceSpan, [], templateBoundDirectivePropNames);
const templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
element.name, templateElementOrDirectiveProps, templateDirectiveAsts);
element.name, templateElementOrDirectiveProps, templateBoundDirectivePropNames);
this._assertNoComponentsNorElementBindingsOnTemplate(
templateDirectiveAsts, templateElementProps, element.sourceSpan);
const templateProviderContext = new ProviderElementContext(
@@ -544,7 +555,8 @@ class TemplateParseVisitor implements html.Visitor {
private _createDirectiveAsts(
isTemplateElement: boolean, elementName: string, directives: CompileDirectiveSummary[],
props: BoundProperty[], elementOrDirectiveRefs: ElementOrDirectiveRef[],
elementSourceSpan: ParseSourceSpan, targetReferences: ReferenceAst[]): DirectiveAst[] {
elementSourceSpan: ParseSourceSpan, targetReferences: ReferenceAst[],
targetBoundDirectivePropNames: Set<string>): DirectiveAst[] {
const matchedReferences = new Set<string>();
let component: CompileDirectiveSummary = null;
@@ -557,13 +569,14 @@ class TemplateParseVisitor implements html.Visitor {
component = directive;
}
const directiveProperties: BoundDirectivePropertyAst[] = [];
const hostProperties =
let hostProperties =
this._bindingParser.createDirectiveHostPropertyAsts(directive, sourceSpan);
// Note: We need to check the host properties here as well,
// as we don't know the element name in the DirectiveWrapperCompiler yet.
this._checkPropertiesInSchema(elementName, hostProperties);
hostProperties = this._checkPropertiesInSchema(elementName, hostProperties);
const hostEvents = this._bindingParser.createDirectiveHostEventAsts(directive, sourceSpan);
this._createDirectivePropertyAsts(directive.inputs, props, directiveProperties);
this._createDirectivePropertyAsts(
directive.inputs, props, directiveProperties, targetBoundDirectivePropNames);
elementOrDirectiveRefs.forEach((elOrDirRef) => {
if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
(directive.exportAs == elOrDirRef.value)) {
@@ -596,7 +609,8 @@ class TemplateParseVisitor implements html.Visitor {
private _createDirectivePropertyAsts(
directiveProperties: {[key: string]: string}, boundProps: BoundProperty[],
targetBoundDirectiveProps: BoundDirectivePropertyAst[]) {
targetBoundDirectiveProps: BoundDirectivePropertyAst[],
targetBoundDirectivePropNames: Set<string>) {
if (directiveProperties) {
const boundPropsByName = new Map<string, BoundProperty>();
boundProps.forEach(boundProp => {
@@ -613,8 +627,11 @@ class TemplateParseVisitor implements html.Visitor {
// Bindings are optional, so this binding only needs to be set up if an expression is given.
if (boundProp) {
targetBoundDirectiveProps.push(new BoundDirectivePropertyAst(
dirProp, boundProp.name, boundProp.expression, boundProp.sourceSpan));
targetBoundDirectivePropNames.add(boundProp.name);
if (!isEmptyExpression(boundProp.expression)) {
targetBoundDirectiveProps.push(new BoundDirectivePropertyAst(
dirProp, boundProp.name, boundProp.expression, boundProp.sourceSpan));
}
}
});
}
@@ -622,23 +639,15 @@ class TemplateParseVisitor implements html.Visitor {
private _createElementPropertyAsts(
elementName: string, props: BoundProperty[],
directives: DirectiveAst[]): BoundElementPropertyAst[] {
boundDirectivePropNames: Set<string>): BoundElementPropertyAst[] {
const boundElementProps: BoundElementPropertyAst[] = [];
const boundDirectivePropsIndex = new Map<string, BoundDirectivePropertyAst>();
directives.forEach((directive: DirectiveAst) => {
directive.inputs.forEach((prop: BoundDirectivePropertyAst) => {
boundDirectivePropsIndex.set(prop.templateName, prop);
});
});
props.forEach((prop: BoundProperty) => {
if (!prop.isLiteral && !boundDirectivePropsIndex.get(prop.name)) {
if (!prop.isLiteral && !boundDirectivePropNames.has(prop.name)) {
boundElementProps.push(this._bindingParser.createElementPropertyAst(elementName, prop));
}
});
this._checkPropertiesInSchema(elementName, boundElementProps);
return boundElementProps;
return this._checkPropertiesInSchema(elementName, boundElementProps);
}
private _findComponentDirectives(directives: DirectiveAst[]): DirectiveAst[] {
@@ -723,8 +732,11 @@ class TemplateParseVisitor implements html.Visitor {
});
}
private _checkPropertiesInSchema(elementName: string, boundProps: BoundElementPropertyAst[]) {
boundProps.forEach((boundProp) => {
private _checkPropertiesInSchema(elementName: string, boundProps: BoundElementPropertyAst[]):
BoundElementPropertyAst[] {
// Note: We can't filter out empty expressions before this method,
// as we still want to validate them!
return boundProps.filter((boundProp) => {
if (boundProp.type === PropertyBindingType.Property &&
!this._schemaRegistry.hasProperty(elementName, boundProp.name, this._schemas)) {
let errorMsg =
@@ -741,6 +753,7 @@ class TemplateParseVisitor implements html.Visitor {
}
this._reportError(errorMsg, boundProp.sourceSpan);
}
return !isEmptyExpression(boundProp.value);
});
}
@@ -870,3 +883,10 @@ export function removeSummaryDuplicates<T extends{type: CompileTypeMetadata}>(it
return Array.from(map.values());
}
function isEmptyExpression(ast: AST): boolean {
if (ast instanceof ASTWithSource) {
ast = ast.ast;
}
return ast instanceof EmptyExpr;
}
@@ -8,7 +8,7 @@
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
import {CompileDirectiveMetadata, CompilePipeSummary, tokenName, viewClassName} from '../compile_metadata';
import {EventHandlerVars, NameResolver} from '../compiler_util/expression_converter';
import {EventHandlerVars, LegacyNameResolver} from '../compiler_util/expression_converter';
import {CompilerConfig} from '../config';
import {isPresent} from '../facade/lang';
import * as o from '../output/output_ast';
@@ -33,7 +33,7 @@ export class CompileViewRootNode {
public ngContentIndex?: number) {}
}
export class CompileView implements NameResolver {
export class CompileView implements LegacyNameResolver {
public viewType: ViewType;
public viewQueries: Map<any, CompileQuery[]>;
@@ -102,8 +102,8 @@ function generateHandleEventMethod(
});
boundEvents.forEach((renderEvent, renderEventIdx) => {
const evalResult = convertActionBinding(
compileElement.view, compileElement.view, compileElement.view.componentContext,
renderEvent.handler, `sub_${renderEventIdx}`);
compileElement.view, compileElement.view.componentContext, renderEvent.handler,
`sub_${renderEventIdx}`);
const trueStmts = evalResult.stmts;
if (evalResult.allowDefault) {
trueStmts.push(resultVar.set(evalResult.allowDefault.and(resultVar)).toStmt());
@@ -9,7 +9,7 @@
import {SecurityContext} from '@angular/core';
import {createCheckBindingField} from '../compiler_util/binding_util';
import {convertPropertyBinding} from '../compiler_util/expression_converter';
import {legacyConvertPropertyBinding} from '../compiler_util/expression_converter';
import {createEnumExpression} from '../compiler_util/identifier_util';
import {createCheckAnimationBindingStmts, createCheckRenderBindingStmt} from '../compiler_util/render_util';
import {DirectiveWrapperExpressions} from '../directive_wrapper_compiler';
@@ -26,7 +26,7 @@ import {getHandleEventMethodName} from './util';
export function bindRenderText(
boundText: BoundTextAst, compileNode: CompileNode, view: CompileView): void {
const valueField = createCheckBindingField(view);
const evalResult = convertPropertyBinding(
const evalResult = legacyConvertPropertyBinding(
view, view, view.componentContext, boundText.value, valueField.bindingId);
if (!evalResult) {
return null;
@@ -53,7 +53,7 @@ export function bindRenderInputs(
boundProps.forEach((boundProp) => {
const bindingField = createCheckBindingField(view);
view.detectChangesRenderPropertiesMethod.resetDebugInfo(compileElement.nodeIndex, boundProp);
const evalResult = convertPropertyBinding(
const evalResult = legacyConvertPropertyBinding(
view, view, compileElement.view.componentContext, boundProp.value, bindingField.bindingId);
if (!evalResult) {
return;
@@ -123,7 +123,7 @@ export function bindDirectiveInputs(
const bindingId = `${compileElement.nodeIndex}_${dirIndex}_${inputIdx}`;
detectChangesInInputsMethod.resetDebugInfo(compileElement.nodeIndex, input);
const evalResult =
convertPropertyBinding(view, view, view.componentContext, input.value, bindingId);
legacyConvertPropertyBinding(view, view, view.componentContext, input.value, bindingId);
if (!evalResult) {
return;
}
@@ -9,7 +9,7 @@
import {ViewEncapsulation} from '@angular/core';
import {CompileDirectiveSummary, identifierModuleUrl, identifierName} from '../compile_metadata';
import {createSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter';
import {legacyCreateSharedBindingVariablesIfNeeded} from '../compiler_util/expression_converter';
import {createDiTokenExpression, createInlineArray} from '../compiler_util/identifier_util';
import {isPresent} from '../facade/lang';
import {Identifiers, createIdentifier, identifierToken} from '../identifiers';
@@ -586,7 +586,7 @@ function generateDetectChangesMethod(view: CompileView): o.Statement[] {
stmts.push(new o.IfStmt(o.not(ViewProperties.throwOnChange), afterViewStmts));
}
const varStmts = createSharedBindingVariablesIfNeeded(stmts);
const varStmts = legacyCreateSharedBindingVariablesIfNeeded(stmts);
return varStmts.concat(stmts);
}
File diff suppressed because it is too large Load Diff
@@ -70,7 +70,8 @@ export function main() {
if (pipes === null) {
pipes = [];
}
return parser.parse(component, template, directives, pipes, schemas, 'TestComp');
return parser.parse(component, template, directives, pipes, schemas, 'TestComp')
.template;
};
}));
}
@@ -306,10 +307,10 @@ export function main() {
isComponent: true,
template: new CompileTemplateMetadata({interpolation: ['{%', '%}']})
});
expect(humanizeTplAst(parser.parse(component, '{%a%}', [], [], [], 'TestComp'), {
start: '{%',
end: '%}'
})).toEqual([[BoundTextAst, '{% a %}']]);
expect(humanizeTplAst(
parser.parse(component, '{%a%}', [], [], [], 'TestComp').template,
{start: '{%', end: '%}'}))
.toEqual([[BoundTextAst, '{% a %}']]);
}));
describe('bound properties', () => {
+1 -3
View File
@@ -10,7 +10,7 @@ import {isDevMode} from '../application_ref';
import {SecurityContext} from '../security';
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';
import {checkAndUpdateBinding, dispatchEvent, elementEventFullName, resolveViewDefinition, sliceErrorStack} from './util';
export function anchorDef(
flags: NodeFlags, matchedQueries: [string, QueryValueType][], ngContentIndex: number,
@@ -209,8 +209,6 @@ function checkAndUpdateElementValue(view: ViewData, def: NodeDef, bindingIdx: nu
if (!checkAndUpdateBinding(view, def, bindingIdx, value)) {
return;
}
value = unwrapValue(value);
const binding = def.bindings[bindingIdx];
const name = binding.name;
const renderNode = asElementData(view, def.index).renderElement;
-1
View File
@@ -31,7 +31,6 @@ export function viewDebugError(msg: string, context: DebugContext): Error {
const err = new Error(msg);
(err as any)[ERROR_DEBUG_CONTEXT] = context;
err.stack = context.source;
context.view.state |= ViewState.Errored;
return err;
}
+2 -2
View File
@@ -8,13 +8,13 @@
export {anchorDef, elementDef} from './element';
export {ngContentDef} from './ng_content';
export {directiveDef, providerDef} from './provider';
export {directiveDef, pipeDef, providerDef} from './provider';
export {pureArrayDef, pureObjectDef, purePipeDef} from './pure_expression';
export {queryDef} from './query';
export {createComponentFactory} from './refs';
export {initServicesIfNeeded} from './services';
export {textDef} from './text';
export {elementEventFullName, nodeValue, rootRenderNodes} from './util';
export {elementEventFullName, nodeValue, rootRenderNodes, unwrapValue} from './util';
export {viewDef} from './view';
export {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
File diff suppressed because it is too large Load Diff
@@ -7,24 +7,22 @@
*/
import {BindingDef, BindingType, DepDef, DepFlags, NodeData, NodeDef, NodeType, ProviderData, PureExpressionData, PureExpressionType, Services, ViewData, asPureExpressionData} from './types';
import {checkAndUpdateBinding, tokenKey, unwrapValue} from './util';
import {checkAndUpdateBinding, tokenKey} from './util';
export function purePipeDef(pipeToken: any, argCount: number): NodeDef {
return _pureExpressionDef(
PureExpressionType.Pipe, new Array(argCount),
{token: pipeToken, tokenKey: tokenKey(pipeToken), flags: DepFlags.None});
export function purePipeDef(argCount: number): NodeDef {
// argCount + 1 to include the pipe as first arg
return _pureExpressionDef(PureExpressionType.Pipe, new Array(argCount + 1));
}
export function pureArrayDef(argCount: number): NodeDef {
return _pureExpressionDef(PureExpressionType.Array, new Array(argCount), undefined);
return _pureExpressionDef(PureExpressionType.Array, new Array(argCount));
}
export function pureObjectDef(propertyNames: string[]): NodeDef {
return _pureExpressionDef(PureExpressionType.Object, propertyNames, undefined);
return _pureExpressionDef(PureExpressionType.Object, propertyNames);
}
function _pureExpressionDef(
type: PureExpressionType, propertyNames: string[], pipeDep: DepDef): NodeDef {
function _pureExpressionDef(type: PureExpressionType, propertyNames: string[]): NodeDef {
const bindings: BindingDef[] = new Array(propertyNames.length);
for (let i = 0; i < propertyNames.length; i++) {
const prop = propertyNames[i];
@@ -55,17 +53,14 @@ function _pureExpressionDef(
element: undefined,
provider: undefined,
text: undefined,
pureExpression: {type, pipeDep},
pureExpression: {type},
query: undefined,
ngContent: undefined
};
}
export function createPureExpression(view: ViewData, def: NodeDef): PureExpressionData {
const pipe = def.pureExpression.pipeDep ?
Services.resolveDep(view, def.index, def.parent, def.pureExpression.pipeDep) :
undefined;
return {value: undefined, pipe};
return {value: undefined};
}
export function checkAndUpdatePureExpressionInline(
@@ -99,17 +94,6 @@ export function checkAndUpdatePureExpressionInline(
const data = asPureExpressionData(view, def.index);
if (changed) {
v0 = unwrapValue(v0);
v1 = unwrapValue(v1);
v2 = unwrapValue(v2);
v3 = unwrapValue(v3);
v4 = unwrapValue(v4);
v5 = unwrapValue(v5);
v6 = unwrapValue(v6);
v7 = unwrapValue(v7);
v8 = unwrapValue(v8);
v9 = unwrapValue(v9);
let value: any;
switch (def.pureExpression.type) {
case PureExpressionType.Array:
@@ -165,36 +149,37 @@ export function checkAndUpdatePureExpressionInline(
}
break;
case PureExpressionType.Pipe:
const pipe = v0;
switch (bindings.length) {
case 10:
value = data.pipe.transform(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
value = pipe.transform(v1, v2, v3, v4, v5, v6, v7, v8, v9);
break;
case 9:
value = data.pipe.transform(v0, v1, v2, v3, v4, v5, v6, v7, v8);
value = pipe.transform(v1, v2, v3, v4, v5, v6, v7, v8);
break;
case 8:
value = data.pipe.transform(v0, v1, v2, v3, v4, v5, v6, v7);
value = pipe.transform(v1, v2, v3, v4, v5, v6, v7);
break;
case 7:
value = data.pipe.transform(v0, v1, v2, v3, v4, v5, v6);
value = pipe.transform(v1, v2, v3, v4, v5, v6);
break;
case 6:
value = data.pipe.transform(v0, v1, v2, v3, v4, v5);
value = pipe.transform(v1, v2, v3, v4, v5);
break;
case 5:
value = data.pipe.transform(v0, v1, v2, v3, v4);
value = pipe.transform(v1, v2, v3, v4);
break;
case 4:
value = data.pipe.transform(v0, v1, v2, v3);
value = pipe.transform(v1, v2, v3);
break;
case 3:
value = data.pipe.transform(v0, v1, v2);
value = pipe.transform(v1, v2);
break;
case 2:
value = data.pipe.transform(v0, v1);
value = pipe.transform(v1);
break;
case 1:
value = data.pipe.transform(v0);
value = pipe.transform(v0);
break;
}
break;
@@ -219,23 +204,18 @@ export function checkAndUpdatePureExpressionDynamic(view: ViewData, def: NodeDef
let value: any;
switch (def.pureExpression.type) {
case PureExpressionType.Array:
value = new Array(values.length);
for (let i = 0; i < values.length; i++) {
value[i] = unwrapValue(values[i]);
}
value = values;
break;
case PureExpressionType.Object:
value = {};
for (let i = 0; i < values.length; i++) {
value[bindings[i].name] = unwrapValue(values[i]);
value[bindings[i].name] = values[i];
}
break;
case PureExpressionType.Pipe:
const params = new Array(values.length);
for (let i = 0; i < values.length; i++) {
params[i] = unwrapValue(values[i]);
}
value = (<any>data.pipe.transform)(...params);
const pipe = values[0];
const params = values.slice(1);
value = (<any>pipe.transform)(...params);
break;
}
data.value = value;
+9 -7
View File
@@ -13,7 +13,7 @@ import {ViewContainerRef} from '../linker/view_container_ref';
import {createTemplateRef, createViewContainerRef} from './refs';
import {NodeDef, NodeFlags, NodeType, QueryBindingDef, QueryBindingType, QueryDef, QueryValueType, Services, ViewData, asElementData, asProviderData, asQueryList} from './types';
import {declaredViewContainer} from './util';
import {declaredViewContainer, viewParentElIndex} from './util';
export function queryDef(
flags: NodeFlags, id: string, bindings: {[propName: string]: QueryBindingType}): NodeDef {
@@ -54,16 +54,18 @@ export function createQuery(): QueryList<any> {
}
export function dirtyParentQuery(queryId: string, view: ViewData) {
let nodeIndex = view.parentIndex;
let elIndex = viewParentElIndex(view);
view = view.parent;
let queryIdx: number;
while (view) {
const elementDef = view.def.nodes[nodeIndex];
queryIdx = elementDef.element.providerIndices[queryId];
if (queryIdx != null) {
break;
if (elIndex != null) {
const elementDef = view.def.nodes[elIndex];
queryIdx = elementDef.element.providerIndices[queryId];
if (queryIdx != null) {
break;
}
}
nodeIndex = view.parentIndex;
elIndex = viewParentElIndex(view);
view = view.parent;
}
if (!view) {
+4 -4
View File
@@ -19,8 +19,8 @@ import {Sanitizer, SecurityContext} from '../security';
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 {isComponentView, renderNode, resolveViewDefinition, rootRenderNodes, tokenKey, viewParentDiIndex} 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 {isComponentView, renderNode, resolveViewDefinition, rootRenderNodes, tokenKey, viewParentElIndex} from './util';
const EMPTY_CONTEXT = new Object();
@@ -53,7 +53,7 @@ class ComponentFactory_ implements ComponentFactory<any> {
const len = viewDef.nodes.length;
for (let i = 0; i < len; i++) {
const nodeDef = viewDef.nodes[i];
if (nodeDef.provider && nodeDef.provider.component) {
if (nodeDef.flags & NodeFlags.HasComponent) {
componentNodeIndex = i;
break;
}
@@ -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 = viewParentDiIndex(view);
elIndex = viewParentElIndex(view);
view = view.parent;
}
return view ? new Injector_(view, elIndex) : this._view.root.injector;
+68 -34
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, NodeFlags, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, asElementData, asProviderData} from './types';
import {checkBinding, isComponentView, queryIdIsReference, renderNode, resolveViewDefinition, rootRenderNodes, viewParentDiIndex} from './util';
import {ArgumentType, BindingType, DebugContext, DepFlags, ElementData, NodeCheckFn, NodeData, NodeDef, NodeFlags, NodeType, RendererV2, RootData, Services, ViewData, ViewDefinition, ViewDefinitionFactory, ViewState, ViewUpdateFn, asElementData, asProviderData} from './types';
import {checkBinding, isComponentView, queryIdIsReference, renderNode, resolveViewDefinition, rootRenderNodes, viewParentElIndex} from './util';
import {checkAndUpdateView, checkNoChangesView, createEmbeddedView, createRootView, destroyView} from './view';
import {attachEmbeddedView, detachEmbeddedView, moveEmbeddedView} from './view_attach';
@@ -44,7 +44,8 @@ export function initServicesIfNeeded() {
Services.resolveDep = services.resolveDep;
Services.createDebugContext = services.createDebugContext;
Services.handleEvent = services.handleEvent;
Services.updateView = services.updateView;
Services.updateDirectives = services.updateDirectives;
Services.updateRenderer = services.updateRenderer;
}
function createProdServices() {
@@ -62,7 +63,9 @@ function createProdServices() {
createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex),
handleEvent: (view: ViewData, nodeIndex: number, eventName: string, event: any) =>
view.def.handleEvent(view, nodeIndex, eventName, event),
updateView: (check: NodeCheckFn, view: ViewData) => view.def.update(check, view)
updateDirectives: (check: NodeCheckFn, view: ViewData) =>
view.def.updateDirectives(check, view),
updateRenderer: (check: NodeCheckFn, view: ViewData) => view.def.updateRenderer(check, view),
};
}
@@ -80,7 +83,8 @@ function createDebugServices() {
resolveDep: resolveDep,
createDebugContext: (view: ViewData, nodeIndex: number) => new DebugContext_(view, nodeIndex),
handleEvent: debugHandleEvent,
updateView: debugUpdateView
updateDirectives: debugUpdateDirectives,
updateRenderer: debugUpdateRenderer
};
}
@@ -152,37 +156,56 @@ function debugHandleEvent(view: ViewData, nodeIndex: number, eventName: string,
'handleEvent', view.def.handleEvent, null, [view, nodeIndex, eventName, event]);
}
function debugUpdateView(check: NodeCheckFn, view: ViewData) {
function debugUpdateDirectives(check: NodeCheckFn, view: ViewData) {
if (view.state & ViewState.Destroyed) {
throw viewDestroyedError(_currentAction);
}
debugSetCurrentNode(view, nextNodeIndexWithBinding(view, 0));
return view.def.update(debugCheckFn, view);
debugSetCurrentNode(view, nextDirectiveWithBinding(view, 0));
return view.def.updateDirectives(debugCheckDirectivesFn, view);
function debugCheckFn(
view: ViewData, nodeIndex: number, argStyle: ArgumentType, v0?: any, v1?: any, v2?: any,
v3?: any, v4?: any, v5?: any, v6?: any, v7?: any, v8?: any, v9?: any) {
const values = argStyle === ArgumentType.Dynamic ? v0 : [].slice.call(arguments, 3);
const nodeDef = view.def.nodes[nodeIndex];
for (let i = 0; i < nodeDef.bindings.length; i++) {
const binding = nodeDef.bindings[i];
const value = values[i];
if ((binding.type === BindingType.ElementProperty ||
binding.type === BindingType.ProviderProperty) &&
checkBinding(view, nodeDef, i, value)) {
const elIndex = nodeDef.type === NodeType.Provider ? nodeDef.parent : nodeDef.index;
setBindingDebugInfo(
view.root.renderer, asElementData(view, elIndex).renderElement, binding.nonMinifiedName,
value);
}
}
const result = check(view, nodeIndex, <any>argStyle, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
debugSetCurrentNode(view, nextNodeIndexWithBinding(view, nodeIndex));
function debugCheckDirectivesFn(
view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) {
const result = debugCheckFn(check, view, nodeIndex, argStyle, values);
debugSetCurrentNode(view, nextDirectiveWithBinding(view, nodeIndex));
return result;
};
}
function debugUpdateRenderer(check: NodeCheckFn, view: ViewData) {
if (view.state & ViewState.Destroyed) {
throw viewDestroyedError(_currentAction);
}
debugSetCurrentNode(view, nextRenderNodeWithBinding(view, 0));
return view.def.updateRenderer(debugCheckRenderNodeFn, view);
function debugCheckRenderNodeFn(
view: ViewData, nodeIndex: number, argStyle: ArgumentType, ...values: any[]) {
const result = debugCheckFn(check, view, nodeIndex, argStyle, values);
debugSetCurrentNode(view, nextRenderNodeWithBinding(view, nodeIndex));
return result;
};
}
function debugCheckFn(
delegate: NodeCheckFn, view: ViewData, nodeIndex: number, argStyle: ArgumentType,
givenValues: any[]) {
const values = argStyle === ArgumentType.Dynamic ? givenValues[0] : givenValues;
const nodeDef = view.def.nodes[nodeIndex];
for (let i = 0; i < nodeDef.bindings.length; i++) {
const binding = nodeDef.bindings[i];
const value = values[i];
if ((binding.type === BindingType.ElementProperty ||
binding.type === BindingType.DirectiveProperty) &&
checkBinding(view, nodeDef, i, value)) {
const elIndex = nodeDef.type === NodeType.Directive ? nodeDef.parent : nodeDef.index;
setBindingDebugInfo(
view.root.renderer, asElementData(view, elIndex).renderElement, binding.nonMinifiedName,
value);
}
}
return (<any>delegate)(view, nodeIndex, argStyle, ...givenValues);
};
function setBindingDebugInfo(renderer: RendererV2, renderNode: any, propName: string, value: any) {
const renderName = `ng-reflect-${camelCaseToDashCase(propName)}`;
if (value) {
@@ -203,16 +226,26 @@ function camelCaseToDashCase(input: string): string {
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
}
function nextNodeIndexWithBinding(view: ViewData, nodeIndex: number): number {
function nextDirectiveWithBinding(view: ViewData, nodeIndex: number): number {
for (let i = nodeIndex; i < view.def.nodes.length; i++) {
const nodeDef = view.def.nodes[i];
if (nodeDef.bindings && nodeDef.bindings.length) {
if (nodeDef.type === NodeType.Directive && nodeDef.bindings && nodeDef.bindings.length) {
return i;
}
}
return undefined;
}
function nextRenderNodeWithBinding(view: ViewData, nodeIndex: number): number {
for (let i = nodeIndex; i < view.def.nodes.length; i++) {
const nodeDef = view.def.nodes[i];
if ((nodeDef.type === NodeType.Element || nodeDef.type === NodeType.Text) && nodeDef.bindings &&
nodeDef.bindings.length) {
return i;
}
}
return undefined;
}
class DebugRenderer implements RendererV2 {
constructor(private _delegate: RendererV2) {}
@@ -282,7 +315,7 @@ class DebugContext_ implements DebugContext {
}
if (elIndex == null) {
while (elIndex == null && elView) {
elIndex = viewParentDiIndex(elView);
elIndex = viewParentElIndex(elView);
elView = elView.parent;
}
}
@@ -320,7 +353,7 @@ class DebugContext_ implements DebugContext {
if (this.elDef) {
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
const childDef = this.elView.def.nodes[i];
if (childDef.type === NodeType.Provider) {
if (childDef.type === NodeType.Provider || childDef.type === NodeType.Directive) {
tokens.push(childDef.provider.token);
}
i += childDef.childCount;
@@ -335,7 +368,7 @@ class DebugContext_ implements DebugContext {
for (let i = this.elDef.index + 1; i <= this.elDef.index + this.elDef.childCount; i++) {
const childDef = this.elView.def.nodes[i];
if (childDef.type === NodeType.Provider) {
if (childDef.type === NodeType.Provider || childDef.type === NodeType.Directive) {
collectReferences(this.elView, childDef, references);
}
i += childDef.childCount;
@@ -368,7 +401,7 @@ function findHostElement(view: ViewData): ElementData {
view = view.parent;
}
if (view.parent) {
const hostData = asElementData(view.parent, view.parentIndex);
const hostData = asElementData(view.parent, viewParentElIndex(view));
return hostData;
}
return undefined;
@@ -397,6 +430,7 @@ function callWithDebugContext(action: string, fn: any, self: any, args: any[]) {
if (isViewDebugError(e) || !_currentView) {
throw e;
}
_currentView.state |= ViewState.Errored;
throw viewWrappedDebugError(e, getCurrentDebugContext());
}
}
+2 -3
View File
@@ -10,7 +10,7 @@ import {isDevMode} from '../application_ref';
import {looseIdentical} from '../facade/lang';
import {BindingDef, BindingType, DebugContext, NodeData, NodeDef, NodeFlags, NodeType, RootData, Services, TextData, ViewData, ViewFlags, asElementData, asTextData} from './types';
import {checkAndUpdateBinding, sliceErrorStack, unwrapValue} from './util';
import {checkAndUpdateBinding, sliceErrorStack} from './util';
export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
// skip the call to sliceErrorStack itself + the call to this function.
@@ -18,7 +18,7 @@ export function textDef(ngContentIndex: number, constants: string[]): NodeDef {
const bindings: BindingDef[] = new Array(constants.length - 1);
for (let i = 1; i < constants.length; i++) {
bindings[i - 1] = {
type: BindingType.Interpolation,
type: BindingType.TextInterpolation,
name: undefined,
nonMinifiedName: undefined,
securityContext: undefined,
@@ -143,7 +143,6 @@ export function checkAndUpdateTextDynamic(view: ViewData, def: NodeDef, values:
}
function _addInterpolationPart(value: any, binding: BindingDef): string {
value = unwrapValue(value);
const valueStr = value != null ? value.toString() : '';
return valueStr + binding.suffix;
}
+15 -17
View File
@@ -23,7 +23,8 @@ import {Sanitizer, SecurityContext} from '../security';
export interface ViewDefinition {
flags: ViewFlags;
component: ComponentDefinition;
update: ViewUpdateFn;
updateDirectives: ViewUpdateFn;
updateRenderer: ViewUpdateFn;
handleEvent: ViewHandleEventFn;
/**
* Order: Depth first.
@@ -124,7 +125,9 @@ export interface NodeDef {
export enum NodeType {
Element,
Text,
Directive,
Provider,
Pipe,
PureExpression,
Query,
NgContent
@@ -163,8 +166,8 @@ export enum BindingType {
ElementClass,
ElementStyle,
ElementProperty,
ProviderProperty,
Interpolation,
DirectiveProperty,
TextInterpolation,
PureExpressionProperty
}
@@ -200,7 +203,7 @@ export interface ProviderDef {
tokenKey: string;
value: any;
deps: DepDef[];
outputs: ProviderOutputDef[];
outputs: DirectiveOutputDef[];
// closure to allow recursive components
component: ViewDefinitionFactory;
}
@@ -228,7 +231,7 @@ export enum DepFlags {
Value = 2 << 2
}
export interface ProviderOutputDef {
export interface DirectiveOutputDef {
propName: string;
eventName: string;
}
@@ -238,10 +241,7 @@ export interface TextDef {
source: string;
}
export interface PureExpressionDef {
type: PureExpressionType;
pipeDep: DepDef;
}
export interface PureExpressionDef { type: PureExpressionType; }
export enum PureExpressionType {
Array,
@@ -285,8 +285,7 @@ export interface NgContentDef {
export interface ViewData {
def: ViewDefinition;
root: RootData;
// index of parent element / anchor. Not the index
// of the provider with the component view.
// index of component provider / anchor.
parentIndex: number;
parent: ViewData;
component: any;
@@ -385,10 +384,7 @@ export function asProviderData(view: ViewData, index: number): ProviderData {
*
* Attention: Adding fields to this is performance sensitive!
*/
export interface PureExpressionData {
value: any;
pipe: PipeTransform;
}
export interface PureExpressionData { value: any; }
/**
* Accessor for view.nodes, enforcing that every usage site stays monomorphic.
@@ -493,7 +489,8 @@ export interface Services {
notFoundValue?: any): any;
createDebugContext(view: ViewData, nodeIndex: number): DebugContext;
handleEvent: ViewHandleEventFn;
updateView: ViewUpdateFn;
updateDirectives: ViewUpdateFn;
updateRenderer: ViewUpdateFn;
}
/**
@@ -513,5 +510,6 @@ export const Services: Services = {
resolveDep: undefined,
createDebugContext: undefined,
handleEvent: undefined,
updateView: undefined,
updateDirectives: undefined,
updateRenderer: undefined,
};
+30 -24
View File
@@ -30,16 +30,28 @@ export function tokenKey(token: any): string {
return key;
}
let unwrapCounter = 0;
export function unwrapValue(value: any): any {
if (value instanceof WrappedValue) {
value = value.wrapped;
unwrapCounter++;
}
return value;
}
export function checkBinding(
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
return !!(view.state & ViewState.FirstCheck) || !devModeEqual(oldValue, value);
return unwrapCounter > 0 || !!(view.state & ViewState.FirstCheck) ||
!devModeEqual(oldValue, value);
}
export function checkBindingNoChanges(
view: ViewData, def: NodeDef, bindingIdx: number, value: any) {
const oldValue = view.oldValues[def.bindingIndex + bindingIdx];
if ((view.state & ViewState.FirstCheck) || !devModeEqual(oldValue, value)) {
if (unwrapCounter || (view.state & ViewState.FirstCheck) || !devModeEqual(oldValue, value)) {
unwrapCounter = 0;
throw expressionChangedAfterItHasBeenCheckedError(
Services.createDebugContext(view, def.index), oldValue, value,
(view.state & ViewState.FirstCheck) !== 0);
@@ -49,15 +61,10 @@ export function checkBindingNoChanges(
export function checkAndUpdateBinding(
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
const oldValues = view.oldValues;
if ((view.state & ViewState.FirstCheck) ||
if (unwrapCounter || (view.state & ViewState.FirstCheck) ||
!looseIdentical(oldValues[def.bindingIndex + bindingIdx], value)) {
unwrapCounter = 0;
oldValues[def.bindingIndex + bindingIdx] = value;
if (def.flags & NodeFlags.HasComponent) {
const compView = asProviderData(view, def.index).componentView;
if (compView.def.flags & ViewFlags.OnPush) {
compView.state |= ViewState.ChecksEnabled;
}
}
return true;
}
return false;
@@ -75,13 +82,6 @@ export function dispatchEvent(
return Services.handleEvent(view, nodeIndex, eventName, event);
}
export function unwrapValue(value: any): any {
if (value instanceof WrappedValue) {
value = value.wrapped;
}
return value;
}
export function declaredViewContainer(view: ViewData): ElementData {
if (view.parent) {
const parentView = view.parent;
@@ -91,16 +91,17 @@ export function declaredViewContainer(view: ViewData): ElementData {
}
/**
* for component views, this is the same as parentIndex.
* for component views, this is the host element.
* for embedded views, this is the index of the parent node
* that contains the view container.
*/
export function viewParentDiIndex(view: ViewData): number {
if (view.parent && view.context !== view.component) {
const parentNodeDef = view.parent.def.nodes[view.parentIndex];
return parentNodeDef.parent;
export function viewParentElIndex(view: ViewData): number {
const parentView = view.parent;
if (parentView) {
return parentView.def.nodes[view.parentIndex].parent;
} else {
return null;
}
return view.parentIndex;
}
export function renderNode(view: ViewData, def: NodeDef): any {
@@ -119,6 +120,8 @@ export function nodeValue(view: ViewData, index: number): any {
return asElementData(view, def.index).renderElement;
case NodeType.Text:
return asTextData(view, def.index).renderText;
case NodeType.Directive:
case NodeType.Pipe:
case NodeType.Provider:
return asProviderData(view, def.index).instance;
}
@@ -183,7 +186,10 @@ export function visitRootRenderNodes(
const len = view.def.nodes.length;
for (let i = 0; i < len; i++) {
const nodeDef = view.def.nodes[i];
visitRenderNode(view, nodeDef, action, parentNode, nextSibling, target);
if (nodeDef.type === NodeType.Element || nodeDef.type === NodeType.Text ||
nodeDef.type === NodeType.NgContent) {
visitRenderNode(view, nodeDef, action, parentNode, nextSibling, target);
}
// jump to next sibling
i += nodeDef.childCount;
}
@@ -197,7 +203,7 @@ export function visitProjectedRenderNodes(
compView = compView.parent;
}
const hostView = compView.parent;
const hostElDef = hostView.def.nodes[compView.parentIndex];
const hostElDef = hostView.def.nodes[viewParentElIndex(compView)];
const startIndex = hostElDef.index + 1;
const endIndex = hostElDef.index + hostElDef.childCount;
for (let i = startIndex; i <= endIndex; i++) {
+43 -25
View File
@@ -11,19 +11,19 @@ import {ViewEncapsulation} from '../metadata/view';
import {checkAndUpdateElementDynamic, checkAndUpdateElementInline, createElement} from './element';
import {expressionChangedAfterItHasBeenCheckedError} from './errors';
import {appendNgContent} from './ng_content';
import {callLifecycleHooksChildrenFirst, checkAndUpdateProviderDynamic, checkAndUpdateProviderInline, createProviderInstance} from './provider';
import {callLifecycleHooksChildrenFirst, checkAndUpdateDirectiveDynamic, checkAndUpdateDirectiveInline, createDirectiveInstance, createPipeInstance, createProviderInstance} from './provider';
import {checkAndUpdatePureExpressionDynamic, checkAndUpdatePureExpressionInline, createPureExpression} from './pure_expression';
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, queryIdIsReference, resolveViewDefinition} from './util';
import {checkBindingNoChanges, isComponentView, queryIdIsReference, resolveViewDefinition, viewParentElIndex} from './util';
const NOOP = (): any => undefined;
export function viewDef(
flags: ViewFlags, nodesWithoutIndices: NodeDef[], update?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn, compId?: string, encapsulation?: ViewEncapsulation,
styles?: string[]): ViewDefinition {
flags: ViewFlags, nodesWithoutIndices: NodeDef[], updateDirectives?: ViewUpdateFn,
updateRenderer?: ViewUpdateFn, handleEvent?: ViewHandleEventFn, compId?: string,
encapsulation?: ViewEncapsulation, styles?: string[]): ViewDefinition {
// clone nodes and set auto calculated values
if (nodesWithoutIndices.length === 0) {
throw new Error(`Illegal State: Views without nodes are not allowed!`);
@@ -83,7 +83,7 @@ export function viewDef(
if (!currentParent) {
lastRootNode = node;
}
if (node.provider) {
if (node.type === NodeType.Provider || node.type === NodeType.Directive) {
currentParent.element.providerIndices[node.provider.tokenKey] = i;
}
if (node.query) {
@@ -108,7 +108,8 @@ export function viewDef(
nodeFlags: viewNodeFlags,
nodeMatchedQueries: viewMatchedQueries, flags,
nodes: nodes, reverseChildNodes,
update: update || NOOP,
updateDirectives: updateDirectives || NOOP,
updateRenderer: updateRenderer || NOOP,
handleEvent: handleEvent || NOOP,
component: componentDef,
bindingCount: viewBindingCount,
@@ -172,18 +173,18 @@ function validateNode(parent: NodeDef, node: NodeDef, nodeCount: number) {
`Illegal State: Last root node of a template can't have embedded views, at index ${node.index}!`);
}
}
if (node.provider) {
if (node.type === NodeType.Provider || node.type === NodeType.Directive) {
const parentType = parent ? parent.type : null;
if (parentType !== NodeType.Element) {
throw new Error(
`Illegal State: Provider nodes need to be children of elements or anchors, at index ${node.index}!`);
`Illegal State: Provider/Directive nodes need to be children of elements or anchors, at index ${node.index}!`);
}
}
if (node.query) {
const parentType = parent ? parent.type : null;
if (parentType !== NodeType.Provider) {
if (parentType !== NodeType.Directive) {
throw new Error(
`Illegal State: Query nodes need to be children of providers, at index ${node.index}!`);
`Illegal State: Query nodes need to be children of directives, at index ${node.index}!`);
}
}
if (node.childCount) {
@@ -282,7 +283,7 @@ function initView(view: ViewData, component: any, context: any) {
function createViewNodes(view: ViewData) {
let renderHost: any;
if (isComponentView(view)) {
renderHost = asElementData(view.parent, view.parentIndex).renderElement;
renderHost = asElementData(view.parent, viewParentElIndex(view)).renderElement;
}
const def = view.def;
@@ -297,23 +298,36 @@ function createViewNodes(view: ViewData) {
case NodeType.Text:
nodes[i] = createText(view, renderHost, nodeDef) as any;
break;
case NodeType.Provider:
if (nodeDef.provider.component) {
case NodeType.Provider: {
const instance = createProviderInstance(view, nodeDef);
const providerData = <ProviderData>{componentView: undefined, instance};
nodes[i] = providerData as any;
break;
}
case NodeType.Pipe: {
const instance = createPipeInstance(view, nodeDef);
const providerData = <ProviderData>{componentView: undefined, instance};
nodes[i] = providerData as any;
break;
}
case NodeType.Directive: {
if (nodeDef.flags & NodeFlags.HasComponent) {
// Components can inject a ChangeDetectorRef that needs a references to
// the component view. Therefore, we create the component view first
// and set the ProviderData in ViewData, and then instantiate the provider.
const componentView = createView(
view.root, view, nodeDef.parent, resolveViewDefinition(nodeDef.provider.component));
view.root, view, nodeDef.index, resolveViewDefinition(nodeDef.provider.component));
const providerData = <ProviderData>{componentView, instance: undefined};
nodes[i] = providerData as any;
const instance = providerData.instance = createProviderInstance(view, nodeDef);
const instance = providerData.instance = createDirectiveInstance(view, nodeDef);
initView(componentView, instance, instance);
} else {
const instance = createProviderInstance(view, nodeDef);
const instance = createDirectiveInstance(view, nodeDef);
const providerData = <ProviderData>{componentView: undefined, instance};
nodes[i] = providerData as any;
}
break;
}
case NodeType.PureExpression:
nodes[i] = createPureExpression(view, nodeDef) as any;
break;
@@ -333,21 +347,25 @@ function createViewNodes(view: ViewData) {
}
export function checkNoChangesView(view: ViewData) {
Services.updateView(checkNoChangesNode, view);
Services.updateDirectives(checkNoChangesNode, view);
execEmbeddedViewsAction(view, ViewAction.CheckNoChanges);
execQueriesAction(view, NodeFlags.HasContentQuery, QueryAction.CheckNoChanges);
Services.updateRenderer(checkNoChangesNode, view);
execComponentViewsAction(view, ViewAction.CheckNoChanges);
execQueriesAction(view, NodeFlags.HasViewQuery, QueryAction.CheckNoChanges);
}
export function checkAndUpdateView(view: ViewData) {
Services.updateView(checkAndUpdateNode, view);
Services.updateDirectives(checkAndUpdateNode, view);
execEmbeddedViewsAction(view, ViewAction.CheckAndUpdate);
execQueriesAction(view, NodeFlags.HasContentQuery, QueryAction.CheckAndUpdate);
callLifecycleHooksChildrenFirst(
view, NodeFlags.AfterContentChecked |
(view.state & ViewState.FirstCheck ? NodeFlags.AfterContentInit : 0));
Services.updateRenderer(checkAndUpdateNode, view);
execComponentViewsAction(view, ViewAction.CheckAndUpdate);
execQueriesAction(view, NodeFlags.HasViewQuery, QueryAction.CheckAndUpdate);
@@ -380,8 +398,8 @@ function checkAndUpdateNodeInline(
return checkAndUpdateElementInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
case NodeType.Text:
return checkAndUpdateTextInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
case NodeType.Provider:
return checkAndUpdateProviderInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
case NodeType.Directive:
return checkAndUpdateDirectiveInline(view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
case NodeType.PureExpression:
return checkAndUpdatePureExpressionInline(
view, nodeDef, v0, v1, v2, v3, v4, v5, v6, v7, v8, v9);
@@ -395,8 +413,8 @@ function checkAndUpdateNodeDynamic(view: ViewData, nodeIndex: number, values: an
return checkAndUpdateElementDynamic(view, nodeDef, values);
case NodeType.Text:
return checkAndUpdateTextDynamic(view, nodeDef, values);
case NodeType.Provider:
return checkAndUpdateProviderDynamic(view, nodeDef, values);
case NodeType.Directive:
return checkAndUpdateDirectiveDynamic(view, nodeDef, values);
case NodeType.PureExpression:
return checkAndUpdatePureExpressionDynamic(view, nodeDef, values);
}
@@ -462,14 +480,14 @@ function checkNoChangesQuery(view: ViewData, nodeDef: NodeDef) {
}
export function destroyView(view: ViewData) {
execEmbeddedViewsAction(view, ViewAction.Destroy);
execComponentViewsAction(view, ViewAction.Destroy);
callLifecycleHooksChildrenFirst(view, NodeFlags.OnDestroy);
if (view.disposables) {
for (let i = 0; i < view.disposables.length; i++) {
view.disposables[i]();
}
}
execComponentViewsAction(view, ViewAction.Destroy);
execEmbeddedViewsAction(view, ViewAction.Destroy);
view.state |= ViewState.Destroyed;
}
@@ -6,6 +6,7 @@
* found in the LICENSE file at https://angular.io/license
*/
import {USE_VIEW_ENGINE} from '@angular/compiler/src/config';
import {ElementSchemaRegistry} from '@angular/compiler/src/schema/element_schema_registry';
import {TEST_COMPILER_PROVIDERS} from '@angular/compiler/testing/test_bindings';
import {AfterContentChecked, AfterContentInit, AfterViewChecked, AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, DebugElement, Directive, DoCheck, Injectable, Input, OnChanges, OnDestroy, OnInit, Output, Pipe, PipeTransform, RenderComponentType, Renderer, RootRenderer, SimpleChange, SimpleChanges, TemplateRef, Type, ViewChild, ViewContainerRef, WrappedValue} from '@angular/core';
@@ -19,6 +20,19 @@ import {MockSchemaRegistry} from '../../../compiler/testing/index';
import {EventEmitter} from '../../src/facade/async';
export function main() {
describe('Current compiler', () => { createTests({viewEngine: false}); });
describe('View Engine compiler', () => {
beforeEach(() => {
TestBed.configureCompiler(
{useJit: true, providers: [{provide: USE_VIEW_ENGINE, useValue: true}]});
});
createTests({viewEngine: true});
});
}
function createTests({viewEngine}: {viewEngine: boolean}) {
let elSchema: MockSchemaRegistry;
let renderLog: RenderLog;
let directiveLog: DirectiveLog;
@@ -1080,7 +1094,8 @@ export function main() {
ctx.destroy();
expect(directiveLog.filter(['ngOnDestroy'])).toEqual([
// We don't care about the exact order in this test.
expect(directiveLog.filter(['ngOnDestroy']).sort()).toEqual([
'dir.ngOnDestroy', 'injectable.ngOnDestroy'
]);
}));
@@ -1092,8 +1107,9 @@ export function main() {
it('should throw when a record gets changed after it has been checked', fakeAsync(() => {
const ctx = createCompFixture('<div [someProp]="a"></div>', TestData);
ctx.componentInstance.a = 1;
expect(() => ctx.checkNoChanges())
.toThrowError(/:0:5[\s\S]*Expression has changed after it was checked./g);
.toThrowError(/Expression has changed after it was checked./g);
}));
it('should warn when the view has been created in a cd hook', fakeAsync(() => {
@@ -1216,26 +1232,28 @@ export function main() {
expect(renderLog.loggedValues).toEqual(['Tom']);
});
it('should recurse into nested view containers even if there are no bindings in the component view',
() => {
@Component({template: '<template #vc>{{name}}</template>'})
class Comp {
name = 'Tom';
@ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
@ViewChild(TemplateRef) template: TemplateRef<any>;
}
// TODO(tbosch): ViewQueries don't work yet with the view engine...
viewEngine ||
it('should recurse into nested view containers even if there are no bindings in the component view',
() => {
@Component({template: '<template #vc>{{name}}</template>'})
class Comp {
name = 'Tom';
@ViewChild('vc', {read: ViewContainerRef}) vc: ViewContainerRef;
@ViewChild(TemplateRef) template: TemplateRef<any>;
}
TestBed.configureTestingModule({declarations: [Comp]});
initHelpers();
TestBed.configureTestingModule({declarations: [Comp]});
initHelpers();
const ctx = TestBed.createComponent(Comp);
ctx.detectChanges();
expect(renderLog.loggedValues).toEqual([]);
const ctx = TestBed.createComponent(Comp);
ctx.detectChanges();
expect(renderLog.loggedValues).toEqual([]);
ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template);
ctx.detectChanges();
expect(renderLog.loggedValues).toEqual(['Tom']);
});
ctx.componentInstance.vc.createEmbeddedView(ctx.componentInstance.template);
ctx.detectChanges();
expect(renderLog.loggedValues).toEqual(['Tom']);
});
});
});
}
@@ -265,7 +265,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
});
describe('pipes', () => {
viewEngine || it('should support pipes in bindings', () => {
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}});
@@ -545,7 +545,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
});
describe('variables', () => {
viewEngine || it('should allow to use variables in a for loop', () => {
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>';
@@ -670,31 +670,29 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
});
if (getDOM().supportsDOMEvents()) {
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);
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);
}));
}
});
@@ -897,26 +895,24 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
expect(tc.nativeElement.id).toEqual('newId');
});
viewEngine ||
it('should not use template variables for expressions in hostProperties', () => {
@Directive(
{selector: '[host-properties]', host: {'[id]': 'id', '[title]': 'unknownProp'}})
class DirectiveWithHostProps {
id = 'one';
}
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'}})
@@ -930,8 +926,7 @@ function declareTests({useJit, viewEngine}: {useJit: boolean, viewEngine: boolea
.toThrowError(/Host binding expression cannot contain pipes/);
});
// TODO: literal array
viewEngine || it('should not use template variables for expressions in hostListeners', () => {
it('should not use template variables for expressions in hostListeners', () => {
@Directive({selector: '[host-listener]', host: {'(click)': 'doIt(id, unknownProp)'}})
class DirectiveWithHostListener {
id = 'one';
@@ -16,8 +16,9 @@ import {createRootView, isBrowser} from './helper';
export function main() {
describe(`View Anchor`, () => {
function compViewDef(
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn): ViewDefinition {
return viewDef(ViewFlags.None, nodes, update, handleEvent);
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn): ViewDefinition {
return viewDef(ViewFlags.None, nodes, updateDirectives, updateRenderer, handleEvent);
}
function createAndGetRootNodes(
@@ -16,9 +16,9 @@ import {createRootView, isBrowser, removeNodes} from './helper';
export function main() {
describe(`Component Views`, () => {
function compViewDef(
nodes: NodeDef[], update?: ViewUpdateFn, handleEvent?: ViewHandleEventFn,
viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, update, handleEvent);
nodes: NodeDef[], updateDirectives?: ViewUpdateFn, updateRenderer?: ViewUpdateFn,
handleEvent?: ViewHandleEventFn, viewFlags: ViewFlags = ViewFlags.None): ViewDefinition {
return viewDef(viewFlags, nodes, updateDirectives, updateRenderer, handleEvent);
}
function createAndGetRootNodes(viewDef: ViewDefinition): {rootNodes: any[], view: ViewData} {
@@ -119,7 +119,7 @@ export function main() {
directiveDef(NodeFlags.None, null, 0, AComp, [], null, null, () => compViewDef(
[
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
], update
], null, update
)),
]));
const compView = asProviderData(view, 1).componentView;
@@ -194,7 +194,7 @@ export function main() {
[
elementDef(NodeFlags.None, null, null, 0, 'span', null, null, ['click']),
],
update, null, ViewFlags.OnPush)),
update, null, null, ViewFlags.OnPush)),
],
(check, view) => { check(view, 1, ArgumentType.Inline, compInputValue); }));
@@ -246,7 +246,7 @@ export function main() {
[
elementDef(NodeFlags.None, null, null, 0, 'span', null, [[BindingType.ElementAttribute, 'a', SecurityContext.NONE]]),
],
update)),
null, update)),
]));
const compView = asProviderData(view, 1).componentView;

Some files were not shown because too many files have changed in this diff Show More