feat(animations): allow animation integration support into host params
Closes #9044 Closes #9933
This commit is contained in:
@ -15,7 +15,7 @@ import {BaseException} from '../facade/exceptions';
|
||||
import {isArray, isBlank, isPresent} from '../facade/lang';
|
||||
import {Identifiers} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {PropertyBindingType, TemplateAst, TemplateAstVisitor, NgContentAst, EmbeddedTemplateAst, ElementAst, ReferenceAst, VariableAst, BoundEventAst, BoundElementPropertyAst, AttrAst, BoundTextAst, TextAst, DirectiveAst, BoundDirectivePropertyAst, templateVisitAll,} from '../template_ast';
|
||||
import * as t from '../template_ast';
|
||||
|
||||
import {AnimationAst, AnimationAstVisitor, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStepAst, AnimationStylesAst} from './animation_ast';
|
||||
import {AnimationParseError, ParsedAnimationResult, parseAnimationEntry} from './animation_parser';
|
||||
@ -28,10 +28,9 @@ export class CompiledAnimation {
|
||||
}
|
||||
|
||||
export class AnimationCompiler {
|
||||
compileComponent(component: CompileDirectiveMetadata, template: TemplateAst[]):
|
||||
compileComponent(component: CompileDirectiveMetadata, template: t.TemplateAst[]):
|
||||
CompiledAnimation[] {
|
||||
var compiledAnimations: CompiledAnimation[] = [];
|
||||
var index = 0;
|
||||
var groupedErrors: string[] = [];
|
||||
var triggerLookup: {[key: string]: CompiledAnimation} = {};
|
||||
var componentName = component.type.name;
|
||||
@ -44,7 +43,6 @@ export class AnimationCompiler {
|
||||
`Unable to parse the animation sequence for "${triggerName}" due to the following errors:`;
|
||||
result.errors.forEach(
|
||||
(error: AnimationParseError) => { errorMessage += '\n-- ' + error.msg; });
|
||||
// todo (matsko): include the component name when throwing
|
||||
groupedErrors.push(errorMessage);
|
||||
}
|
||||
|
||||
@ -52,11 +50,9 @@ export class AnimationCompiler {
|
||||
groupedErrors.push(
|
||||
`The animation trigger "${triggerName}" has already been registered on "${componentName}"`);
|
||||
} else {
|
||||
var factoryName = `${component.type.name}_${entry.name}_${index}`;
|
||||
index++;
|
||||
|
||||
var factoryName = `${componentName}_${entry.name}`;
|
||||
var visitor = new _AnimationBuilder(triggerName, factoryName);
|
||||
var compileResult = visitor.build(result.ast)
|
||||
var compileResult = visitor.build(result.ast);
|
||||
compiledAnimations.push(compileResult);
|
||||
triggerLookup[entry.name] = compileResult;
|
||||
}
|
||||
@ -387,14 +383,13 @@ function _getStylesArray(obj: any): {[key: string]: any}[] {
|
||||
}
|
||||
|
||||
function _validateAnimationProperties(
|
||||
compiledAnimations: CompiledAnimation[], template: TemplateAst[]): AnimationParseError[] {
|
||||
compiledAnimations: CompiledAnimation[], template: t.TemplateAst[]): AnimationParseError[] {
|
||||
var visitor = new _AnimationTemplatePropertyVisitor(compiledAnimations);
|
||||
templateVisitAll(visitor, template);
|
||||
t.templateVisitAll(visitor, template);
|
||||
return visitor.errors;
|
||||
}
|
||||
|
||||
class _AnimationTemplatePropertyVisitor implements TemplateAstVisitor {
|
||||
private _nodeIndex: number = 0;
|
||||
class _AnimationTemplatePropertyVisitor implements t.TemplateAstVisitor {
|
||||
private _animationRegistry: {[key: string]: boolean} = {};
|
||||
|
||||
public errors: AnimationParseError[] = [];
|
||||
@ -403,9 +398,9 @@ class _AnimationTemplatePropertyVisitor implements TemplateAstVisitor {
|
||||
animations.forEach(entry => { this._animationRegistry[entry.name] = true; });
|
||||
}
|
||||
|
||||
visitElement(ast: ElementAst, ctx: any): any {
|
||||
visitElement(ast: t.ElementAst, ctx: any): any {
|
||||
ast.inputs.forEach(input => {
|
||||
if (input.type == PropertyBindingType.Animation) {
|
||||
if (input.type == t.PropertyBindingType.Animation) {
|
||||
var animationName = input.name;
|
||||
if (!isPresent(this._animationRegistry[animationName])) {
|
||||
this.errors.push(
|
||||
@ -413,21 +408,18 @@ class _AnimationTemplatePropertyVisitor implements TemplateAstVisitor {
|
||||
}
|
||||
}
|
||||
});
|
||||
templateVisitAll(this, ast.children);
|
||||
t.templateVisitAll(this, ast.children);
|
||||
}
|
||||
|
||||
visitBoundText(ast: BoundTextAst, ctx: any): any { this._nodeIndex++; }
|
||||
|
||||
visitText(ast: TextAst, ctx: any): any { this._nodeIndex++; }
|
||||
|
||||
visitEmbeddedTemplate(ast: EmbeddedTemplateAst, ctx: any): any { this._nodeIndex++; }
|
||||
|
||||
visitNgContent(ast: NgContentAst, ctx: any): any {}
|
||||
visitAttr(ast: AttrAst, ctx: any): any {}
|
||||
visitDirective(ast: DirectiveAst, ctx: any): any {}
|
||||
visitEvent(ast: BoundEventAst, ctx: any): any {}
|
||||
visitReference(ast: ReferenceAst, ctx: any): any {}
|
||||
visitVariable(ast: VariableAst, ctx: any): any {}
|
||||
visitDirectiveProperty(ast: BoundDirectivePropertyAst, ctx: any): any {}
|
||||
visitElementProperty(ast: BoundElementPropertyAst, ctx: any): any {}
|
||||
visitBoundText(ast: t.BoundTextAst, ctx: any): any {}
|
||||
visitText(ast: t.TextAst, ctx: any): any {}
|
||||
visitEmbeddedTemplate(ast: t.EmbeddedTemplateAst, ctx: any): any {}
|
||||
visitNgContent(ast: t.NgContentAst, ctx: any): any {}
|
||||
visitAttr(ast: t.AttrAst, ctx: any): any {}
|
||||
visitDirective(ast: t.DirectiveAst, ctx: any): any {}
|
||||
visitEvent(ast: t.BoundEventAst, ctx: any): any {}
|
||||
visitReference(ast: t.ReferenceAst, ctx: any): any {}
|
||||
visitVariable(ast: t.VariableAst, ctx: any): any {}
|
||||
visitDirectiveProperty(ast: t.BoundDirectivePropertyAst, ctx: any): any {}
|
||||
visitElementProperty(ast: t.BoundElementPropertyAst, ctx: any): any {}
|
||||
}
|
||||
|
@ -18,9 +18,11 @@ import {getUrlScheme} from './url_resolver';
|
||||
import {sanitizeIdentifier, splitAtColon} from './util';
|
||||
|
||||
|
||||
|
||||
// group 0: "[prop] or (event) or @trigger"
|
||||
// group 1: "prop" from "[prop]"
|
||||
// group 2: "event" from "(event)"
|
||||
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))$/g;
|
||||
// group 3: "@trigger" from "@trigger"
|
||||
var HOST_REG_EXP = /^(?:(?:\[([^\]]+)\])|(?:\(([^\)]+)\)))|(\@[-\w]+)$/g;
|
||||
|
||||
export abstract class CompileMetadataWithIdentifier {
|
||||
abstract toJson(): {[key: string]: any};
|
||||
@ -741,6 +743,8 @@ export class CompileDirectiveMetadata implements CompileMetadataWithType {
|
||||
hostProperties[matches[1]] = value;
|
||||
} else if (isPresent(matches[2])) {
|
||||
hostListeners[matches[2]] = value;
|
||||
} else if (isPresent(matches[3])) {
|
||||
hostProperties[matches[3]] = value;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -774,13 +774,23 @@ class TemplateParseVisitor implements HtmlAstVisitor {
|
||||
const parts = name.split(PROPERTY_PARTS_SEPARATOR);
|
||||
let securityContext: SecurityContext;
|
||||
if (parts.length === 1) {
|
||||
boundPropertyName = this._schemaRegistry.getMappedPropName(parts[0]);
|
||||
securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName);
|
||||
bindingType = PropertyBindingType.Property;
|
||||
if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName)) {
|
||||
var partValue = parts[0];
|
||||
if (partValue[0] == '@') {
|
||||
boundPropertyName = partValue.substr(1);
|
||||
bindingType = PropertyBindingType.Animation;
|
||||
securityContext = SecurityContext.NONE;
|
||||
this._reportError(
|
||||
`Can't bind to '${boundPropertyName}' since it isn't a known native property`,
|
||||
sourceSpan);
|
||||
`Assigning animation triggers within host data as attributes such as "@prop": "exp" is deprecated. Use "[@prop]": "exp" instead!`,
|
||||
sourceSpan, ParseErrorLevel.WARNING);
|
||||
} else {
|
||||
boundPropertyName = this._schemaRegistry.getMappedPropName(partValue);
|
||||
securityContext = this._schemaRegistry.securityContext(elementName, boundPropertyName);
|
||||
bindingType = PropertyBindingType.Property;
|
||||
if (!this._schemaRegistry.hasProperty(elementName, boundPropertyName)) {
|
||||
this._reportError(
|
||||
`Can't bind to '${boundPropertyName}' since it isn't a known native property`,
|
||||
sourceSpan);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (parts[0] == ATTRIBUTE_PREFIX) {
|
||||
|
@ -65,16 +65,14 @@ export class CompileView implements NameResolver {
|
||||
public literalArrayCount = 0;
|
||||
public literalMapCount = 0;
|
||||
public pipeCount = 0;
|
||||
public animations = new Map<string, CompiledAnimation>();
|
||||
|
||||
public componentContext: o.Expression;
|
||||
|
||||
constructor(
|
||||
public component: CompileDirectiveMetadata, public genConfig: CompilerConfig,
|
||||
public pipeMetas: CompilePipeMetadata[], public styles: o.Expression,
|
||||
animations: CompiledAnimation[], public viewIndex: number,
|
||||
public animations: CompiledAnimation[], public viewIndex: number,
|
||||
public declarationElement: CompileElement, public templateVariableBindings: string[][]) {
|
||||
animations.forEach(entry => this.animations.set(entry.name, entry));
|
||||
this.createMethod = new CompileMethod(this);
|
||||
this.injectorGetMethod = new CompileMethod(this);
|
||||
this.updateContentQueriesMethod = new CompileMethod(this);
|
||||
|
@ -6,8 +6,6 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {BaseException, SecurityContext} from '@angular/core';
|
||||
|
||||
import {EMPTY_STATE as EMPTY_ANIMATION_STATE, LifecycleHooks, isDefaultChangeDetectionStrategy} from '../../core_private';
|
||||
import * as cdAst from '../expression_parser/ast';
|
||||
import {isBlank, isPresent} from '../facade/lang';
|
||||
@ -21,8 +19,7 @@ import {CompileMethod} from './compile_method';
|
||||
import {camelCaseToDashCase} from '../util';
|
||||
import {convertCdExpressionToIr} from './expression_converter';
|
||||
import {CompileBinding} from './compile_binding';
|
||||
import {BaseException, SecurityContext} from '@angular/core';
|
||||
|
||||
import {SecurityContext} from '@angular/core';
|
||||
|
||||
function createBindFieldExpr(exprIndex: number): o.ReadPropExpr {
|
||||
return o.THIS_EXPR.prop(`_expr_${exprIndex}`);
|
||||
@ -85,7 +82,8 @@ export function bindRenderText(
|
||||
}
|
||||
|
||||
function bindAndWriteToRenderer(
|
||||
boundProps: BoundElementPropertyAst[], context: o.Expression, compileElement: CompileElement) {
|
||||
boundProps: BoundElementPropertyAst[], context: o.Expression, compileElement: CompileElement,
|
||||
isHostProp: boolean) {
|
||||
var view = compileElement.view;
|
||||
var renderNode = compileElement.renderNode;
|
||||
boundProps.forEach((boundProp) => {
|
||||
@ -129,6 +127,7 @@ function bindAndWriteToRenderer(
|
||||
if (isPresent(boundProp.unit)) {
|
||||
strValue = strValue.plus(o.literal(boundProp.unit));
|
||||
}
|
||||
|
||||
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, strValue);
|
||||
updateStmts.push(
|
||||
o.THIS_EXPR.prop('renderer')
|
||||
@ -137,7 +136,13 @@ function bindAndWriteToRenderer(
|
||||
break;
|
||||
case PropertyBindingType.Animation:
|
||||
var animationName = boundProp.name;
|
||||
var animation = view.componentView.animations.get(animationName);
|
||||
var targetViewExpr: o.Expression = o.THIS_EXPR;
|
||||
if (isHostProp) {
|
||||
targetViewExpr = compileElement.appElement.prop('componentView');
|
||||
}
|
||||
|
||||
var animationFnExpr =
|
||||
targetViewExpr.prop('componentType').prop('animations').key(o.literal(animationName));
|
||||
|
||||
// it's important to normalize the void value as `void` explicitly
|
||||
// so that the styles data can be obtained from the stringmap
|
||||
@ -158,11 +163,10 @@ function bindAndWriteToRenderer(
|
||||
[newRenderVar.set(emptyStateValue).toStmt()]));
|
||||
|
||||
updateStmts.push(
|
||||
animation.fnVariable.callFn([o.THIS_EXPR, renderNode, oldRenderVar, newRenderVar])
|
||||
.toStmt());
|
||||
animationFnExpr.callFn([o.THIS_EXPR, renderNode, oldRenderVar, newRenderVar]).toStmt());
|
||||
|
||||
view.detachMethod.addStmt(
|
||||
animation.fnVariable.callFn([o.THIS_EXPR, renderNode, oldRenderValue, emptyStateValue])
|
||||
animationFnExpr.callFn([o.THIS_EXPR, renderNode, oldRenderValue, emptyStateValue])
|
||||
.toStmt());
|
||||
|
||||
if (!_animationViewCheckedFlagMap.get(view)) {
|
||||
@ -212,13 +216,13 @@ function sanitizedValue(
|
||||
|
||||
export function bindRenderInputs(
|
||||
boundProps: BoundElementPropertyAst[], compileElement: CompileElement): void {
|
||||
bindAndWriteToRenderer(boundProps, compileElement.view.componentContext, compileElement);
|
||||
bindAndWriteToRenderer(boundProps, compileElement.view.componentContext, compileElement, false);
|
||||
}
|
||||
|
||||
export function bindDirectiveHostProps(
|
||||
directiveAst: DirectiveAst, directiveInstance: o.Expression,
|
||||
compileElement: CompileElement): void {
|
||||
bindAndWriteToRenderer(directiveAst.hostProperties, directiveInstance, compileElement);
|
||||
bindAndWriteToRenderer(directiveAst.hostProperties, directiveInstance, compileElement, true);
|
||||
}
|
||||
|
||||
export function bindDirectiveInputs(
|
||||
|
@ -500,6 +500,7 @@ function createViewFactory(
|
||||
templateUrlInfo = view.component.template.templateUrl;
|
||||
}
|
||||
if (view.viewIndex === 0) {
|
||||
var animationsExpr = o.literalMap(view.animations.map(entry => [entry.name, entry.fnVariable]));
|
||||
initRenderCompTypeStmts = [new o.IfStmt(renderCompTypeVar.identical(o.NULL_EXPR), [
|
||||
renderCompTypeVar
|
||||
.set(ViewConstructorVars.viewUtils.callMethod(
|
||||
@ -507,7 +508,8 @@ function createViewFactory(
|
||||
[
|
||||
o.literal(templateUrlInfo),
|
||||
o.literal(view.component.template.ngContentSelectors.length),
|
||||
ViewEncapsulationEnum.fromValue(view.component.template.encapsulation), view.styles
|
||||
ViewEncapsulationEnum.fromValue(view.component.template.encapsulation), view.styles,
|
||||
animationsExpr
|
||||
]))
|
||||
.toStmt()
|
||||
])];
|
||||
|
Reference in New Issue
Block a user