refactor(animations): ensure animation input/outputs are managed within the template parser (#11782)

Closes #11782
Closes #11601
Related #11707
This commit is contained in:
Matias Niemelä
2016-09-23 16:37:04 -04:00
committed by Rado Kirov
parent 45ad13560b
commit f1b6c6efa1
23 changed files with 325 additions and 399 deletions

View File

@ -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);

View File

@ -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 {

View File

@ -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[],

View File

@ -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;
}

View File

@ -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(

View File

@ -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);