refactor(compiler): remove a dependency from the IVY AST to the template AST (#23476)
PR Close #23476
This commit is contained in:
parent
0b47902ad7
commit
6761a64522
|
@ -6,7 +6,8 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {SecurityContext} from '../core';
|
||||||
|
import {ParseSourceSpan} from '../parse_util';
|
||||||
|
|
||||||
export class ParserError {
|
export class ParserError {
|
||||||
public message: string;
|
public message: string;
|
||||||
|
@ -663,3 +664,63 @@ export function visitAstChildren(ast: AST, visitor: AstVisitor, context?: any) {
|
||||||
visitSafePropertyRead(ast) { visit(ast.receiver); },
|
visitSafePropertyRead(ast) { visit(ast.receiver); },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Bindings
|
||||||
|
|
||||||
|
export class ParsedProperty {
|
||||||
|
public readonly isLiteral: boolean;
|
||||||
|
public readonly isAnimation: boolean;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public name: string, public expression: ASTWithSource, public type: ParsedPropertyType,
|
||||||
|
public sourceSpan: ParseSourceSpan) {
|
||||||
|
this.isLiteral = this.type === ParsedPropertyType.LITERAL_ATTR;
|
||||||
|
this.isAnimation = this.type === ParsedPropertyType.ANIMATION;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export enum ParsedPropertyType {
|
||||||
|
DEFAULT,
|
||||||
|
LITERAL_ATTR,
|
||||||
|
ANIMATION
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum ParsedEventType {
|
||||||
|
// DOM or Directive event
|
||||||
|
Regular,
|
||||||
|
// Animation specific event
|
||||||
|
Animation,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ParsedEvent {
|
||||||
|
// Regular events have a target
|
||||||
|
// Animation events have a phase
|
||||||
|
constructor(
|
||||||
|
public name: string, public targetOrPhase: string, public type: ParsedEventType,
|
||||||
|
public handler: AST, public sourceSpan: ParseSourceSpan) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ParsedVariable {
|
||||||
|
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const enum BoundElementBindingType {
|
||||||
|
// A regular binding to a property (e.g. `[property]="expression"`).
|
||||||
|
Property,
|
||||||
|
// A binding to an element attribute (e.g. `[attr.name]="expression"`).
|
||||||
|
Attribute,
|
||||||
|
// A binding to a CSS class (e.g. `[class.name]="condition"`).
|
||||||
|
Class,
|
||||||
|
// A binding to a style rule (e.g. `[style.rule]="expression"`).
|
||||||
|
Style,
|
||||||
|
// A binding to an animation reference (e.g. `[animate.key]="expression"`).
|
||||||
|
Animation,
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BoundElementProperty {
|
||||||
|
constructor(
|
||||||
|
public name: string, public type: BoundElementBindingType,
|
||||||
|
public securityContext: SecurityContext, public value: AST, public unit: string|null,
|
||||||
|
public sourceSpan: ParseSourceSpan) {}
|
||||||
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {SecurityContext} from '../core';
|
import {SecurityContext} from '../core';
|
||||||
import {AST} from '../expression_parser/ast';
|
import {AST, BoundElementBindingType, BoundElementProperty, ParsedEvent, ParsedEventType} from '../expression_parser/ast';
|
||||||
import {ParseSourceSpan} from '../parse_util';
|
import {ParseSourceSpan} from '../parse_util';
|
||||||
|
|
||||||
export interface Node {
|
export interface Node {
|
||||||
|
@ -32,42 +32,17 @@ export class TextAttribute implements Node {
|
||||||
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitAttribute(this); }
|
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitAttribute(this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Enumeration of types of property bindings.
|
|
||||||
*/
|
|
||||||
export enum PropertyBindingType {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A normal binding to a property (e.g. `[property]="expression"`).
|
|
||||||
*/
|
|
||||||
Property,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A binding to an element attribute (e.g. `[attr.name]="expression"`).
|
|
||||||
*/
|
|
||||||
Attribute,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A binding to a CSS class (e.g. `[class.name]="condition"`).
|
|
||||||
*/
|
|
||||||
Class,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A binding to a style rule (e.g. `[style.rule]="expression"`).
|
|
||||||
*/
|
|
||||||
Style,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A binding to an animation reference (e.g. `[animate.key]="expression"`).
|
|
||||||
*/
|
|
||||||
Animation
|
|
||||||
}
|
|
||||||
|
|
||||||
export class BoundAttribute implements Node {
|
export class BoundAttribute implements Node {
|
||||||
constructor(
|
constructor(
|
||||||
public name: string, public type: PropertyBindingType,
|
public name: string, public type: BoundElementBindingType,
|
||||||
public securityContext: SecurityContext, public value: AST, public unit: string|null,
|
public securityContext: SecurityContext, public value: AST, public unit: string|null,
|
||||||
public sourceSpan: ParseSourceSpan) {}
|
public sourceSpan: ParseSourceSpan) {}
|
||||||
|
|
||||||
|
static fromBoundElementProperty(prop: BoundElementProperty) {
|
||||||
|
return new BoundAttribute(
|
||||||
|
prop.name, prop.type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitBoundAttribute(this); }
|
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitBoundAttribute(this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -75,6 +50,14 @@ export class BoundEvent implements Node {
|
||||||
constructor(
|
constructor(
|
||||||
public name: string, public handler: AST, public target: string|null,
|
public name: string, public handler: AST, public target: string|null,
|
||||||
public phase: string|null, public sourceSpan: ParseSourceSpan) {}
|
public phase: string|null, public sourceSpan: ParseSourceSpan) {}
|
||||||
|
|
||||||
|
static fromParsedEvent(event: ParsedEvent) {
|
||||||
|
const target: string|null = event.type === ParsedEventType.Regular ? event.targetOrPhase : null;
|
||||||
|
const phase: string|null =
|
||||||
|
event.type === ParsedEventType.Animation ? event.targetOrPhase : null;
|
||||||
|
return new BoundEvent(event.name, event.handler, target, phase, event.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitBoundEvent(this); }
|
visit<Result>(visitor: Visitor<Result>): Result { return visitor.visitBoundEvent(this); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -6,14 +6,13 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {ParsedEvent, ParsedProperty, ParsedVariable} from '../expression_parser/ast';
|
||||||
import * as html from '../ml_parser/ast';
|
import * as html from '../ml_parser/ast';
|
||||||
import {replaceNgsp} from '../ml_parser/html_whitespaces';
|
import {replaceNgsp} from '../ml_parser/html_whitespaces';
|
||||||
import {isNgTemplate} from '../ml_parser/tags';
|
import {isNgTemplate} from '../ml_parser/tags';
|
||||||
import {ParseError, ParseErrorLevel, ParseSourceSpan} from '../parse_util';
|
import {ParseError, ParseErrorLevel, ParseSourceSpan} from '../parse_util';
|
||||||
import {isStyleUrlResolvable} from '../style_url_resolver';
|
import {isStyleUrlResolvable} from '../style_url_resolver';
|
||||||
import {BindingParser, BoundProperty} from '../template_parser/binding_parser';
|
import {BindingParser} from '../template_parser/binding_parser';
|
||||||
// TODO(chuckj): Refactor binding parser to not have a dependency on template_ast.
|
|
||||||
import {BoundEventAst, VariableAst} from '../template_parser/template_ast';
|
|
||||||
import {PreparsedElementType, preparseElement} from '../template_parser/template_preparser';
|
import {PreparsedElementType, preparseElement} from '../template_parser/template_preparser';
|
||||||
|
|
||||||
import * as t from './r3_ast';
|
import * as t from './r3_ast';
|
||||||
|
@ -78,7 +77,7 @@ export class HtmlToTemplateTransform implements html.Visitor {
|
||||||
const isTemplateElement = isNgTemplate(element.name);
|
const isTemplateElement = isNgTemplate(element.name);
|
||||||
|
|
||||||
const matchableAttributes: [string, string][] = [];
|
const matchableAttributes: [string, string][] = [];
|
||||||
const boundProperties: BoundProperty[] = [];
|
const parsedProperties: ParsedProperty[] = [];
|
||||||
const boundEvents: t.BoundEvent[] = [];
|
const boundEvents: t.BoundEvent[] = [];
|
||||||
const variables: t.Variable[] = [];
|
const variables: t.Variable[] = [];
|
||||||
const references: t.Reference[] = [];
|
const references: t.Reference[] = [];
|
||||||
|
@ -86,7 +85,7 @@ export class HtmlToTemplateTransform implements html.Visitor {
|
||||||
|
|
||||||
const templateMatchableAttributes: [string, string][] = [];
|
const templateMatchableAttributes: [string, string][] = [];
|
||||||
let inlineTemplateSourceSpan: ParseSourceSpan;
|
let inlineTemplateSourceSpan: ParseSourceSpan;
|
||||||
const templateBoundProperties: BoundProperty[] = [];
|
const templateParsedProperties: ParsedProperty[] = [];
|
||||||
const templateVariables: t.Variable[] = [];
|
const templateVariables: t.Variable[] = [];
|
||||||
|
|
||||||
// Whether the element has any *-attribute
|
// Whether the element has any *-attribute
|
||||||
|
@ -110,20 +109,15 @@ export class HtmlToTemplateTransform implements html.Visitor {
|
||||||
const templateValue = attribute.value;
|
const templateValue = attribute.value;
|
||||||
const templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length);
|
const templateKey = normalizedName.substring(TEMPLATE_ATTR_PREFIX.length);
|
||||||
|
|
||||||
const oldVariables: VariableAst[] = [];
|
|
||||||
|
|
||||||
inlineTemplateSourceSpan = attribute.valueSpan || attribute.sourceSpan;
|
inlineTemplateSourceSpan = attribute.valueSpan || attribute.sourceSpan;
|
||||||
|
|
||||||
this.bindingParser.parseInlineTemplateBinding(
|
this.bindingParser.parseInlineTemplateBinding(
|
||||||
templateKey, templateValue, attribute.sourceSpan, templateMatchableAttributes,
|
templateKey, templateValue, attribute.sourceSpan, templateMatchableAttributes,
|
||||||
templateBoundProperties, oldVariables);
|
templateParsedProperties, templateVariables);
|
||||||
|
|
||||||
templateVariables.push(
|
|
||||||
...oldVariables.map(v => new t.Variable(v.name, v.value, v.sourceSpan)));
|
|
||||||
} else {
|
} else {
|
||||||
// Check for variables, events, property bindings, interpolation
|
// Check for variables, events, property bindings, interpolation
|
||||||
hasBinding = this.parseAttribute(
|
hasBinding = this.parseAttribute(
|
||||||
isTemplateElement, attribute, matchableAttributes, boundProperties, boundEvents,
|
isTemplateElement, attribute, matchableAttributes, parsedProperties, boundEvents,
|
||||||
variables, references);
|
variables, references);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -158,12 +152,12 @@ export class HtmlToTemplateTransform implements html.Visitor {
|
||||||
parsedElement = new t.Content(selectorIndex, attributes, element.sourceSpan);
|
parsedElement = new t.Content(selectorIndex, attributes, element.sourceSpan);
|
||||||
} else if (isTemplateElement) {
|
} else if (isTemplateElement) {
|
||||||
// `<ng-template>`
|
// `<ng-template>`
|
||||||
const boundAttributes = this.createBoundAttributes(element.name, boundProperties);
|
const boundAttributes = this.createBoundAttributes(element.name, parsedProperties);
|
||||||
parsedElement = new t.Template(
|
parsedElement = new t.Template(
|
||||||
attributes, boundAttributes, children, references, variables, element.sourceSpan,
|
attributes, boundAttributes, children, references, variables, element.sourceSpan,
|
||||||
element.startSourceSpan, element.endSourceSpan);
|
element.startSourceSpan, element.endSourceSpan);
|
||||||
} else {
|
} else {
|
||||||
const boundAttributes = this.createBoundAttributes(element.name, boundProperties);
|
const boundAttributes = this.createBoundAttributes(element.name, parsedProperties);
|
||||||
|
|
||||||
parsedElement = new t.Element(
|
parsedElement = new t.Element(
|
||||||
element.name, attributes, boundAttributes, boundEvents, children, references,
|
element.name, attributes, boundAttributes, boundEvents, children, references,
|
||||||
|
@ -177,7 +171,7 @@ export class HtmlToTemplateTransform implements html.Visitor {
|
||||||
([name, value]) =>
|
([name, value]) =>
|
||||||
attributes.push(new t.TextAttribute(name, value, inlineTemplateSourceSpan)));
|
attributes.push(new t.TextAttribute(name, value, inlineTemplateSourceSpan)));
|
||||||
|
|
||||||
const boundAttributes = this.createBoundAttributes('ng-template', templateBoundProperties);
|
const boundAttributes = this.createBoundAttributes('ng-template', templateParsedProperties);
|
||||||
parsedElement = new t.Template(
|
parsedElement = new t.Template(
|
||||||
attributes, boundAttributes, [parsedElement], [], templateVariables, element.sourceSpan,
|
attributes, boundAttributes, [parsedElement], [], templateVariables, element.sourceSpan,
|
||||||
element.startSourceSpan, element.endSourceSpan);
|
element.startSourceSpan, element.endSourceSpan);
|
||||||
|
@ -202,22 +196,16 @@ export class HtmlToTemplateTransform implements html.Visitor {
|
||||||
|
|
||||||
visitExpansionCase(expansionCase: html.ExpansionCase): null { return null; }
|
visitExpansionCase(expansionCase: html.ExpansionCase): null { return null; }
|
||||||
|
|
||||||
private createBoundAttributes(elementName: string, boundProperties: BoundProperty[]):
|
private createBoundAttributes(elementName: string, properties: ParsedProperty[]):
|
||||||
t.BoundAttribute[] {
|
t.BoundAttribute[] {
|
||||||
const literalProperties = boundProperties.filter(prop => !prop.isLiteral);
|
return properties.filter(prop => !prop.isLiteral)
|
||||||
|
.map(prop => this.bindingParser.createBoundElementProperty(elementName, prop))
|
||||||
return literalProperties.map(property => {
|
.map(prop => t.BoundAttribute.fromBoundElementProperty(prop));
|
||||||
// TODO(vicb): get ride of the boundProperty (from TemplateAst)
|
|
||||||
const boundProp = this.bindingParser.createElementPropertyAst(elementName, property);
|
|
||||||
return new t.BoundAttribute(
|
|
||||||
boundProp.name, boundProp.type as any as t.PropertyBindingType, boundProp.securityContext,
|
|
||||||
boundProp.value, boundProp.unit, boundProp.sourceSpan);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private parseAttribute(
|
private parseAttribute(
|
||||||
isTemplateElement: boolean, attribute: html.Attribute, matchableAttributes: string[][],
|
isTemplateElement: boolean, attribute: html.Attribute, matchableAttributes: string[][],
|
||||||
boundProperties: BoundProperty[], boundEvents: t.BoundEvent[], variables: t.Variable[],
|
parsedProperties: ParsedProperty[], boundEvents: t.BoundEvent[], variables: t.Variable[],
|
||||||
references: t.Reference[]) {
|
references: t.Reference[]) {
|
||||||
const name = normalizeAttributeName(attribute.name);
|
const name = normalizeAttributeName(attribute.name);
|
||||||
const value = attribute.value;
|
const value = attribute.value;
|
||||||
|
@ -230,7 +218,7 @@ export class HtmlToTemplateTransform implements html.Visitor {
|
||||||
hasBinding = true;
|
hasBinding = true;
|
||||||
if (bindParts[KW_BIND_IDX] != null) {
|
if (bindParts[KW_BIND_IDX] != null) {
|
||||||
this.bindingParser.parsePropertyBinding(
|
this.bindingParser.parsePropertyBinding(
|
||||||
bindParts[IDENT_KW_IDX], value, false, srcSpan, matchableAttributes, boundProperties);
|
bindParts[IDENT_KW_IDX], value, false, srcSpan, matchableAttributes, parsedProperties);
|
||||||
|
|
||||||
} else if (bindParts[KW_LET_IDX]) {
|
} else if (bindParts[KW_LET_IDX]) {
|
||||||
if (isTemplateElement) {
|
if (isTemplateElement) {
|
||||||
|
@ -245,40 +233,40 @@ export class HtmlToTemplateTransform implements html.Visitor {
|
||||||
this.parseReference(identifier, value, srcSpan, references);
|
this.parseReference(identifier, value, srcSpan, references);
|
||||||
|
|
||||||
} else if (bindParts[KW_ON_IDX]) {
|
} else if (bindParts[KW_ON_IDX]) {
|
||||||
const events: BoundEventAst[] = [];
|
const events: ParsedEvent[] = [];
|
||||||
this.bindingParser.parseEvent(
|
this.bindingParser.parseEvent(
|
||||||
bindParts[IDENT_KW_IDX], value, srcSpan, matchableAttributes, events);
|
bindParts[IDENT_KW_IDX], value, srcSpan, matchableAttributes, events);
|
||||||
addEvents(events, boundEvents);
|
addEvents(events, boundEvents);
|
||||||
} else if (bindParts[KW_BINDON_IDX]) {
|
} else if (bindParts[KW_BINDON_IDX]) {
|
||||||
this.bindingParser.parsePropertyBinding(
|
this.bindingParser.parsePropertyBinding(
|
||||||
bindParts[IDENT_KW_IDX], value, false, srcSpan, matchableAttributes, boundProperties);
|
bindParts[IDENT_KW_IDX], value, false, srcSpan, matchableAttributes, parsedProperties);
|
||||||
this.parseAssignmentEvent(
|
this.parseAssignmentEvent(
|
||||||
bindParts[IDENT_KW_IDX], value, srcSpan, matchableAttributes, boundEvents);
|
bindParts[IDENT_KW_IDX], value, srcSpan, matchableAttributes, boundEvents);
|
||||||
} else if (bindParts[KW_AT_IDX]) {
|
} else if (bindParts[KW_AT_IDX]) {
|
||||||
this.bindingParser.parseLiteralAttr(
|
this.bindingParser.parseLiteralAttr(
|
||||||
name, value, srcSpan, matchableAttributes, boundProperties);
|
name, value, srcSpan, matchableAttributes, parsedProperties);
|
||||||
|
|
||||||
} else if (bindParts[IDENT_BANANA_BOX_IDX]) {
|
} else if (bindParts[IDENT_BANANA_BOX_IDX]) {
|
||||||
this.bindingParser.parsePropertyBinding(
|
this.bindingParser.parsePropertyBinding(
|
||||||
bindParts[IDENT_BANANA_BOX_IDX], value, false, srcSpan, matchableAttributes,
|
bindParts[IDENT_BANANA_BOX_IDX], value, false, srcSpan, matchableAttributes,
|
||||||
boundProperties);
|
parsedProperties);
|
||||||
this.parseAssignmentEvent(
|
this.parseAssignmentEvent(
|
||||||
bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, matchableAttributes, boundEvents);
|
bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, matchableAttributes, boundEvents);
|
||||||
|
|
||||||
} else if (bindParts[IDENT_PROPERTY_IDX]) {
|
} else if (bindParts[IDENT_PROPERTY_IDX]) {
|
||||||
this.bindingParser.parsePropertyBinding(
|
this.bindingParser.parsePropertyBinding(
|
||||||
bindParts[IDENT_PROPERTY_IDX], value, false, srcSpan, matchableAttributes,
|
bindParts[IDENT_PROPERTY_IDX], value, false, srcSpan, matchableAttributes,
|
||||||
boundProperties);
|
parsedProperties);
|
||||||
|
|
||||||
} else if (bindParts[IDENT_EVENT_IDX]) {
|
} else if (bindParts[IDENT_EVENT_IDX]) {
|
||||||
const events: BoundEventAst[] = [];
|
const events: ParsedEvent[] = [];
|
||||||
this.bindingParser.parseEvent(
|
this.bindingParser.parseEvent(
|
||||||
bindParts[IDENT_EVENT_IDX], value, srcSpan, matchableAttributes, events);
|
bindParts[IDENT_EVENT_IDX], value, srcSpan, matchableAttributes, events);
|
||||||
addEvents(events, boundEvents);
|
addEvents(events, boundEvents);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
hasBinding = this.bindingParser.parsePropertyInterpolation(
|
hasBinding = this.bindingParser.parsePropertyInterpolation(
|
||||||
name, value, srcSpan, matchableAttributes, boundProperties);
|
name, value, srcSpan, matchableAttributes, parsedProperties);
|
||||||
}
|
}
|
||||||
|
|
||||||
return hasBinding;
|
return hasBinding;
|
||||||
|
@ -305,7 +293,7 @@ export class HtmlToTemplateTransform implements html.Visitor {
|
||||||
private parseAssignmentEvent(
|
private parseAssignmentEvent(
|
||||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||||
targetMatchableAttrs: string[][], boundEvents: t.BoundEvent[]) {
|
targetMatchableAttrs: string[][], boundEvents: t.BoundEvent[]) {
|
||||||
const events: BoundEventAst[] = [];
|
const events: ParsedEvent[] = [];
|
||||||
this.bindingParser.parseEvent(
|
this.bindingParser.parseEvent(
|
||||||
`${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, events);
|
`${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, events);
|
||||||
addEvents(events, boundEvents);
|
addEvents(events, boundEvents);
|
||||||
|
@ -356,9 +344,8 @@ function normalizeAttributeName(attrName: string): string {
|
||||||
return /^data-/i.test(attrName) ? attrName.substring(5) : attrName;
|
return /^data-/i.test(attrName) ? attrName.substring(5) : attrName;
|
||||||
}
|
}
|
||||||
|
|
||||||
function addEvents(events: BoundEventAst[], boundEvents: t.BoundEvent[]) {
|
function addEvents(events: ParsedEvent[], boundEvents: t.BoundEvent[]) {
|
||||||
boundEvents.push(
|
boundEvents.push(...events.map(e => t.BoundEvent.fromParsedEvent(e)));
|
||||||
...events.map(e => new t.BoundEvent(e.name, e.handler, e.target, e.phase, e.sourceSpan)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function isEmptyTextNode(node: html.Node): boolean {
|
function isEmptyTextNode(node: html.Node): boolean {
|
||||||
|
|
|
@ -12,6 +12,7 @@ import {BindingForm, BuiltinFunctionCall, LocalResolver, convertActionBinding, c
|
||||||
import {ConstantPool, DefinitionKind} from '../constant_pool';
|
import {ConstantPool, DefinitionKind} from '../constant_pool';
|
||||||
import * as core from '../core';
|
import * as core from '../core';
|
||||||
import {AST, AstMemoryEfficientTransformer, BindingPipe, FunctionCall, ImplicitReceiver, LiteralArray, LiteralMap, LiteralPrimitive, PropertyRead} from '../expression_parser/ast';
|
import {AST, AstMemoryEfficientTransformer, BindingPipe, FunctionCall, ImplicitReceiver, LiteralArray, LiteralMap, LiteralPrimitive, PropertyRead} from '../expression_parser/ast';
|
||||||
|
import {BoundElementBindingType} from '../expression_parser/ast';
|
||||||
import {Identifiers} from '../identifiers';
|
import {Identifiers} from '../identifiers';
|
||||||
import {LifecycleHooks} from '../lifecycle_reflector';
|
import {LifecycleHooks} from '../lifecycle_reflector';
|
||||||
import * as o from '../output/output_ast';
|
import * as o from '../output/output_ast';
|
||||||
|
@ -24,6 +25,7 @@ import * as t from './r3_ast';
|
||||||
import {Identifiers as R3} from './r3_identifiers';
|
import {Identifiers as R3} from './r3_identifiers';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/** Name of the context parameter passed into a template function */
|
/** Name of the context parameter passed into a template function */
|
||||||
const CONTEXT_NAME = 'ctx';
|
const CONTEXT_NAME = 'ctx';
|
||||||
|
|
||||||
|
@ -213,10 +215,10 @@ function unsupported(feature: string): never {
|
||||||
}
|
}
|
||||||
|
|
||||||
const BINDING_INSTRUCTION_MAP: {[type: number]: o.ExternalReference} = {
|
const BINDING_INSTRUCTION_MAP: {[type: number]: o.ExternalReference} = {
|
||||||
[t.PropertyBindingType.Property]: R3.elementProperty,
|
[BoundElementBindingType.Property]: R3.elementProperty,
|
||||||
[t.PropertyBindingType.Attribute]: R3.elementAttribute,
|
[BoundElementBindingType.Attribute]: R3.elementAttribute,
|
||||||
[t.PropertyBindingType.Class]: R3.elementClassNamed,
|
[BoundElementBindingType.Class]: R3.elementClassNamed,
|
||||||
[t.PropertyBindingType.Style]: R3.elementStyleNamed,
|
[BoundElementBindingType.Style]: R3.elementStyleNamed,
|
||||||
};
|
};
|
||||||
|
|
||||||
function interpolate(args: o.Expression[]): o.Expression {
|
function interpolate(args: o.Expression[]): o.Expression {
|
||||||
|
@ -679,7 +681,7 @@ class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver {
|
||||||
|
|
||||||
// Generate element input bindings
|
// Generate element input bindings
|
||||||
element.inputs.forEach((input: t.BoundAttribute) => {
|
element.inputs.forEach((input: t.BoundAttribute) => {
|
||||||
if (input.type === t.PropertyBindingType.Animation) {
|
if (input.type === BoundElementBindingType.Animation) {
|
||||||
this._unsupported('animations');
|
this._unsupported('animations');
|
||||||
}
|
}
|
||||||
const convertedBinding = this.convertPropertyBinding(implicit, input.value);
|
const convertedBinding = this.convertPropertyBinding(implicit, input.value);
|
||||||
|
@ -691,7 +693,7 @@ class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver {
|
||||||
this._bindingCode, input.sourceSpan, instruction, o.literal(elementIndex),
|
this._bindingCode, input.sourceSpan, instruction, o.literal(elementIndex),
|
||||||
o.literal(input.name), value);
|
o.literal(input.name), value);
|
||||||
} else {
|
} else {
|
||||||
this._unsupported(`binding ${t.PropertyBindingType[input.type]}`);
|
this._unsupported(`binding type ${input.type}`);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
import {CompileDirectiveSummary, CompilePipeSummary} from '../compile_metadata';
|
import {CompileDirectiveSummary, CompilePipeSummary} from '../compile_metadata';
|
||||||
import {SecurityContext} from '../core';
|
import {SecurityContext} from '../core';
|
||||||
import {ASTWithSource, BindingPipe, EmptyExpr, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
|
import {ASTWithSource, BindingPipe, EmptyExpr, ParserError, RecursiveAstVisitor, TemplateBinding} from '../expression_parser/ast';
|
||||||
|
import {BoundElementBindingType, BoundElementProperty, ParsedEvent, ParsedEventType, ParsedProperty, ParsedPropertyType, ParsedVariable} from '../expression_parser/ast';
|
||||||
import {Parser} from '../expression_parser/parser';
|
import {Parser} from '../expression_parser/parser';
|
||||||
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
import {InterpolationConfig} from '../ml_parser/interpolation_config';
|
||||||
import {mergeNsAndName} from '../ml_parser/tags';
|
import {mergeNsAndName} from '../ml_parser/tags';
|
||||||
|
@ -17,7 +18,7 @@ import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
import {CssSelector} from '../selector';
|
import {CssSelector} from '../selector';
|
||||||
import {splitAtColon, splitAtPeriod} from '../util';
|
import {splitAtColon, splitAtPeriod} from '../util';
|
||||||
|
|
||||||
import {BoundElementPropertyAst, BoundEventAst, PropertyBindingType, VariableAst} from './template_ast';
|
import {BoundElementPropertyAst, PropertyBindingType} from './template_ast';
|
||||||
|
|
||||||
const PROPERTY_PARTS_SEPARATOR = '.';
|
const PROPERTY_PARTS_SEPARATOR = '.';
|
||||||
const ATTRIBUTE_PREFIX = 'attr';
|
const ATTRIBUTE_PREFIX = 'attr';
|
||||||
|
@ -26,27 +27,6 @@ const STYLE_PREFIX = 'style';
|
||||||
|
|
||||||
const ANIMATE_PROP_PREFIX = 'animate-';
|
const ANIMATE_PROP_PREFIX = 'animate-';
|
||||||
|
|
||||||
export enum BoundPropertyType {
|
|
||||||
DEFAULT,
|
|
||||||
LITERAL_ATTR,
|
|
||||||
ANIMATION
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Represents a parsed property.
|
|
||||||
*/
|
|
||||||
export class BoundProperty {
|
|
||||||
public readonly isLiteral: boolean;
|
|
||||||
public readonly isAnimation: boolean;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
public name: string, public expression: ASTWithSource, public type: BoundPropertyType,
|
|
||||||
public sourceSpan: ParseSourceSpan) {
|
|
||||||
this.isLiteral = this.type === BoundPropertyType.LITERAL_ATTR;
|
|
||||||
this.isAnimation = this.type === BoundPropertyType.ANIMATION;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parses bindings in templates and in the directive host area.
|
* Parses bindings in templates and in the directive host area.
|
||||||
*/
|
*/
|
||||||
|
@ -70,9 +50,9 @@ export class BindingParser {
|
||||||
getUsedPipes(): CompilePipeSummary[] { return Array.from(this._usedPipes.values()); }
|
getUsedPipes(): CompilePipeSummary[] { return Array.from(this._usedPipes.values()); }
|
||||||
|
|
||||||
createBoundHostProperties(dirMeta: CompileDirectiveSummary, sourceSpan: ParseSourceSpan):
|
createBoundHostProperties(dirMeta: CompileDirectiveSummary, sourceSpan: ParseSourceSpan):
|
||||||
BoundProperty[]|null {
|
ParsedProperty[]|null {
|
||||||
if (dirMeta.hostProperties) {
|
if (dirMeta.hostProperties) {
|
||||||
const boundProps: BoundProperty[] = [];
|
const boundProps: ParsedProperty[] = [];
|
||||||
Object.keys(dirMeta.hostProperties).forEach(propName => {
|
Object.keys(dirMeta.hostProperties).forEach(propName => {
|
||||||
const expression = dirMeta.hostProperties[propName];
|
const expression = dirMeta.hostProperties[propName];
|
||||||
if (typeof expression === 'string') {
|
if (typeof expression === 'string') {
|
||||||
|
@ -90,27 +70,27 @@ export class BindingParser {
|
||||||
|
|
||||||
createDirectiveHostPropertyAsts(
|
createDirectiveHostPropertyAsts(
|
||||||
dirMeta: CompileDirectiveSummary, elementSelector: string,
|
dirMeta: CompileDirectiveSummary, elementSelector: string,
|
||||||
sourceSpan: ParseSourceSpan): BoundElementPropertyAst[]|null {
|
sourceSpan: ParseSourceSpan): BoundElementProperty[]|null {
|
||||||
const boundProps = this.createBoundHostProperties(dirMeta, sourceSpan);
|
const boundProps = this.createBoundHostProperties(dirMeta, sourceSpan);
|
||||||
return boundProps &&
|
return boundProps &&
|
||||||
boundProps.map((prop) => this.createElementPropertyAst(elementSelector, prop));
|
boundProps.map((prop) => this.createBoundElementProperty(elementSelector, prop));
|
||||||
}
|
}
|
||||||
|
|
||||||
createDirectiveHostEventAsts(dirMeta: CompileDirectiveSummary, sourceSpan: ParseSourceSpan):
|
createDirectiveHostEventAsts(dirMeta: CompileDirectiveSummary, sourceSpan: ParseSourceSpan):
|
||||||
BoundEventAst[]|null {
|
ParsedEvent[]|null {
|
||||||
if (dirMeta.hostListeners) {
|
if (dirMeta.hostListeners) {
|
||||||
const targetEventAsts: BoundEventAst[] = [];
|
const targetEvents: ParsedEvent[] = [];
|
||||||
Object.keys(dirMeta.hostListeners).forEach(propName => {
|
Object.keys(dirMeta.hostListeners).forEach(propName => {
|
||||||
const expression = dirMeta.hostListeners[propName];
|
const expression = dirMeta.hostListeners[propName];
|
||||||
if (typeof expression === 'string') {
|
if (typeof expression === 'string') {
|
||||||
this.parseEvent(propName, expression, sourceSpan, [], targetEventAsts);
|
this.parseEvent(propName, expression, sourceSpan, [], targetEvents);
|
||||||
} else {
|
} else {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`,
|
`Value of the host listener "${propName}" needs to be a string representing an expression but got "${expression}" (${typeof expression})`,
|
||||||
sourceSpan);
|
sourceSpan);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return targetEventAsts;
|
return targetEvents;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -133,13 +113,14 @@ export class BindingParser {
|
||||||
// Parse an inline template binding. ie `<tag *tplKey="<tplValue>">`
|
// Parse an inline template binding. ie `<tag *tplKey="<tplValue>">`
|
||||||
parseInlineTemplateBinding(
|
parseInlineTemplateBinding(
|
||||||
tplKey: string, tplValue: string, sourceSpan: ParseSourceSpan,
|
tplKey: string, tplValue: string, sourceSpan: ParseSourceSpan,
|
||||||
targetMatchableAttrs: string[][], targetProps: BoundProperty[], targetVars: VariableAst[]) {
|
targetMatchableAttrs: string[][], targetProps: ParsedProperty[],
|
||||||
|
targetVars: ParsedVariable[]) {
|
||||||
const bindings = this._parseTemplateBindings(tplKey, tplValue, sourceSpan);
|
const bindings = this._parseTemplateBindings(tplKey, tplValue, sourceSpan);
|
||||||
|
|
||||||
for (let i = 0; i < bindings.length; i++) {
|
for (let i = 0; i < bindings.length; i++) {
|
||||||
const binding = bindings[i];
|
const binding = bindings[i];
|
||||||
if (binding.keyIsVar) {
|
if (binding.keyIsVar) {
|
||||||
targetVars.push(new VariableAst(binding.key, binding.name, sourceSpan));
|
targetVars.push(new ParsedVariable(binding.key, binding.name, sourceSpan));
|
||||||
} else if (binding.expression) {
|
} else if (binding.expression) {
|
||||||
this._parsePropertyAst(
|
this._parsePropertyAst(
|
||||||
binding.key, binding.expression, sourceSpan, targetMatchableAttrs, targetProps);
|
binding.key, binding.expression, sourceSpan, targetMatchableAttrs, targetProps);
|
||||||
|
@ -173,8 +154,8 @@ export class BindingParser {
|
||||||
|
|
||||||
parseLiteralAttr(
|
parseLiteralAttr(
|
||||||
name: string, value: string|null, sourceSpan: ParseSourceSpan,
|
name: string, value: string|null, sourceSpan: ParseSourceSpan,
|
||||||
targetMatchableAttrs: string[][], targetProps: BoundProperty[]) {
|
targetMatchableAttrs: string[][], targetProps: ParsedProperty[]) {
|
||||||
if (_isAnimationLabel(name)) {
|
if (isAnimationLabel(name)) {
|
||||||
name = name.substring(1);
|
name = name.substring(1);
|
||||||
if (value) {
|
if (value) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
|
@ -184,20 +165,20 @@ export class BindingParser {
|
||||||
}
|
}
|
||||||
this._parseAnimation(name, value, sourceSpan, targetMatchableAttrs, targetProps);
|
this._parseAnimation(name, value, sourceSpan, targetMatchableAttrs, targetProps);
|
||||||
} else {
|
} else {
|
||||||
targetProps.push(new BoundProperty(
|
targetProps.push(new ParsedProperty(
|
||||||
name, this._exprParser.wrapLiteralPrimitive(value, ''), BoundPropertyType.LITERAL_ATTR,
|
name, this._exprParser.wrapLiteralPrimitive(value, ''), ParsedPropertyType.LITERAL_ATTR,
|
||||||
sourceSpan));
|
sourceSpan));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
parsePropertyBinding(
|
parsePropertyBinding(
|
||||||
name: string, expression: string, isHost: boolean, sourceSpan: ParseSourceSpan,
|
name: string, expression: string, isHost: boolean, sourceSpan: ParseSourceSpan,
|
||||||
targetMatchableAttrs: string[][], targetProps: BoundProperty[]) {
|
targetMatchableAttrs: string[][], targetProps: ParsedProperty[]) {
|
||||||
let isAnimationProp = false;
|
let isAnimationProp = false;
|
||||||
if (name.startsWith(ANIMATE_PROP_PREFIX)) {
|
if (name.startsWith(ANIMATE_PROP_PREFIX)) {
|
||||||
isAnimationProp = true;
|
isAnimationProp = true;
|
||||||
name = name.substring(ANIMATE_PROP_PREFIX.length);
|
name = name.substring(ANIMATE_PROP_PREFIX.length);
|
||||||
} else if (_isAnimationLabel(name)) {
|
} else if (isAnimationLabel(name)) {
|
||||||
isAnimationProp = true;
|
isAnimationProp = true;
|
||||||
name = name.substring(1);
|
name = name.substring(1);
|
||||||
}
|
}
|
||||||
|
@ -213,7 +194,7 @@ export class BindingParser {
|
||||||
|
|
||||||
parsePropertyInterpolation(
|
parsePropertyInterpolation(
|
||||||
name: string, value: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][],
|
name: string, value: string, sourceSpan: ParseSourceSpan, targetMatchableAttrs: string[][],
|
||||||
targetProps: BoundProperty[]): boolean {
|
targetProps: ParsedProperty[]): boolean {
|
||||||
const expr = this.parseInterpolation(value, sourceSpan);
|
const expr = this.parseInterpolation(value, sourceSpan);
|
||||||
if (expr) {
|
if (expr) {
|
||||||
this._parsePropertyAst(name, expr, sourceSpan, targetMatchableAttrs, targetProps);
|
this._parsePropertyAst(name, expr, sourceSpan, targetMatchableAttrs, targetProps);
|
||||||
|
@ -224,20 +205,20 @@ export class BindingParser {
|
||||||
|
|
||||||
private _parsePropertyAst(
|
private _parsePropertyAst(
|
||||||
name: string, ast: ASTWithSource, sourceSpan: ParseSourceSpan,
|
name: string, ast: ASTWithSource, sourceSpan: ParseSourceSpan,
|
||||||
targetMatchableAttrs: string[][], targetProps: BoundProperty[]) {
|
targetMatchableAttrs: string[][], targetProps: ParsedProperty[]) {
|
||||||
targetMatchableAttrs.push([name, ast.source !]);
|
targetMatchableAttrs.push([name, ast.source !]);
|
||||||
targetProps.push(new BoundProperty(name, ast, BoundPropertyType.DEFAULT, sourceSpan));
|
targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.DEFAULT, sourceSpan));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseAnimation(
|
private _parseAnimation(
|
||||||
name: string, expression: string|null, sourceSpan: ParseSourceSpan,
|
name: string, expression: string|null, sourceSpan: ParseSourceSpan,
|
||||||
targetMatchableAttrs: string[][], targetProps: BoundProperty[]) {
|
targetMatchableAttrs: string[][], targetProps: ParsedProperty[]) {
|
||||||
// This will occur when a @trigger is not paired with an expression.
|
// This will occur when a @trigger is not paired with an expression.
|
||||||
// For animations it is valid to not have an expression since */void
|
// For animations it is valid to not have an expression since */void
|
||||||
// states will be applied by angular when the element is attached/detached
|
// states will be applied by angular when the element is attached/detached
|
||||||
const ast = this._parseBinding(expression || 'undefined', false, sourceSpan);
|
const ast = this._parseBinding(expression || 'undefined', false, sourceSpan);
|
||||||
targetMatchableAttrs.push([name, ast.source !]);
|
targetMatchableAttrs.push([name, ast.source !]);
|
||||||
targetProps.push(new BoundProperty(name, ast, BoundPropertyType.ANIMATION, sourceSpan));
|
targetProps.push(new ParsedProperty(name, ast, ParsedPropertyType.ANIMATION, sourceSpan));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseBinding(value: string, isHostBinding: boolean, sourceSpan: ParseSourceSpan):
|
private _parseBinding(value: string, isHostBinding: boolean, sourceSpan: ParseSourceSpan):
|
||||||
|
@ -257,16 +238,16 @@ export class BindingParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
createElementPropertyAst(elementSelector: string, boundProp: BoundProperty):
|
createBoundElementProperty(elementSelector: string, boundProp: ParsedProperty):
|
||||||
BoundElementPropertyAst {
|
BoundElementProperty {
|
||||||
if (boundProp.isAnimation) {
|
if (boundProp.isAnimation) {
|
||||||
return new BoundElementPropertyAst(
|
return new BoundElementProperty(
|
||||||
boundProp.name, PropertyBindingType.Animation, SecurityContext.NONE, boundProp.expression,
|
boundProp.name, BoundElementBindingType.Animation, SecurityContext.NONE,
|
||||||
null, boundProp.sourceSpan);
|
boundProp.expression, null, boundProp.sourceSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
let unit: string|null = null;
|
let unit: string|null = null;
|
||||||
let bindingType: PropertyBindingType = undefined !;
|
let bindingType: BoundElementBindingType = undefined !;
|
||||||
let boundPropertyName: string|null = null;
|
let boundPropertyName: string|null = null;
|
||||||
const parts = boundProp.name.split(PROPERTY_PARTS_SEPARATOR);
|
const parts = boundProp.name.split(PROPERTY_PARTS_SEPARATOR);
|
||||||
let securityContexts: SecurityContext[] = undefined !;
|
let securityContexts: SecurityContext[] = undefined !;
|
||||||
|
@ -286,15 +267,15 @@ export class BindingParser {
|
||||||
boundPropertyName = mergeNsAndName(ns, name);
|
boundPropertyName = mergeNsAndName(ns, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
bindingType = PropertyBindingType.Attribute;
|
bindingType = BoundElementBindingType.Attribute;
|
||||||
} else if (parts[0] == CLASS_PREFIX) {
|
} else if (parts[0] == CLASS_PREFIX) {
|
||||||
boundPropertyName = parts[1];
|
boundPropertyName = parts[1];
|
||||||
bindingType = PropertyBindingType.Class;
|
bindingType = BoundElementBindingType.Class;
|
||||||
securityContexts = [SecurityContext.NONE];
|
securityContexts = [SecurityContext.NONE];
|
||||||
} else if (parts[0] == STYLE_PREFIX) {
|
} else if (parts[0] == STYLE_PREFIX) {
|
||||||
unit = parts.length > 2 ? parts[2] : null;
|
unit = parts.length > 2 ? parts[2] : null;
|
||||||
boundPropertyName = parts[1];
|
boundPropertyName = parts[1];
|
||||||
bindingType = PropertyBindingType.Style;
|
bindingType = BoundElementBindingType.Style;
|
||||||
securityContexts = [SecurityContext.STYLE];
|
securityContexts = [SecurityContext.STYLE];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -304,29 +285,28 @@ export class BindingParser {
|
||||||
boundPropertyName = this._schemaRegistry.getMappedPropName(boundProp.name);
|
boundPropertyName = this._schemaRegistry.getMappedPropName(boundProp.name);
|
||||||
securityContexts = calcPossibleSecurityContexts(
|
securityContexts = calcPossibleSecurityContexts(
|
||||||
this._schemaRegistry, elementSelector, boundPropertyName, false);
|
this._schemaRegistry, elementSelector, boundPropertyName, false);
|
||||||
bindingType = PropertyBindingType.Property;
|
bindingType = BoundElementBindingType.Property;
|
||||||
this._validatePropertyOrAttributeName(boundPropertyName, boundProp.sourceSpan, false);
|
this._validatePropertyOrAttributeName(boundPropertyName, boundProp.sourceSpan, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
return new BoundElementPropertyAst(
|
return new BoundElementProperty(
|
||||||
boundPropertyName, bindingType, securityContexts[0], boundProp.expression, unit,
|
boundPropertyName, bindingType, securityContexts[0], boundProp.expression, unit,
|
||||||
boundProp.sourceSpan);
|
boundProp.sourceSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
parseEvent(
|
parseEvent(
|
||||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||||
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
|
targetMatchableAttrs: string[][], targetEvents: ParsedEvent[]) {
|
||||||
if (_isAnimationLabel(name)) {
|
if (isAnimationLabel(name)) {
|
||||||
name = name.substr(1);
|
name = name.substr(1);
|
||||||
this._parseAnimationEvent(name, expression, sourceSpan, targetEvents);
|
this._parseAnimationEvent(name, expression, sourceSpan, targetEvents);
|
||||||
} else {
|
} else {
|
||||||
this._parseEvent(name, expression, sourceSpan, targetMatchableAttrs, targetEvents);
|
this._parseRegularEvent(name, expression, sourceSpan, targetMatchableAttrs, targetEvents);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseAnimationEvent(
|
private _parseAnimationEvent(
|
||||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
name: string, expression: string, sourceSpan: ParseSourceSpan, targetEvents: ParsedEvent[]) {
|
||||||
targetEvents: BoundEventAst[]) {
|
|
||||||
const matches = splitAtPeriod(name, [name, '']);
|
const matches = splitAtPeriod(name, [name, '']);
|
||||||
const eventName = matches[0];
|
const eventName = matches[0];
|
||||||
const phase = matches[1].toLowerCase();
|
const phase = matches[1].toLowerCase();
|
||||||
|
@ -335,7 +315,8 @@ export class BindingParser {
|
||||||
case 'start':
|
case 'start':
|
||||||
case 'done':
|
case 'done':
|
||||||
const ast = this._parseAction(expression, sourceSpan);
|
const ast = this._parseAction(expression, sourceSpan);
|
||||||
targetEvents.push(new BoundEventAst(eventName, null, phase, ast, sourceSpan));
|
targetEvents.push(
|
||||||
|
new ParsedEvent(eventName, phase, ParsedEventType.Animation, ast, sourceSpan));
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -351,14 +332,14 @@ export class BindingParser {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseEvent(
|
private _parseRegularEvent(
|
||||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||||
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
|
targetMatchableAttrs: string[][], targetEvents: ParsedEvent[]) {
|
||||||
// long format: 'target: eventName'
|
// long format: 'target: eventName'
|
||||||
const [target, eventName] = splitAtColon(name, [null !, name]);
|
const [target, eventName] = splitAtColon(name, [null !, name]);
|
||||||
const ast = this._parseAction(expression, sourceSpan);
|
const ast = this._parseAction(expression, sourceSpan);
|
||||||
targetMatchableAttrs.push([name !, ast.source !]);
|
targetMatchableAttrs.push([name !, ast.source !]);
|
||||||
targetEvents.push(new BoundEventAst(eventName, target, null, ast, sourceSpan));
|
targetEvents.push(new ParsedEvent(eventName, target, ParsedEventType.Regular, ast, sourceSpan));
|
||||||
// Don't detect directives for event names for now,
|
// Don't detect directives for event names for now,
|
||||||
// so don't add the event name to the matchableAttrs
|
// so don't add the event name to the matchableAttrs
|
||||||
}
|
}
|
||||||
|
@ -439,7 +420,7 @@ export class PipeCollector extends RecursiveAstVisitor {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function _isAnimationLabel(name: string): boolean {
|
function isAnimationLabel(name: string): boolean {
|
||||||
return name[0] == '@';
|
return name[0] == '@';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@
|
||||||
import {AstPath} from '../ast_path';
|
import {AstPath} from '../ast_path';
|
||||||
import {CompileDirectiveSummary, CompileProviderMetadata, CompileTokenMetadata} from '../compile_metadata';
|
import {CompileDirectiveSummary, CompileProviderMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||||
import {SecurityContext} from '../core';
|
import {SecurityContext} from '../core';
|
||||||
import {AST} from '../expression_parser/ast';
|
import {AST, BoundElementBindingType, BoundElementProperty, ParsedEvent, ParsedEventType, ParsedVariable} from '../expression_parser/ast';
|
||||||
import {LifecycleHooks} from '../lifecycle_reflector';
|
import {LifecycleHooks} from '../lifecycle_reflector';
|
||||||
import {ParseSourceSpan} from '../parse_util';
|
import {ParseSourceSpan} from '../parse_util';
|
||||||
|
|
||||||
|
@ -58,12 +58,33 @@ export class AttrAst implements TemplateAst {
|
||||||
visit(visitor: TemplateAstVisitor, context: any): any { return visitor.visitAttr(this, context); }
|
visit(visitor: TemplateAstVisitor, context: any): any { return visitor.visitAttr(this, context); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum PropertyBindingType {
|
||||||
|
// A normal binding to a property (e.g. `[property]="expression"`).
|
||||||
|
Property,
|
||||||
|
// A binding to an element attribute (e.g. `[attr.name]="expression"`).
|
||||||
|
Attribute,
|
||||||
|
// A binding to a CSS class (e.g. `[class.name]="condition"`).
|
||||||
|
Class,
|
||||||
|
// A binding to a style rule (e.g. `[style.rule]="expression"`).
|
||||||
|
Style,
|
||||||
|
// A binding to an animation reference (e.g. `[animate.key]="expression"`).
|
||||||
|
Animation,
|
||||||
|
}
|
||||||
|
|
||||||
|
const BoundPropertyMapping = {
|
||||||
|
[BoundElementBindingType.Animation]: PropertyBindingType.Animation,
|
||||||
|
[BoundElementBindingType.Attribute]: PropertyBindingType.Attribute,
|
||||||
|
[BoundElementBindingType.Class]: PropertyBindingType.Class,
|
||||||
|
[BoundElementBindingType.Property]: PropertyBindingType.Property,
|
||||||
|
[BoundElementBindingType.Style]: PropertyBindingType.Style,
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A binding for an element property (e.g. `[property]="expression"`) or an animation trigger (e.g.
|
* A binding for an element property (e.g. `[property]="expression"`) or an animation trigger (e.g.
|
||||||
* `[@trigger]="stateExp"`)
|
* `[@trigger]="stateExp"`)
|
||||||
*/
|
*/
|
||||||
export class BoundElementPropertyAst implements TemplateAst {
|
export class BoundElementPropertyAst implements TemplateAst {
|
||||||
public readonly isAnimation: boolean;
|
readonly isAnimation: boolean;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public name: string, public type: PropertyBindingType,
|
public name: string, public type: PropertyBindingType,
|
||||||
|
@ -71,6 +92,13 @@ export class BoundElementPropertyAst implements TemplateAst {
|
||||||
public sourceSpan: ParseSourceSpan) {
|
public sourceSpan: ParseSourceSpan) {
|
||||||
this.isAnimation = this.type === PropertyBindingType.Animation;
|
this.isAnimation = this.type === PropertyBindingType.Animation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static fromBoundProperty(prop: BoundElementProperty) {
|
||||||
|
const type = BoundPropertyMapping[prop.type];
|
||||||
|
return new BoundElementPropertyAst(
|
||||||
|
prop.name, type, prop.securityContext, prop.value, prop.unit, prop.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||||
return visitor.visitElementProperty(this, context);
|
return visitor.visitElementProperty(this, context);
|
||||||
}
|
}
|
||||||
|
@ -81,18 +109,8 @@ export class BoundElementPropertyAst implements TemplateAst {
|
||||||
* `(@trigger.phase)="callback($event)"`).
|
* `(@trigger.phase)="callback($event)"`).
|
||||||
*/
|
*/
|
||||||
export class BoundEventAst implements TemplateAst {
|
export class BoundEventAst implements TemplateAst {
|
||||||
static calcFullName(name: string, target: string|null, phase: string|null): string {
|
readonly fullName: string;
|
||||||
if (target) {
|
readonly isAnimation: boolean;
|
||||||
return `${target}:${name}`;
|
|
||||||
} else if (phase) {
|
|
||||||
return `@${name}.${phase}`;
|
|
||||||
} else {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public readonly fullName: string;
|
|
||||||
public readonly isAnimation: boolean;
|
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
public name: string, public target: string|null, public phase: string|null,
|
public name: string, public target: string|null, public phase: string|null,
|
||||||
|
@ -100,6 +118,25 @@ export class BoundEventAst implements TemplateAst {
|
||||||
this.fullName = BoundEventAst.calcFullName(this.name, this.target, this.phase);
|
this.fullName = BoundEventAst.calcFullName(this.name, this.target, this.phase);
|
||||||
this.isAnimation = !!this.phase;
|
this.isAnimation = !!this.phase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static calcFullName(name: string, target: string|null, phase: string|null): string {
|
||||||
|
if (target) {
|
||||||
|
return `${target}:${name}`;
|
||||||
|
}
|
||||||
|
if (phase) {
|
||||||
|
return `@${name}.${phase}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
|
||||||
|
static fromParsedEvent(event: ParsedEvent) {
|
||||||
|
const target: string|null = event.type === ParsedEventType.Regular ? event.targetOrPhase : null;
|
||||||
|
const phase: string|null =
|
||||||
|
event.type === ParsedEventType.Animation ? event.targetOrPhase : null;
|
||||||
|
return new BoundEventAst(event.name, target, phase, event.handler, event.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||||
return visitor.visitEvent(this, context);
|
return visitor.visitEvent(this, context);
|
||||||
}
|
}
|
||||||
|
@ -122,6 +159,11 @@ export class ReferenceAst implements TemplateAst {
|
||||||
*/
|
*/
|
||||||
export class VariableAst implements TemplateAst {
|
export class VariableAst implements TemplateAst {
|
||||||
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
|
constructor(public name: string, public value: string, public sourceSpan: ParseSourceSpan) {}
|
||||||
|
|
||||||
|
static fromParsedVariable(v: ParsedVariable) {
|
||||||
|
return new VariableAst(v.name, v.value, v.sourceSpan);
|
||||||
|
}
|
||||||
|
|
||||||
visit(visitor: TemplateAstVisitor, context: any): any {
|
visit(visitor: TemplateAstVisitor, context: any): any {
|
||||||
return visitor.visitVariable(this, context);
|
return visitor.visitVariable(this, context);
|
||||||
}
|
}
|
||||||
|
@ -220,37 +262,6 @@ export class NgContentAst implements TemplateAst {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Enumeration of types of property bindings.
|
|
||||||
*/
|
|
||||||
export enum PropertyBindingType {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A normal binding to a property (e.g. `[property]="expression"`).
|
|
||||||
*/
|
|
||||||
Property,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A binding to an element attribute (e.g. `[attr.name]="expression"`).
|
|
||||||
*/
|
|
||||||
Attribute,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A binding to a CSS class (e.g. `[class.name]="condition"`).
|
|
||||||
*/
|
|
||||||
Class,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A binding to a style rule (e.g. `[style.rule]="expression"`).
|
|
||||||
*/
|
|
||||||
Style,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A binding to an animation reference (e.g. `[animate.key]="expression"`).
|
|
||||||
*/
|
|
||||||
Animation
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface QueryMatch {
|
export interface QueryMatch {
|
||||||
queryId: number;
|
queryId: number;
|
||||||
value: CompileTokenMetadata;
|
value: CompileTokenMetadata;
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {CompileDirectiveMetadata, CompileDirectiveSummary, CompilePipeSummary, C
|
||||||
import {CompileReflector} from '../compile_reflector';
|
import {CompileReflector} from '../compile_reflector';
|
||||||
import {CompilerConfig} from '../config';
|
import {CompilerConfig} from '../config';
|
||||||
import {SchemaMetadata} from '../core';
|
import {SchemaMetadata} from '../core';
|
||||||
import {AST, ASTWithSource, EmptyExpr} from '../expression_parser/ast';
|
import {AST, ASTWithSource, EmptyExpr, ParsedEvent, ParsedProperty, ParsedVariable} from '../expression_parser/ast';
|
||||||
import {Parser} from '../expression_parser/parser';
|
import {Parser} from '../expression_parser/parser';
|
||||||
import {Identifiers, createTokenForExternalReference, createTokenForReference} from '../identifiers';
|
import {Identifiers, createTokenForExternalReference, createTokenForReference} from '../identifiers';
|
||||||
import * as html from '../ml_parser/ast';
|
import * as html from '../ml_parser/ast';
|
||||||
|
@ -26,8 +26,8 @@ import {CssSelector, SelectorMatcher} from '../selector';
|
||||||
import {isStyleUrlResolvable} from '../style_url_resolver';
|
import {isStyleUrlResolvable} from '../style_url_resolver';
|
||||||
import {Console, syntaxError} from '../util';
|
import {Console, syntaxError} from '../util';
|
||||||
|
|
||||||
import {BindingParser, BoundProperty} from './binding_parser';
|
import {BindingParser} from './binding_parser';
|
||||||
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, PropertyBindingType, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from './template_ast';
|
import * as t from './template_ast';
|
||||||
import {PreparsedElementType, preparseElement} from './template_preparser';
|
import {PreparsedElementType, preparseElement} from './template_preparser';
|
||||||
|
|
||||||
const BIND_NAME_REGEXP =
|
const BIND_NAME_REGEXP =
|
||||||
|
@ -79,7 +79,7 @@ export class TemplateParseError extends ParseError {
|
||||||
|
|
||||||
export class TemplateParseResult {
|
export class TemplateParseResult {
|
||||||
constructor(
|
constructor(
|
||||||
public templateAst?: TemplateAst[], public usedPipes?: CompilePipeSummary[],
|
public templateAst?: t.TemplateAst[], public usedPipes?: CompilePipeSummary[],
|
||||||
public errors?: ParseError[]) {}
|
public errors?: ParseError[]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ export class TemplateParser {
|
||||||
private _config: CompilerConfig, private _reflector: CompileReflector,
|
private _config: CompilerConfig, private _reflector: CompileReflector,
|
||||||
private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
|
private _exprParser: Parser, private _schemaRegistry: ElementSchemaRegistry,
|
||||||
private _htmlParser: HtmlParser, private _console: Console,
|
private _htmlParser: HtmlParser, private _console: Console,
|
||||||
public transforms: TemplateAstVisitor[]) {}
|
public transforms: t.TemplateAstVisitor[]) {}
|
||||||
|
|
||||||
public get expressionParser() { return this._exprParser; }
|
public get expressionParser() { return this._exprParser; }
|
||||||
|
|
||||||
|
@ -96,7 +96,7 @@ export class TemplateParser {
|
||||||
component: CompileDirectiveMetadata, template: string|ParseTreeResult,
|
component: CompileDirectiveMetadata, template: string|ParseTreeResult,
|
||||||
directives: CompileDirectiveSummary[], pipes: CompilePipeSummary[], schemas: SchemaMetadata[],
|
directives: CompileDirectiveSummary[], pipes: CompilePipeSummary[], schemas: SchemaMetadata[],
|
||||||
templateUrl: string,
|
templateUrl: string,
|
||||||
preserveWhitespaces: boolean): {template: TemplateAst[], pipes: CompilePipeSummary[]} {
|
preserveWhitespaces: boolean): {template: t.TemplateAst[], pipes: CompilePipeSummary[]} {
|
||||||
const result = this.tryParse(
|
const result = this.tryParse(
|
||||||
component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces);
|
component, template, directives, pipes, schemas, templateUrl, preserveWhitespaces);
|
||||||
const warnings = result.errors !.filter(error => error.level === ParseErrorLevel.WARNING);
|
const warnings = result.errors !.filter(error => error.level === ParseErrorLevel.WARNING);
|
||||||
|
@ -136,7 +136,7 @@ export class TemplateParser {
|
||||||
htmlAstWithErrors: ParseTreeResult, component: CompileDirectiveMetadata,
|
htmlAstWithErrors: ParseTreeResult, component: CompileDirectiveMetadata,
|
||||||
directives: CompileDirectiveSummary[], pipes: CompilePipeSummary[],
|
directives: CompileDirectiveSummary[], pipes: CompilePipeSummary[],
|
||||||
schemas: SchemaMetadata[]): TemplateParseResult {
|
schemas: SchemaMetadata[]): TemplateParseResult {
|
||||||
let result: TemplateAst[];
|
let result: t.TemplateAst[];
|
||||||
const errors = htmlAstWithErrors.errors;
|
const errors = htmlAstWithErrors.errors;
|
||||||
const usedPipes: CompilePipeSummary[] = [];
|
const usedPipes: CompilePipeSummary[] = [];
|
||||||
if (htmlAstWithErrors.rootNodes.length > 0) {
|
if (htmlAstWithErrors.rootNodes.length > 0) {
|
||||||
|
@ -169,7 +169,7 @@ export class TemplateParser {
|
||||||
|
|
||||||
if (this.transforms) {
|
if (this.transforms) {
|
||||||
this.transforms.forEach(
|
this.transforms.forEach(
|
||||||
(transform: TemplateAstVisitor) => { result = templateVisitAll(transform, result); });
|
(transform: t.TemplateAstVisitor) => { result = t.templateVisitAll(transform, result); });
|
||||||
}
|
}
|
||||||
|
|
||||||
return new TemplateParseResult(result, usedPipes, errors);
|
return new TemplateParseResult(result, usedPipes, errors);
|
||||||
|
@ -195,12 +195,12 @@ export class TemplateParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @internal */
|
/** @internal */
|
||||||
_assertNoReferenceDuplicationOnTemplate(result: TemplateAst[], errors: TemplateParseError[]):
|
_assertNoReferenceDuplicationOnTemplate(result: t.TemplateAst[], errors: TemplateParseError[]):
|
||||||
void {
|
void {
|
||||||
const existingReferences: string[] = [];
|
const existingReferences: string[] = [];
|
||||||
|
|
||||||
result.filter(element => !!(<any>element).references)
|
result.filter(element => !!(<any>element).references)
|
||||||
.forEach(element => (<any>element).references.forEach((reference: ReferenceAst) => {
|
.forEach(element => (<any>element).references.forEach((reference: t.ReferenceAst) => {
|
||||||
const name = reference.name;
|
const name = reference.name;
|
||||||
if (existingReferences.indexOf(name) < 0) {
|
if (existingReferences.indexOf(name) < 0) {
|
||||||
existingReferences.push(name);
|
existingReferences.push(name);
|
||||||
|
@ -242,12 +242,12 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR) !;
|
const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR) !;
|
||||||
const valueNoNgsp = replaceNgsp(text.value);
|
const valueNoNgsp = replaceNgsp(text.value);
|
||||||
const expr = this._bindingParser.parseInterpolation(valueNoNgsp, text.sourceSpan !);
|
const expr = this._bindingParser.parseInterpolation(valueNoNgsp, text.sourceSpan !);
|
||||||
return expr ? new BoundTextAst(expr, ngContentIndex, text.sourceSpan !) :
|
return expr ? new t.BoundTextAst(expr, ngContentIndex, text.sourceSpan !) :
|
||||||
new TextAst(valueNoNgsp, ngContentIndex, text.sourceSpan !);
|
new t.TextAst(valueNoNgsp, ngContentIndex, text.sourceSpan !);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitAttribute(attribute: html.Attribute, context: any): any {
|
visitAttribute(attribute: html.Attribute, context: any): any {
|
||||||
return new AttrAst(attribute.name, attribute.value, attribute.sourceSpan);
|
return new t.AttrAst(attribute.name, attribute.value, attribute.sourceSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitComment(comment: html.Comment, context: any): any { return null; }
|
visitComment(comment: html.Comment, context: any): any { return null; }
|
||||||
|
@ -271,23 +271,25 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
const matchableAttrs: [string, string][] = [];
|
const matchableAttrs: [string, string][] = [];
|
||||||
const elementOrDirectiveProps: BoundProperty[] = [];
|
const elementOrDirectiveProps: ParsedProperty[] = [];
|
||||||
const elementOrDirectiveRefs: ElementOrDirectiveRef[] = [];
|
const elementOrDirectiveRefs: ElementOrDirectiveRef[] = [];
|
||||||
const elementVars: VariableAst[] = [];
|
const elementVars: t.VariableAst[] = [];
|
||||||
const events: BoundEventAst[] = [];
|
const events: t.BoundEventAst[] = [];
|
||||||
|
|
||||||
const templateElementOrDirectiveProps: BoundProperty[] = [];
|
const templateElementOrDirectiveProps: ParsedProperty[] = [];
|
||||||
const templateMatchableAttrs: [string, string][] = [];
|
const templateMatchableAttrs: [string, string][] = [];
|
||||||
const templateElementVars: VariableAst[] = [];
|
const templateElementVars: t.VariableAst[] = [];
|
||||||
|
|
||||||
let hasInlineTemplates = false;
|
let hasInlineTemplates = false;
|
||||||
const attrs: AttrAst[] = [];
|
const attrs: t.AttrAst[] = [];
|
||||||
const isTemplateElement = isNgTemplate(element.name);
|
const isTemplateElement = isNgTemplate(element.name);
|
||||||
|
|
||||||
element.attrs.forEach(attr => {
|
element.attrs.forEach(attr => {
|
||||||
|
const parsedVariables: ParsedVariable[] = [];
|
||||||
const hasBinding = this._parseAttr(
|
const hasBinding = this._parseAttr(
|
||||||
isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, events,
|
isTemplateElement, attr, matchableAttrs, elementOrDirectiveProps, events,
|
||||||
elementOrDirectiveRefs, elementVars);
|
elementOrDirectiveRefs, elementVars);
|
||||||
|
elementVars.push(...parsedVariables.map(v => t.VariableAst.fromParsedVariable(v)));
|
||||||
|
|
||||||
let templateValue: string|undefined;
|
let templateValue: string|undefined;
|
||||||
let templateKey: string|undefined;
|
let templateKey: string|undefined;
|
||||||
|
@ -306,9 +308,11 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
attr.sourceSpan);
|
attr.sourceSpan);
|
||||||
}
|
}
|
||||||
hasInlineTemplates = true;
|
hasInlineTemplates = true;
|
||||||
|
const parsedVariables: ParsedVariable[] = [];
|
||||||
this._bindingParser.parseInlineTemplateBinding(
|
this._bindingParser.parseInlineTemplateBinding(
|
||||||
templateKey !, templateValue !, attr.sourceSpan, templateMatchableAttrs,
|
templateKey !, templateValue !, attr.sourceSpan, templateMatchableAttrs,
|
||||||
templateElementOrDirectiveProps, templateElementVars);
|
templateElementOrDirectiveProps, parsedVariables);
|
||||||
|
templateElementVars.push(...parsedVariables.map(v => t.VariableAst.fromParsedVariable(v)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!hasBinding && !hasTemplateBinding) {
|
if (!hasBinding && !hasTemplateBinding) {
|
||||||
|
@ -321,12 +325,12 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
const elementCssSelector = createElementCssSelector(elName, matchableAttrs);
|
const elementCssSelector = createElementCssSelector(elName, matchableAttrs);
|
||||||
const {directives: directiveMetas, matchElement} =
|
const {directives: directiveMetas, matchElement} =
|
||||||
this._parseDirectives(this.selectorMatcher, elementCssSelector);
|
this._parseDirectives(this.selectorMatcher, elementCssSelector);
|
||||||
const references: ReferenceAst[] = [];
|
const references: t.ReferenceAst[] = [];
|
||||||
const boundDirectivePropNames = new Set<string>();
|
const boundDirectivePropNames = new Set<string>();
|
||||||
const directiveAsts = this._createDirectiveAsts(
|
const directiveAsts = this._createDirectiveAsts(
|
||||||
isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps,
|
isTemplateElement, element.name, directiveMetas, elementOrDirectiveProps,
|
||||||
elementOrDirectiveRefs, element.sourceSpan !, references, boundDirectivePropNames);
|
elementOrDirectiveRefs, element.sourceSpan !, references, boundDirectivePropNames);
|
||||||
const elementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
|
const elementProps: t.BoundElementPropertyAst[] = this._createElementPropertyAsts(
|
||||||
element.name, elementOrDirectiveProps, boundDirectivePropNames);
|
element.name, elementOrDirectiveProps, boundDirectivePropNames);
|
||||||
const isViewRoot = parent.isTemplateElement || hasInlineTemplates;
|
const isViewRoot = parent.isTemplateElement || hasInlineTemplates;
|
||||||
|
|
||||||
|
@ -334,7 +338,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
this.providerViewContext, parent.providerContext !, isViewRoot, directiveAsts, attrs,
|
this.providerViewContext, parent.providerContext !, isViewRoot, directiveAsts, attrs,
|
||||||
references, isTemplateElement, queryStartIndex, element.sourceSpan !);
|
references, isTemplateElement, queryStartIndex, element.sourceSpan !);
|
||||||
|
|
||||||
const children: TemplateAst[] = html.visitAll(
|
const children: t.TemplateAst[] = html.visitAll(
|
||||||
preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children,
|
preparsedElement.nonBindable ? NON_BINDABLE_VISITOR : this, element.children,
|
||||||
ElementContext.create(
|
ElementContext.create(
|
||||||
isTemplateElement, directiveAsts,
|
isTemplateElement, directiveAsts,
|
||||||
|
@ -345,7 +349,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
CssSelector.parse(preparsedElement.projectAs)[0] :
|
CssSelector.parse(preparsedElement.projectAs)[0] :
|
||||||
elementCssSelector;
|
elementCssSelector;
|
||||||
const ngContentIndex = parent.findNgContentIndex(projectionSelector) !;
|
const ngContentIndex = parent.findNgContentIndex(projectionSelector) !;
|
||||||
let parsedElement: TemplateAst;
|
let parsedElement: t.TemplateAst;
|
||||||
|
|
||||||
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
if (preparsedElement.type === PreparsedElementType.NG_CONTENT) {
|
||||||
// `<ng-content>` element
|
// `<ng-content>` element
|
||||||
|
@ -353,7 +357,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
this._reportError(`<ng-content> element cannot have content.`, element.sourceSpan !);
|
this._reportError(`<ng-content> element cannot have content.`, element.sourceSpan !);
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedElement = new NgContentAst(
|
parsedElement = new t.NgContentAst(
|
||||||
this.ngContentCount++, hasInlineTemplates ? null ! : ngContentIndex,
|
this.ngContentCount++, hasInlineTemplates ? null ! : ngContentIndex,
|
||||||
element.sourceSpan !);
|
element.sourceSpan !);
|
||||||
} else if (isTemplateElement) {
|
} else if (isTemplateElement) {
|
||||||
|
@ -362,7 +366,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
this._assertNoComponentsNorElementBindingsOnTemplate(
|
this._assertNoComponentsNorElementBindingsOnTemplate(
|
||||||
directiveAsts, elementProps, element.sourceSpan !);
|
directiveAsts, elementProps, element.sourceSpan !);
|
||||||
|
|
||||||
parsedElement = new EmbeddedTemplateAst(
|
parsedElement = new t.EmbeddedTemplateAst(
|
||||||
attrs, events, references, elementVars, providerContext.transformedDirectiveAsts,
|
attrs, events, references, elementVars, providerContext.transformedDirectiveAsts,
|
||||||
providerContext.transformProviders, providerContext.transformedHasViewContainer,
|
providerContext.transformProviders, providerContext.transformedHasViewContainer,
|
||||||
providerContext.queryMatches, children, hasInlineTemplates ? null ! : ngContentIndex,
|
providerContext.queryMatches, children, hasInlineTemplates ? null ! : ngContentIndex,
|
||||||
|
@ -374,7 +378,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
|
|
||||||
const ngContentIndex =
|
const ngContentIndex =
|
||||||
hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector);
|
hasInlineTemplates ? null : parent.findNgContentIndex(projectionSelector);
|
||||||
parsedElement = new ElementAst(
|
parsedElement = new t.ElementAst(
|
||||||
elName, attrs, elementProps, events, references, providerContext.transformedDirectiveAsts,
|
elName, attrs, elementProps, events, references, providerContext.transformedDirectiveAsts,
|
||||||
providerContext.transformProviders, providerContext.transformedHasViewContainer,
|
providerContext.transformProviders, providerContext.transformedHasViewContainer,
|
||||||
providerContext.queryMatches, children, hasInlineTemplates ? null : ngContentIndex,
|
providerContext.queryMatches, children, hasInlineTemplates ? null : ngContentIndex,
|
||||||
|
@ -390,7 +394,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
const templateDirectiveAsts = this._createDirectiveAsts(
|
const templateDirectiveAsts = this._createDirectiveAsts(
|
||||||
true, elName, directives, templateElementOrDirectiveProps, [], element.sourceSpan !, [],
|
true, elName, directives, templateElementOrDirectiveProps, [], element.sourceSpan !, [],
|
||||||
templateBoundDirectivePropNames);
|
templateBoundDirectivePropNames);
|
||||||
const templateElementProps: BoundElementPropertyAst[] = this._createElementPropertyAsts(
|
const templateElementProps: t.BoundElementPropertyAst[] = this._createElementPropertyAsts(
|
||||||
elName, templateElementOrDirectiveProps, templateBoundDirectivePropNames);
|
elName, templateElementOrDirectiveProps, templateBoundDirectivePropNames);
|
||||||
this._assertNoComponentsNorElementBindingsOnTemplate(
|
this._assertNoComponentsNorElementBindingsOnTemplate(
|
||||||
templateDirectiveAsts, templateElementProps, element.sourceSpan !);
|
templateDirectiveAsts, templateElementProps, element.sourceSpan !);
|
||||||
|
@ -399,7 +403,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
templateDirectiveAsts, [], [], true, templateQueryStartIndex, element.sourceSpan !);
|
templateDirectiveAsts, [], [], true, templateQueryStartIndex, element.sourceSpan !);
|
||||||
templateProviderContext.afterElement();
|
templateProviderContext.afterElement();
|
||||||
|
|
||||||
parsedElement = new EmbeddedTemplateAst(
|
parsedElement = new t.EmbeddedTemplateAst(
|
||||||
[], [], [], templateElementVars, templateProviderContext.transformedDirectiveAsts,
|
[], [], [], templateElementVars, templateProviderContext.transformedDirectiveAsts,
|
||||||
templateProviderContext.transformProviders,
|
templateProviderContext.transformProviders,
|
||||||
templateProviderContext.transformedHasViewContainer, templateProviderContext.queryMatches,
|
templateProviderContext.transformedHasViewContainer, templateProviderContext.queryMatches,
|
||||||
|
@ -411,15 +415,15 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
|
|
||||||
private _parseAttr(
|
private _parseAttr(
|
||||||
isTemplateElement: boolean, attr: html.Attribute, targetMatchableAttrs: string[][],
|
isTemplateElement: boolean, attr: html.Attribute, targetMatchableAttrs: string[][],
|
||||||
targetProps: BoundProperty[], targetEvents: BoundEventAst[],
|
targetProps: ParsedProperty[], targetEvents: t.BoundEventAst[],
|
||||||
targetRefs: ElementOrDirectiveRef[], targetVars: VariableAst[]): boolean {
|
targetRefs: ElementOrDirectiveRef[], targetVars: t.VariableAst[]): boolean {
|
||||||
const name = this._normalizeAttributeName(attr.name);
|
const name = this._normalizeAttributeName(attr.name);
|
||||||
const value = attr.value;
|
const value = attr.value;
|
||||||
const srcSpan = attr.sourceSpan;
|
const srcSpan = attr.sourceSpan;
|
||||||
|
|
||||||
|
const boundEvents: ParsedEvent[] = [];
|
||||||
const bindParts = name.match(BIND_NAME_REGEXP);
|
const bindParts = name.match(BIND_NAME_REGEXP);
|
||||||
let hasBinding = false;
|
let hasBinding = false;
|
||||||
const boundEvents: BoundEventAst[] = [];
|
|
||||||
|
|
||||||
if (bindParts !== null) {
|
if (bindParts !== null) {
|
||||||
hasBinding = true;
|
hasBinding = true;
|
||||||
|
@ -441,13 +445,13 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
|
|
||||||
} else if (bindParts[KW_ON_IDX]) {
|
} else if (bindParts[KW_ON_IDX]) {
|
||||||
this._bindingParser.parseEvent(
|
this._bindingParser.parseEvent(
|
||||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, boundEvents);
|
||||||
|
|
||||||
} else if (bindParts[KW_BINDON_IDX]) {
|
} else if (bindParts[KW_BINDON_IDX]) {
|
||||||
this._bindingParser.parsePropertyBinding(
|
this._bindingParser.parsePropertyBinding(
|
||||||
bindParts[IDENT_KW_IDX], value, false, srcSpan, targetMatchableAttrs, targetProps);
|
bindParts[IDENT_KW_IDX], value, false, srcSpan, targetMatchableAttrs, targetProps);
|
||||||
this._parseAssignmentEvent(
|
this._parseAssignmentEvent(
|
||||||
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
bindParts[IDENT_KW_IDX], value, srcSpan, targetMatchableAttrs, boundEvents);
|
||||||
|
|
||||||
} else if (bindParts[KW_AT_IDX]) {
|
} else if (bindParts[KW_AT_IDX]) {
|
||||||
this._bindingParser.parseLiteralAttr(
|
this._bindingParser.parseLiteralAttr(
|
||||||
|
@ -458,7 +462,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
bindParts[IDENT_BANANA_BOX_IDX], value, false, srcSpan, targetMatchableAttrs,
|
bindParts[IDENT_BANANA_BOX_IDX], value, false, srcSpan, targetMatchableAttrs,
|
||||||
targetProps);
|
targetProps);
|
||||||
this._parseAssignmentEvent(
|
this._parseAssignmentEvent(
|
||||||
bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
bindParts[IDENT_BANANA_BOX_IDX], value, srcSpan, targetMatchableAttrs, boundEvents);
|
||||||
|
|
||||||
} else if (bindParts[IDENT_PROPERTY_IDX]) {
|
} else if (bindParts[IDENT_PROPERTY_IDX]) {
|
||||||
this._bindingParser.parsePropertyBinding(
|
this._bindingParser.parsePropertyBinding(
|
||||||
|
@ -467,7 +471,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
|
|
||||||
} else if (bindParts[IDENT_EVENT_IDX]) {
|
} else if (bindParts[IDENT_EVENT_IDX]) {
|
||||||
this._bindingParser.parseEvent(
|
this._bindingParser.parseEvent(
|
||||||
bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, targetEvents);
|
bindParts[IDENT_EVENT_IDX], value, srcSpan, targetMatchableAttrs, boundEvents);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
hasBinding = this._bindingParser.parsePropertyInterpolation(
|
hasBinding = this._bindingParser.parsePropertyInterpolation(
|
||||||
|
@ -478,6 +482,8 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
this._bindingParser.parseLiteralAttr(name, value, srcSpan, targetMatchableAttrs, targetProps);
|
this._bindingParser.parseLiteralAttr(name, value, srcSpan, targetMatchableAttrs, targetProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
targetEvents.push(...boundEvents.map(e => t.BoundEventAst.fromParsedEvent(e)));
|
||||||
|
|
||||||
return hasBinding;
|
return hasBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -486,12 +492,12 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseVariable(
|
private _parseVariable(
|
||||||
identifier: string, value: string, sourceSpan: ParseSourceSpan, targetVars: VariableAst[]) {
|
identifier: string, value: string, sourceSpan: ParseSourceSpan, targetVars: t.VariableAst[]) {
|
||||||
if (identifier.indexOf('-') > -1) {
|
if (identifier.indexOf('-') > -1) {
|
||||||
this._reportError(`"-" is not allowed in variable names`, sourceSpan);
|
this._reportError(`"-" is not allowed in variable names`, sourceSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
targetVars.push(new VariableAst(identifier, value, sourceSpan));
|
targetVars.push(new t.VariableAst(identifier, value, sourceSpan));
|
||||||
}
|
}
|
||||||
|
|
||||||
private _parseReference(
|
private _parseReference(
|
||||||
|
@ -506,7 +512,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
|
|
||||||
private _parseAssignmentEvent(
|
private _parseAssignmentEvent(
|
||||||
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
name: string, expression: string, sourceSpan: ParseSourceSpan,
|
||||||
targetMatchableAttrs: string[][], targetEvents: BoundEventAst[]) {
|
targetMatchableAttrs: string[][], targetEvents: ParsedEvent[]) {
|
||||||
this._bindingParser.parseEvent(
|
this._bindingParser.parseEvent(
|
||||||
`${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, targetEvents);
|
`${name}Change`, `${expression}=$event`, sourceSpan, targetMatchableAttrs, targetEvents);
|
||||||
}
|
}
|
||||||
|
@ -533,9 +539,9 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
|
|
||||||
private _createDirectiveAsts(
|
private _createDirectiveAsts(
|
||||||
isTemplateElement: boolean, elementName: string, directives: CompileDirectiveSummary[],
|
isTemplateElement: boolean, elementName: string, directives: CompileDirectiveSummary[],
|
||||||
props: BoundProperty[], elementOrDirectiveRefs: ElementOrDirectiveRef[],
|
props: ParsedProperty[], elementOrDirectiveRefs: ElementOrDirectiveRef[],
|
||||||
elementSourceSpan: ParseSourceSpan, targetReferences: ReferenceAst[],
|
elementSourceSpan: ParseSourceSpan, targetReferences: t.ReferenceAst[],
|
||||||
targetBoundDirectivePropNames: Set<string>): DirectiveAst[] {
|
targetBoundDirectivePropNames: Set<string>): t.DirectiveAst[] {
|
||||||
const matchedReferences = new Set<string>();
|
const matchedReferences = new Set<string>();
|
||||||
let component: CompileDirectiveSummary = null !;
|
let component: CompileDirectiveSummary = null !;
|
||||||
|
|
||||||
|
@ -547,27 +553,32 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
if (directive.isComponent) {
|
if (directive.isComponent) {
|
||||||
component = directive;
|
component = directive;
|
||||||
}
|
}
|
||||||
const directiveProperties: BoundDirectivePropertyAst[] = [];
|
const directiveProperties: t.BoundDirectivePropertyAst[] = [];
|
||||||
let hostProperties =
|
const boundProperties =
|
||||||
this._bindingParser.createDirectiveHostPropertyAsts(directive, elementName, sourceSpan) !;
|
this._bindingParser.createDirectiveHostPropertyAsts(directive, elementName, sourceSpan) !;
|
||||||
|
|
||||||
|
let hostProperties =
|
||||||
|
boundProperties.map(prop => t.BoundElementPropertyAst.fromBoundProperty(prop));
|
||||||
// Note: We need to check the host properties here as well,
|
// Note: We need to check the host properties here as well,
|
||||||
// as we don't know the element name in the DirectiveWrapperCompiler yet.
|
// as we don't know the element name in the DirectiveWrapperCompiler yet.
|
||||||
hostProperties = this._checkPropertiesInSchema(elementName, hostProperties);
|
hostProperties = this._checkPropertiesInSchema(elementName, hostProperties);
|
||||||
const hostEvents = this._bindingParser.createDirectiveHostEventAsts(directive, sourceSpan) !;
|
const parsedEvents =
|
||||||
|
this._bindingParser.createDirectiveHostEventAsts(directive, sourceSpan) !;
|
||||||
this._createDirectivePropertyAsts(
|
this._createDirectivePropertyAsts(
|
||||||
directive.inputs, props, directiveProperties, targetBoundDirectivePropNames);
|
directive.inputs, props, directiveProperties, targetBoundDirectivePropNames);
|
||||||
elementOrDirectiveRefs.forEach((elOrDirRef) => {
|
elementOrDirectiveRefs.forEach((elOrDirRef) => {
|
||||||
if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
|
if ((elOrDirRef.value.length === 0 && directive.isComponent) ||
|
||||||
(elOrDirRef.isReferenceToDirective(directive))) {
|
(elOrDirRef.isReferenceToDirective(directive))) {
|
||||||
targetReferences.push(new ReferenceAst(
|
targetReferences.push(new t.ReferenceAst(
|
||||||
elOrDirRef.name, createTokenForReference(directive.type.reference), elOrDirRef.value,
|
elOrDirRef.name, createTokenForReference(directive.type.reference), elOrDirRef.value,
|
||||||
elOrDirRef.sourceSpan));
|
elOrDirRef.sourceSpan));
|
||||||
matchedReferences.add(elOrDirRef.name);
|
matchedReferences.add(elOrDirRef.name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
const hostEvents = parsedEvents.map(e => t.BoundEventAst.fromParsedEvent(e));
|
||||||
const contentQueryStartId = this.contentQueryStartId;
|
const contentQueryStartId = this.contentQueryStartId;
|
||||||
this.contentQueryStartId += directive.queries.length;
|
this.contentQueryStartId += directive.queries.length;
|
||||||
return new DirectiveAst(
|
return new t.DirectiveAst(
|
||||||
directive, directiveProperties, hostProperties, hostEvents, contentQueryStartId,
|
directive, directiveProperties, hostProperties, hostEvents, contentQueryStartId,
|
||||||
sourceSpan);
|
sourceSpan);
|
||||||
});
|
});
|
||||||
|
@ -585,18 +596,18 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
refToken = createTokenForExternalReference(this.reflector, Identifiers.TemplateRef);
|
refToken = createTokenForExternalReference(this.reflector, Identifiers.TemplateRef);
|
||||||
}
|
}
|
||||||
targetReferences.push(
|
targetReferences.push(
|
||||||
new ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.value, elOrDirRef.sourceSpan));
|
new t.ReferenceAst(elOrDirRef.name, refToken, elOrDirRef.value, elOrDirRef.sourceSpan));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return directiveAsts;
|
return directiveAsts;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createDirectivePropertyAsts(
|
private _createDirectivePropertyAsts(
|
||||||
directiveProperties: {[key: string]: string}, boundProps: BoundProperty[],
|
directiveProperties: {[key: string]: string}, boundProps: ParsedProperty[],
|
||||||
targetBoundDirectiveProps: BoundDirectivePropertyAst[],
|
targetBoundDirectiveProps: t.BoundDirectivePropertyAst[],
|
||||||
targetBoundDirectivePropNames: Set<string>) {
|
targetBoundDirectivePropNames: Set<string>) {
|
||||||
if (directiveProperties) {
|
if (directiveProperties) {
|
||||||
const boundPropsByName = new Map<string, BoundProperty>();
|
const boundPropsByName = new Map<string, ParsedProperty>();
|
||||||
boundProps.forEach(boundProp => {
|
boundProps.forEach(boundProp => {
|
||||||
const prevValue = boundPropsByName.get(boundProp.name);
|
const prevValue = boundPropsByName.get(boundProp.name);
|
||||||
if (!prevValue || prevValue.isLiteral) {
|
if (!prevValue || prevValue.isLiteral) {
|
||||||
|
@ -613,7 +624,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
if (boundProp) {
|
if (boundProp) {
|
||||||
targetBoundDirectivePropNames.add(boundProp.name);
|
targetBoundDirectivePropNames.add(boundProp.name);
|
||||||
if (!isEmptyExpression(boundProp.expression)) {
|
if (!isEmptyExpression(boundProp.expression)) {
|
||||||
targetBoundDirectiveProps.push(new BoundDirectivePropertyAst(
|
targetBoundDirectiveProps.push(new t.BoundDirectivePropertyAst(
|
||||||
dirProp, boundProp.name, boundProp.expression, boundProp.sourceSpan));
|
dirProp, boundProp.name, boundProp.expression, boundProp.sourceSpan));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -622,28 +633,29 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _createElementPropertyAsts(
|
private _createElementPropertyAsts(
|
||||||
elementName: string, props: BoundProperty[],
|
elementName: string, props: ParsedProperty[],
|
||||||
boundDirectivePropNames: Set<string>): BoundElementPropertyAst[] {
|
boundDirectivePropNames: Set<string>): t.BoundElementPropertyAst[] {
|
||||||
const boundElementProps: BoundElementPropertyAst[] = [];
|
const boundElementProps: t.BoundElementPropertyAst[] = [];
|
||||||
|
|
||||||
props.forEach((prop: BoundProperty) => {
|
props.forEach((prop: ParsedProperty) => {
|
||||||
if (!prop.isLiteral && !boundDirectivePropNames.has(prop.name)) {
|
if (!prop.isLiteral && !boundDirectivePropNames.has(prop.name)) {
|
||||||
boundElementProps.push(this._bindingParser.createElementPropertyAst(elementName, prop));
|
const boundProp = this._bindingParser.createBoundElementProperty(elementName, prop);
|
||||||
|
boundElementProps.push(t.BoundElementPropertyAst.fromBoundProperty(boundProp));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return this._checkPropertiesInSchema(elementName, boundElementProps);
|
return this._checkPropertiesInSchema(elementName, boundElementProps);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _findComponentDirectives(directives: DirectiveAst[]): DirectiveAst[] {
|
private _findComponentDirectives(directives: t.DirectiveAst[]): t.DirectiveAst[] {
|
||||||
return directives.filter(directive => directive.directive.isComponent);
|
return directives.filter(directive => directive.directive.isComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _findComponentDirectiveNames(directives: DirectiveAst[]): string[] {
|
private _findComponentDirectiveNames(directives: t.DirectiveAst[]): string[] {
|
||||||
return this._findComponentDirectives(directives)
|
return this._findComponentDirectives(directives)
|
||||||
.map(directive => identifierName(directive.directive.type) !);
|
.map(directive => identifierName(directive.directive.type) !);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _assertOnlyOneComponent(directives: DirectiveAst[], sourceSpan: ParseSourceSpan) {
|
private _assertOnlyOneComponent(directives: t.DirectiveAst[], sourceSpan: ParseSourceSpan) {
|
||||||
const componentTypeNames = this._findComponentDirectiveNames(directives);
|
const componentTypeNames = this._findComponentDirectiveNames(directives);
|
||||||
if (componentTypeNames.length > 1) {
|
if (componentTypeNames.length > 1) {
|
||||||
this._reportError(
|
this._reportError(
|
||||||
|
@ -682,7 +694,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _assertNoComponentsNorElementBindingsOnTemplate(
|
private _assertNoComponentsNorElementBindingsOnTemplate(
|
||||||
directives: DirectiveAst[], elementProps: BoundElementPropertyAst[],
|
directives: t.DirectiveAst[], elementProps: t.BoundElementPropertyAst[],
|
||||||
sourceSpan: ParseSourceSpan) {
|
sourceSpan: ParseSourceSpan) {
|
||||||
const componentTypeNames: string[] = this._findComponentDirectiveNames(directives);
|
const componentTypeNames: string[] = this._findComponentDirectiveNames(directives);
|
||||||
if (componentTypeNames.length > 0) {
|
if (componentTypeNames.length > 0) {
|
||||||
|
@ -697,7 +709,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
private _assertAllEventsPublishedByDirectives(
|
private _assertAllEventsPublishedByDirectives(
|
||||||
directives: DirectiveAst[], events: BoundEventAst[]) {
|
directives: t.DirectiveAst[], events: t.BoundEventAst[]) {
|
||||||
const allDirectiveEvents = new Set<string>();
|
const allDirectiveEvents = new Set<string>();
|
||||||
|
|
||||||
directives.forEach(directive => {
|
directives.forEach(directive => {
|
||||||
|
@ -716,12 +728,12 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private _checkPropertiesInSchema(elementName: string, boundProps: BoundElementPropertyAst[]):
|
private _checkPropertiesInSchema(elementName: string, boundProps: t.BoundElementPropertyAst[]):
|
||||||
BoundElementPropertyAst[] {
|
t.BoundElementPropertyAst[] {
|
||||||
// Note: We can't filter out empty expressions before this method,
|
// Note: We can't filter out empty expressions before this method,
|
||||||
// as we still want to validate them!
|
// as we still want to validate them!
|
||||||
return boundProps.filter((boundProp) => {
|
return boundProps.filter((boundProp) => {
|
||||||
if (boundProp.type === PropertyBindingType.Property &&
|
if (boundProp.type === t.PropertyBindingType.Property &&
|
||||||
!this._schemaRegistry.hasProperty(elementName, boundProp.name, this._schemas)) {
|
!this._schemaRegistry.hasProperty(elementName, boundProp.name, this._schemas)) {
|
||||||
let errorMsg =
|
let errorMsg =
|
||||||
`Can't bind to '${boundProp.name}' since it isn't a known property of '${elementName}'.`;
|
`Can't bind to '${boundProp.name}' since it isn't a known property of '${elementName}'.`;
|
||||||
|
@ -749,7 +761,7 @@ class TemplateParseVisitor implements html.Visitor {
|
||||||
}
|
}
|
||||||
|
|
||||||
class NonBindableVisitor implements html.Visitor {
|
class NonBindableVisitor implements html.Visitor {
|
||||||
visitElement(ast: html.Element, parent: ElementContext): ElementAst|null {
|
visitElement(ast: html.Element, parent: ElementContext): t.ElementAst|null {
|
||||||
const preparsedElement = preparseElement(ast);
|
const preparsedElement = preparseElement(ast);
|
||||||
if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
if (preparsedElement.type === PreparsedElementType.SCRIPT ||
|
||||||
preparsedElement.type === PreparsedElementType.STYLE ||
|
preparsedElement.type === PreparsedElementType.STYLE ||
|
||||||
|
@ -763,20 +775,20 @@ class NonBindableVisitor implements html.Visitor {
|
||||||
const attrNameAndValues = ast.attrs.map((attr): [string, string] => [attr.name, attr.value]);
|
const attrNameAndValues = ast.attrs.map((attr): [string, string] => [attr.name, attr.value]);
|
||||||
const selector = createElementCssSelector(ast.name, attrNameAndValues);
|
const selector = createElementCssSelector(ast.name, attrNameAndValues);
|
||||||
const ngContentIndex = parent.findNgContentIndex(selector);
|
const ngContentIndex = parent.findNgContentIndex(selector);
|
||||||
const children: TemplateAst[] = html.visitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT);
|
const children: t.TemplateAst[] = html.visitAll(this, ast.children, EMPTY_ELEMENT_CONTEXT);
|
||||||
return new ElementAst(
|
return new t.ElementAst(
|
||||||
ast.name, html.visitAll(this, ast.attrs), [], [], [], [], [], false, [], children,
|
ast.name, html.visitAll(this, ast.attrs), [], [], [], [], [], false, [], children,
|
||||||
ngContentIndex, ast.sourceSpan, ast.endSourceSpan);
|
ngContentIndex, ast.sourceSpan, ast.endSourceSpan);
|
||||||
}
|
}
|
||||||
visitComment(comment: html.Comment, context: any): any { return null; }
|
visitComment(comment: html.Comment, context: any): any { return null; }
|
||||||
|
|
||||||
visitAttribute(attribute: html.Attribute, context: any): AttrAst {
|
visitAttribute(attribute: html.Attribute, context: any): t.AttrAst {
|
||||||
return new AttrAst(attribute.name, attribute.value, attribute.sourceSpan);
|
return new t.AttrAst(attribute.name, attribute.value, attribute.sourceSpan);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitText(text: html.Text, parent: ElementContext): TextAst {
|
visitText(text: html.Text, parent: ElementContext): t.TextAst {
|
||||||
const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR) !;
|
const ngContentIndex = parent.findNgContentIndex(TEXT_CSS_SELECTOR) !;
|
||||||
return new TextAst(text.value, ngContentIndex, text.sourceSpan !);
|
return new t.TextAst(text.value, ngContentIndex, text.sourceSpan !);
|
||||||
}
|
}
|
||||||
|
|
||||||
visitExpansion(expansion: html.Expansion, context: any): any { return expansion; }
|
visitExpansion(expansion: html.Expansion, context: any): any { return expansion; }
|
||||||
|
@ -811,7 +823,7 @@ export function splitClasses(classAttrValue: string): string[] {
|
||||||
|
|
||||||
class ElementContext {
|
class ElementContext {
|
||||||
static create(
|
static create(
|
||||||
isTemplateElement: boolean, directives: DirectiveAst[],
|
isTemplateElement: boolean, directives: t.DirectiveAst[],
|
||||||
providerContext: ProviderElementContext): ElementContext {
|
providerContext: ProviderElementContext): ElementContext {
|
||||||
const matcher = new SelectorMatcher();
|
const matcher = new SelectorMatcher();
|
||||||
let wildcardNgContentIndex: number = null !;
|
let wildcardNgContentIndex: number = null !;
|
||||||
|
|
|
@ -278,6 +278,9 @@
|
||||||
{
|
{
|
||||||
"name": "BoundDirectivePropertyAst"
|
"name": "BoundDirectivePropertyAst"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "BoundElementProperty"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "BoundElementPropertyAst"
|
"name": "BoundElementPropertyAst"
|
||||||
},
|
},
|
||||||
|
@ -285,10 +288,7 @@
|
||||||
"name": "BoundEventAst"
|
"name": "BoundEventAst"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "BoundProperty"
|
"name": "BoundPropertyMapping"
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "BoundPropertyType"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "BoundTextAst"
|
"name": "BoundTextAst"
|
||||||
|
@ -1235,6 +1235,18 @@
|
||||||
{
|
{
|
||||||
"name": "ParseTreeResult"
|
"name": "ParseTreeResult"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ParsedEvent"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ParsedProperty"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ParsedPropertyType"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "ParsedVariable"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "Parser"
|
"name": "Parser"
|
||||||
},
|
},
|
||||||
|
@ -2195,9 +2207,6 @@
|
||||||
{
|
{
|
||||||
"name": "_global"
|
"name": "_global"
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"name": "_isAnimationLabel"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "_isClosingComment"
|
"name": "_isClosingComment"
|
||||||
},
|
},
|
||||||
|
@ -3221,6 +3230,9 @@
|
||||||
{
|
{
|
||||||
"name": "invalid"
|
"name": "invalid"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "isAnimationLabel"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "isArray"
|
"name": "isArray"
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue