refactor(animations): ensure animation input/outputs are managed within the template parser (#11782)
Closes #11782 Closes #11601 Related #11707
This commit is contained in:

committed by
Rado Kirov

parent
45ad13560b
commit
f1b6c6efa1
@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {CompiledAnimationTriggerResult} from '../animation/animation_compiler';
|
||||
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompilePipeMetadata, CompileTokenMetadata} from '../compile_metadata';
|
||||
import {CompilerConfig} from '../config';
|
||||
import {ListWrapper, MapWrapper} from '../facade/collection';
|
||||
@ -72,7 +72,7 @@ export class CompileView implements NameResolver {
|
||||
constructor(
|
||||
public component: CompileDirectiveMetadata, public genConfig: CompilerConfig,
|
||||
public pipeMetas: CompilePipeMetadata[], public styles: o.Expression,
|
||||
public animations: CompiledAnimationTriggerResult[], public viewIndex: number,
|
||||
public animations: AnimationEntryCompileResult[], public viewIndex: number,
|
||||
public declarationElement: CompileElement, public templateVariableBindings: string[][]) {
|
||||
this.createMethod = new CompileMethod(this);
|
||||
this.animationBindingsMethod = new CompileMethod(this);
|
||||
|
@ -11,7 +11,6 @@ import {ListWrapper, StringMapWrapper} from '../facade/collection';
|
||||
import {StringWrapper, isBlank, isPresent} from '../facade/lang';
|
||||
import {Identifiers, identifierToken, resolveIdentifier} from '../identifiers';
|
||||
import * as o from '../output/output_ast';
|
||||
import {AnimationOutput} from '../private_import_core';
|
||||
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';
|
||||
|
||||
import {CompileBinding} from './compile_binding';
|
||||
@ -20,10 +19,6 @@ import {CompileMethod} from './compile_method';
|
||||
import {EventHandlerVars, ViewProperties} from './constants';
|
||||
import {convertCdStatementToIr} from './expression_converter';
|
||||
|
||||
export class CompileElementAnimationOutput {
|
||||
constructor(public listener: CompileEventListener, public output: AnimationOutput) {}
|
||||
}
|
||||
|
||||
export class CompileEventListener {
|
||||
private _method: CompileMethod;
|
||||
private _hasComponentHostListener: boolean = false;
|
||||
@ -32,13 +27,14 @@ export class CompileEventListener {
|
||||
private _actionResultExprs: o.Expression[] = [];
|
||||
|
||||
static getOrCreate(
|
||||
compileElement: CompileElement, eventTarget: string, eventName: string,
|
||||
compileElement: CompileElement, eventTarget: string, eventName: string, eventPhase: string,
|
||||
targetEventListeners: CompileEventListener[]): CompileEventListener {
|
||||
var listener = targetEventListeners.find(
|
||||
listener => listener.eventTarget == eventTarget && listener.eventName == eventName);
|
||||
listener => listener.eventTarget == eventTarget && listener.eventName == eventName &&
|
||||
listener.eventPhase == eventPhase);
|
||||
if (isBlank(listener)) {
|
||||
listener = new CompileEventListener(
|
||||
compileElement, eventTarget, eventName, targetEventListeners.length);
|
||||
compileElement, eventTarget, eventName, eventPhase, targetEventListeners.length);
|
||||
targetEventListeners.push(listener);
|
||||
}
|
||||
return listener;
|
||||
@ -48,7 +44,7 @@ export class CompileEventListener {
|
||||
|
||||
constructor(
|
||||
public compileElement: CompileElement, public eventTarget: string, public eventName: string,
|
||||
listenerIndex: number) {
|
||||
public eventPhase: string, listenerIndex: number) {
|
||||
this._method = new CompileMethod(compileElement.view);
|
||||
this._methodName =
|
||||
`_handle_${santitizeEventName(eventName)}_${compileElement.nodeIndex}_${listenerIndex}`;
|
||||
@ -119,7 +115,7 @@ export class CompileEventListener {
|
||||
disposable.set(listenExpr).toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
|
||||
}
|
||||
|
||||
listenToAnimation(output: AnimationOutput) {
|
||||
listenToAnimation() {
|
||||
var outputListener = o.THIS_EXPR.callMethod(
|
||||
'eventHandler',
|
||||
[o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])]);
|
||||
@ -129,11 +125,8 @@ export class CompileEventListener {
|
||||
.callMethod(
|
||||
'registerAnimationOutput',
|
||||
[
|
||||
this.compileElement.renderNode,
|
||||
o.importExpr(resolveIdentifier(Identifiers.AnimationOutput)).instantiate([
|
||||
o.literal(output.name), o.literal(output.phase)
|
||||
]),
|
||||
outputListener
|
||||
this.compileElement.renderNode, o.literal(this.eventName),
|
||||
o.literal(this.eventPhase), outputListener
|
||||
])
|
||||
.toStmt();
|
||||
this.compileElement.view.createMethod.addStmt(stmt);
|
||||
@ -160,7 +153,7 @@ export function collectEventListeners(
|
||||
hostEvents.forEach((hostEvent) => {
|
||||
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
|
||||
var listener = CompileEventListener.getOrCreate(
|
||||
compileElement, hostEvent.target, hostEvent.name, eventListeners);
|
||||
compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners);
|
||||
listener.addAction(hostEvent, null, null);
|
||||
});
|
||||
dirs.forEach((directiveAst) => {
|
||||
@ -169,7 +162,7 @@ export function collectEventListeners(
|
||||
directiveAst.hostEvents.forEach((hostEvent) => {
|
||||
compileElement.view.bindings.push(new CompileBinding(compileElement, hostEvent));
|
||||
var listener = CompileEventListener.getOrCreate(
|
||||
compileElement, hostEvent.target, hostEvent.name, eventListeners);
|
||||
compileElement, hostEvent.target, hostEvent.name, hostEvent.phase, eventListeners);
|
||||
listener.addAction(hostEvent, directiveAst.directive, directiveInstance);
|
||||
});
|
||||
});
|
||||
@ -190,11 +183,13 @@ export function bindDirectiveOutputs(
|
||||
}
|
||||
|
||||
export function bindRenderOutputs(eventListeners: CompileEventListener[]) {
|
||||
eventListeners.forEach(listener => listener.listenToRenderer());
|
||||
}
|
||||
|
||||
export function bindAnimationOutputs(eventListeners: CompileElementAnimationOutput[]) {
|
||||
eventListeners.forEach(entry => { entry.listener.listenToAnimation(entry.output); });
|
||||
eventListeners.forEach(listener => {
|
||||
if (listener.eventPhase) {
|
||||
listener.listenToAnimation();
|
||||
} else {
|
||||
listener.listenToRenderer();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function convertStmtIntoExpression(stmt: o.Statement): o.Expression {
|
||||
|
@ -31,8 +31,6 @@ function createCurrValueExpr(exprIndex: number): o.ReadVarExpr {
|
||||
return o.variable(`currVal_${exprIndex}`); // fix syntax highlighting: `
|
||||
}
|
||||
|
||||
const _animationViewCheckedFlagMap = new Map<CompileView, boolean>();
|
||||
|
||||
function bind(
|
||||
view: CompileView, currValExpr: o.ReadVarExpr, fieldExpr: o.ReadPropExpr,
|
||||
parsedExpression: cdAst.AST, context: o.Expression, actions: o.Statement[],
|
||||
|
@ -7,18 +7,16 @@
|
||||
*/
|
||||
import {ListWrapper} from '../facade/collection';
|
||||
import {identifierToken} from '../identifiers';
|
||||
import {AnimationOutput} from '../private_import_core';
|
||||
import {AttrAst, BoundDirectivePropertyAst, BoundElementPropertyAst, BoundEventAst, BoundTextAst, DirectiveAst, ElementAst, EmbeddedTemplateAst, NgContentAst, ReferenceAst, TemplateAst, TemplateAstVisitor, TextAst, VariableAst, templateVisitAll} from '../template_parser/template_ast';
|
||||
|
||||
import {CompileElement, CompileNode} from './compile_element';
|
||||
import {CompileView} from './compile_view';
|
||||
import {CompileElementAnimationOutput, CompileEventListener, bindAnimationOutputs, bindDirectiveOutputs, bindRenderOutputs, collectEventListeners} from './event_binder';
|
||||
import {CompileEventListener, bindDirectiveOutputs, bindRenderOutputs, collectEventListeners} from './event_binder';
|
||||
import {bindDirectiveAfterContentLifecycleCallbacks, bindDirectiveAfterViewLifecycleCallbacks, bindDirectiveDetectChangesLifecycleCallbacks, bindInjectableDestroyLifecycleCallbacks, bindPipeDestroyLifecycleCallbacks} from './lifecycle_binder';
|
||||
import {bindDirectiveHostProps, bindDirectiveInputs, bindRenderInputs, bindRenderText} from './property_binder';
|
||||
|
||||
export function bindView(
|
||||
view: CompileView, parsedTemplate: TemplateAst[], animationOutputs: AnimationOutput[]): void {
|
||||
var visitor = new ViewBinderVisitor(view, animationOutputs);
|
||||
export function bindView(view: CompileView, parsedTemplate: TemplateAst[]): void {
|
||||
var visitor = new ViewBinderVisitor(view);
|
||||
templateVisitAll(visitor, parsedTemplate);
|
||||
view.pipes.forEach(
|
||||
(pipe) => { bindPipeDestroyLifecycleCallbacks(pipe.meta, pipe.instance, pipe.view); });
|
||||
@ -26,12 +24,8 @@ export function bindView(
|
||||
|
||||
class ViewBinderVisitor implements TemplateAstVisitor {
|
||||
private _nodeIndex: number = 0;
|
||||
private _animationOutputsMap: {[key: string]: AnimationOutput} = {};
|
||||
|
||||
constructor(public view: CompileView, public animationOutputs: AnimationOutput[]) {
|
||||
animationOutputs.forEach(
|
||||
entry => { this._animationOutputsMap[entry.fullPropertyName] = entry; });
|
||||
}
|
||||
constructor(public view: CompileView) {}
|
||||
|
||||
visitBoundText(ast: BoundTextAst, parent: CompileElement): any {
|
||||
var node = this.view.nodes[this._nodeIndex++];
|
||||
@ -48,22 +42,9 @@ class ViewBinderVisitor implements TemplateAstVisitor {
|
||||
visitElement(ast: ElementAst, parent: CompileElement): any {
|
||||
var compileElement = <CompileElement>this.view.nodes[this._nodeIndex++];
|
||||
var eventListeners: CompileEventListener[] = [];
|
||||
var animationEventListeners: CompileElementAnimationOutput[] = [];
|
||||
collectEventListeners(ast.outputs, ast.directives, compileElement).forEach(entry => {
|
||||
// TODO: figure out how to abstract this `if` statement elsewhere
|
||||
if (entry.eventName[0] == '@') {
|
||||
let animationOutputName = entry.eventName.substr(1);
|
||||
let output = this._animationOutputsMap[animationOutputName];
|
||||
// no need to report an error here since the parser will
|
||||
// have caught the missing animation trigger definition
|
||||
if (output) {
|
||||
animationEventListeners.push(new CompileElementAnimationOutput(entry, output));
|
||||
}
|
||||
} else {
|
||||
eventListeners.push(entry);
|
||||
}
|
||||
eventListeners.push(entry);
|
||||
});
|
||||
bindAnimationOutputs(animationEventListeners);
|
||||
bindRenderInputs(ast.inputs, compileElement);
|
||||
bindRenderOutputs(eventListeners);
|
||||
ast.directives.forEach((directiveAst) => {
|
||||
@ -108,7 +89,7 @@ class ViewBinderVisitor implements TemplateAstVisitor {
|
||||
var providerInstance = compileElement.instances.get(providerAst.token.reference);
|
||||
bindInjectableDestroyLifecycleCallbacks(providerAst, providerInstance, compileElement);
|
||||
});
|
||||
bindView(compileElement.embeddedView, ast.children, this.animationOutputs);
|
||||
bindView(compileElement.embeddedView, ast.children);
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,6 @@
|
||||
*/
|
||||
|
||||
import {ChangeDetectionStrategy, ViewEncapsulation} from '@angular/core';
|
||||
|
||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||
import {CompileDirectiveMetadata, CompileIdentifierMetadata, CompileTokenMetadata, CompileTypeMetadata} from '../compile_metadata';
|
||||
import {ListWrapper, SetWrapper, StringMapWrapper} from '../facade/collection';
|
||||
import {StringWrapper, isPresent} from '../facade/lang';
|
||||
@ -65,8 +63,6 @@ export function finishView(view: CompileView, targetStatements: o.Statement[]) {
|
||||
class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
nestedViewCount: number = 0;
|
||||
|
||||
private _animationCompiler = new AnimationCompiler();
|
||||
|
||||
constructor(
|
||||
public view: CompileView,
|
||||
public targetDependencies: Array<ViewFactoryDependency|ComponentFactoryDependency>) {}
|
||||
@ -279,12 +275,10 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
|
||||
ast.hasViewContainer, true, ast.references);
|
||||
this.view.nodes.push(compileElement);
|
||||
|
||||
var compiledAnimations = this._animationCompiler.compileComponent(this.view.component, [ast]);
|
||||
|
||||
this.nestedViewCount++;
|
||||
var embeddedView = new CompileView(
|
||||
this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR,
|
||||
compiledAnimations.triggers, this.view.viewIndex + this.nestedViewCount, compileElement,
|
||||
this.view.animations, this.view.viewIndex + this.nestedViewCount, compileElement,
|
||||
templateVariableBindings);
|
||||
this.nestedViewCount += buildView(embeddedView, ast.children, this.targetDependencies);
|
||||
|
||||
@ -518,7 +512,7 @@ function createViewFactory(
|
||||
templateUrlInfo = view.component.template.templateUrl;
|
||||
}
|
||||
if (view.viewIndex === 0) {
|
||||
var animationsExpr = o.literalMap(view.animations.map(entry => [entry.name, entry.fnVariable]));
|
||||
var animationsExpr = o.literalMap(view.animations.map(entry => [entry.name, entry.fnExp]));
|
||||
initRenderCompTypeStmts = [new o.IfStmt(renderCompTypeVar.identical(o.NULL_EXPR), [
|
||||
renderCompTypeVar
|
||||
.set(ViewConstructorVars.viewUtils.callMethod(
|
||||
|
@ -8,7 +8,7 @@
|
||||
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
import {AnimationCompiler} from '../animation/animation_compiler';
|
||||
import {AnimationCompiler, AnimationEntryCompileResult} from '../animation/animation_compiler';
|
||||
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
|
||||
import {CompilerConfig} from '../config';
|
||||
import * as o from '../output/output_ast';
|
||||
@ -34,22 +34,18 @@ export class ViewCompiler {
|
||||
|
||||
compileComponent(
|
||||
component: CompileDirectiveMetadata, template: TemplateAst[], styles: o.Expression,
|
||||
pipes: CompilePipeMetadata[]): ViewCompileResult {
|
||||
var dependencies: Array<ViewFactoryDependency|ComponentFactoryDependency> = [];
|
||||
var compiledAnimations = this._animationCompiler.compileComponent(component, template);
|
||||
var statements: o.Statement[] = [];
|
||||
var animationTriggers = compiledAnimations.triggers;
|
||||
animationTriggers.forEach(entry => {
|
||||
statements.push(entry.statesMapStatement);
|
||||
statements.push(entry.fnStatement);
|
||||
});
|
||||
var view = new CompileView(
|
||||
component, this._genConfig, pipes, styles, animationTriggers, 0,
|
||||
pipes: CompilePipeMetadata[],
|
||||
compiledAnimations: AnimationEntryCompileResult[]): ViewCompileResult {
|
||||
const dependencies: Array<ViewFactoryDependency|ComponentFactoryDependency> = [];
|
||||
const view = new CompileView(
|
||||
component, this._genConfig, pipes, styles, compiledAnimations, 0,
|
||||
CompileElement.createNull(), []);
|
||||
|
||||
const statements: o.Statement[] = [];
|
||||
buildView(view, template, dependencies);
|
||||
// Need to separate binding from creation to be able to refer to
|
||||
// variables that have been declared after usage.
|
||||
bindView(view, template, compiledAnimations.outputs);
|
||||
bindView(view, template);
|
||||
finishView(view, statements);
|
||||
|
||||
return new ViewCompileResult(statements, view.viewFactory.name, dependencies);
|
||||
|
Reference in New Issue
Block a user