Skip to content

Commit

Permalink
fix(animations): generate aot code for animation trigger output events
Browse files Browse the repository at this point in the history
  • Loading branch information
matsko committed Oct 17, 2016
1 parent 7e76f27 commit 374d991
Show file tree
Hide file tree
Showing 12 changed files with 118 additions and 95 deletions.
20 changes: 11 additions & 9 deletions modules/@angular/compiler/src/animation/animation_compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -270,23 +270,25 @@ class _AnimationBuilder implements AnimationAstVisitor {
.toStmt()])])
.toStmt());

var transitionParams = o.literalMap([
['toState', _ANIMATION_NEXT_STATE_VAR], ['fromState', _ANIMATION_CURRENT_STATE_VAR],
['totalTime', _ANIMATION_TIME_VAR]
]);

var transitionEvent = o.importExpr(resolveIdentifier(Identifiers.AnimationTransitionEvent))
.instantiate([transitionParams]);

statements.push(_ANIMATION_FACTORY_VIEW_CONTEXT
.callMethod(
'queueAnimation',
[
_ANIMATION_FACTORY_ELEMENT_VAR, o.literal(this.animationName),
_ANIMATION_PLAYER_VAR, transitionEvent
_ANIMATION_PLAYER_VAR
])
.toStmt());


var transitionParams = o.literalMap([
['toState', _ANIMATION_NEXT_STATE_VAR], ['fromState', _ANIMATION_CURRENT_STATE_VAR],
['totalTime', _ANIMATION_TIME_VAR]
]);

statements.push(
new o.ReturnStatement(o.importExpr(resolveIdentifier(Identifiers.AnimationTransition))
.instantiate([_ANIMATION_PLAYER_VAR, transitionParams])));

return o.fn(
[
new o.FnParam(
Expand Down
12 changes: 6 additions & 6 deletions modules/@angular/compiler/src/identifiers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/

import {ANALYZE_FOR_ENTRY_COMPONENTS, AnimationTransitionEvent, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, LOCALE_ID as LOCALE_ID_, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT as TRANSLATIONS_FORMAT_, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';
import {ANALYZE_FOR_ENTRY_COMPONENTS, ChangeDetectionStrategy, ChangeDetectorRef, ComponentFactory, ComponentFactoryResolver, ElementRef, Injector, LOCALE_ID as LOCALE_ID_, NgModuleFactory, QueryList, RenderComponentType, Renderer, SecurityContext, SimpleChange, TRANSLATIONS_FORMAT as TRANSLATIONS_FORMAT_, TemplateRef, ViewContainerRef, ViewEncapsulation} from '@angular/core';

import {CompileIdentifierMetadata, CompileTokenMetadata} from './compile_metadata';
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes, castByValue, checkBinding, clearStyles, collectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, reflector, registerModuleFactory, renderStyles} from './private_import_core';
import {AnimationGroupPlayer, AnimationKeyframe, AnimationSequencePlayer, AnimationStyles, AnimationTransition, AppElement, AppView, ChangeDetectorStatus, CodegenComponentFactoryResolver, DebugAppView, DebugContext, EMPTY_ARRAY, EMPTY_MAP, NgModuleInjector, NoOpAnimationPlayer, StaticNodeDebugInfo, TemplateRef_, UNINITIALIZED, ValueUnwrapper, ViewType, ViewUtils, balanceAnimationKeyframes, castByValue, checkBinding, clearStyles, collectAndResolveStyles, devModeEqual, flattenNestedViewRenderNodes, interpolate, prepareFinalAnimationStyles, pureProxy1, pureProxy10, pureProxy2, pureProxy3, pureProxy4, pureProxy5, pureProxy6, pureProxy7, pureProxy8, pureProxy9, reflector, registerModuleFactory, renderStyles} from './private_import_core';
import {assetUrl} from './util';

var APP_VIEW_MODULE_URL = assetUrl('core', 'linker/view');
Expand Down Expand Up @@ -266,10 +266,10 @@ export class Identifiers {
moduleUrl: assetUrl('core', 'i18n/tokens'),
runtime: TRANSLATIONS_FORMAT_
};
static AnimationTransitionEvent: IdentifierSpec = {
name: 'AnimationTransitionEvent',
moduleUrl: assetUrl('core', 'animation/animation_transition_event'),
runtime: AnimationTransitionEvent
static AnimationTransition: IdentifierSpec = {
name: 'AnimationTransition',
moduleUrl: assetUrl('core', 'animation/animation_transition'),
runtime: AnimationTransition
};
}

Expand Down
1 change: 1 addition & 0 deletions modules/@angular/compiler/src/private_import_core.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,3 +90,4 @@ export const ViewMetadata: typeof r.ViewMetadata = r.ViewMetadata;
export type ComponentStillLoadingError = typeof r._ComponentStillLoadingError;
export const ComponentStillLoadingError: typeof r.ComponentStillLoadingError =
r.ComponentStillLoadingError;
export const AnimationTransition: typeof r.AnimationTransition = r.AnimationTransition;
25 changes: 5 additions & 20 deletions modules/@angular/compiler/src/view_compiler/event_binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {CompileDirectiveMetadata} from '../compile_metadata';
import {isPresent} from '../facade/lang';
import {identifierToken} from '../identifiers';
import * as o from '../output/output_ast';
import {ViewType} from '../private_import_core';
import {BoundEventAst, DirectiveAst} from '../template_parser/template_ast';

import {CompileBinding} from './compile_binding';
Expand Down Expand Up @@ -40,6 +41,7 @@ export class CompileEventListener {
}

get methodName() { return this._methodName; }
get isAnimation() { return !!this.eventPhase; }

constructor(
public compileElement: CompileElement, public eventTarget: string, public eventName: string,
Expand Down Expand Up @@ -113,23 +115,6 @@ export class CompileEventListener {
disposable.set(listenExpr).toDeclStmt(o.FUNCTION_TYPE, [o.StmtModifier.Private]));
}

listenToAnimation() {
var outputListener = o.THIS_EXPR.callMethod(
'eventHandler',
[o.THIS_EXPR.prop(this._methodName).callMethod(o.BuiltinMethod.Bind, [o.THIS_EXPR])]);

// tie the property callback method to the view animations map
var stmt = o.THIS_EXPR.prop('animationContext')
.callMethod(
'registerOutputHandler',
[
this.compileElement.renderNode, o.literal(this.eventName),
o.literal(this.eventPhase), outputListener
])
.toStmt();
this.compileElement.view.createMethod.addStmt(stmt);
}

listenToDirective(directiveInstance: o.Expression, observablePropName: string) {
var subscription = o.variable(`subscription_${this.compileElement.view.subscriptions.length}`);
this.compileElement.view.subscriptions.push(subscription);
Expand Down Expand Up @@ -185,9 +170,9 @@ export function bindDirectiveOutputs(

export function bindRenderOutputs(eventListeners: CompileEventListener[]) {
eventListeners.forEach(listener => {
if (listener.eventPhase) {
listener.listenToAnimation();
} else {
// the animation listeners are handled within property_binder.ts to
// allow then to be placed next to the animation factory statements
if (!listener.isAnimation) {
listener.listenToRenderer();
}
});
Expand Down
47 changes: 37 additions & 10 deletions modules/@angular/compiler/src/view_compiler/property_binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {CompileElement, CompileNode} from './compile_element';
import {CompileMethod} from './compile_method';
import {CompileView} from './compile_view';
import {DetectChangesVars, ViewProperties} from './constants';
import {CompileEventListener} from './event_binder';
import {convertCdExpressionToIr, temporaryDeclaration} from './expression_converter';

function createBindFieldExpr(exprIndex: number): o.ReadPropExpr {
Expand Down Expand Up @@ -90,7 +91,7 @@ export function bindRenderText(

function bindAndWriteToRenderer(
boundProps: BoundElementPropertyAst[], context: o.Expression, compileElement: CompileElement,
isHostProp: boolean) {
isHostProp: boolean, eventListeners: CompileEventListener[]) {
var view = compileElement.view;
var renderNode = compileElement.renderNode;
boundProps.forEach((boundProp) => {
Expand Down Expand Up @@ -148,8 +149,11 @@ function bindAndWriteToRenderer(
targetViewExpr = compileElement.appElement.prop('componentView');
}

var detachStmts: o.Statement[] = [];
compileMethod = view.animationBindingsMethod;

const _ANIMATION_TRANSITION_VAR = o.variable('animationTransition_' + animationName);

var animationFnExpr =
targetViewExpr.prop('componentType').prop('animations').key(o.literal(animationName));

Expand All @@ -172,12 +176,32 @@ function bindAndWriteToRenderer(
[newRenderVar.set(emptyStateValue).toStmt()]));

updateStmts.push(
animationFnExpr.callFn([o.THIS_EXPR, renderNode, oldRenderVar, newRenderVar]).toStmt());
_ANIMATION_TRANSITION_VAR
.set(animationFnExpr.callFn([o.THIS_EXPR, renderNode, oldRenderVar, newRenderVar]))
.toDeclStmt());

view.detachMethod.addStmt(
animationFnExpr.callFn([o.THIS_EXPR, renderNode, oldRenderValue, emptyStateValue])
.toStmt());
detachStmts.push(_ANIMATION_TRANSITION_VAR
.set(animationFnExpr.callFn(
[o.THIS_EXPR, renderNode, oldRenderValue, emptyStateValue]))
.toDeclStmt());

eventListeners.forEach(listener => {
if (listener.isAnimation && listener.eventName === animationName) {
let callbackMethod = listener.eventPhase == 'start' ? 'onStart' : 'onDone';
let transitionEvent = o.variable('e');
let outputStmt =
_ANIMATION_TRANSITION_VAR
.callMethod(
callbackMethod,
[o.THIS_EXPR.prop(listener.methodName).callMethod('bind', [o.THIS_EXPR])])
.toStmt();

detachStmts.push(outputStmt);
updateStmts.push(outputStmt);
}
});

view.detachMethod.addStmts(detachStmts);
break;
}

Expand Down Expand Up @@ -218,14 +242,17 @@ function sanitizedValue(
}

export function bindRenderInputs(
boundProps: BoundElementPropertyAst[], compileElement: CompileElement): void {
bindAndWriteToRenderer(boundProps, compileElement.view.componentContext, compileElement, false);
boundProps: BoundElementPropertyAst[], compileElement: CompileElement,
eventListeners: CompileEventListener[]): void {
bindAndWriteToRenderer(
boundProps, compileElement.view.componentContext, compileElement, false, eventListeners);
}

export function bindDirectiveHostProps(
directiveAst: DirectiveAst, directiveInstance: o.Expression,
compileElement: CompileElement): void {
bindAndWriteToRenderer(directiveAst.hostProperties, directiveInstance, compileElement, true);
directiveAst: DirectiveAst, directiveInstance: o.Expression, compileElement: CompileElement,
eventListeners: CompileEventListener[]): void {
bindAndWriteToRenderer(
directiveAst.hostProperties, directiveInstance, compileElement, true, eventListeners);
}

export function bindDirectiveInputs(
Expand Down
4 changes: 2 additions & 2 deletions modules/@angular/compiler/src/view_compiler/view_binder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,14 @@ class ViewBinderVisitor implements TemplateAstVisitor {
collectEventListeners(ast.outputs, ast.directives, compileElement).forEach(entry => {
eventListeners.push(entry);
});
bindRenderInputs(ast.inputs, compileElement);
bindRenderInputs(ast.inputs, compileElement, eventListeners);
bindRenderOutputs(eventListeners);
ast.directives.forEach((directiveAst) => {
var directiveInstance = compileElement.instances.get(directiveAst.directive.type.reference);
bindDirectiveInputs(directiveAst, directiveInstance, compileElement);
bindDirectiveDetectChangesLifecycleCallbacks(directiveAst, directiveInstance, compileElement);

bindDirectiveHostProps(directiveAst, directiveInstance, compileElement);
bindDirectiveHostProps(directiveAst, directiveInstance, compileElement, eventListeners);
bindDirectiveOutputs(directiveAst, directiveInstance, eventListeners);
});
templateVisitAll(this, ast.children, compileElement);
Expand Down
3 changes: 1 addition & 2 deletions modules/@angular/compiler/src/view_compiler/view_compiler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

import {Injectable} from '@angular/core';

import {AnimationCompiler, AnimationEntryCompileResult} from '../animation/animation_compiler';
import {AnimationEntryCompileResult} from '../animation/animation_compiler';
import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata';
import {CompilerConfig} from '../config';
import * as o from '../output/output_ast';
Expand All @@ -29,7 +29,6 @@ export class ViewCompileResult {

@Injectable()
export class ViewCompiler {
private _animationCompiler = new AnimationCompiler();
constructor(private _genConfig: CompilerConfig) {}

compileComponent(
Expand Down
42 changes: 42 additions & 0 deletions modules/@angular/core/src/animation/animation_transition.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {AnimationPlayer} from './animation_player';
import {AnimationTransitionEvent} from './animation_transition_event';

export class AnimationTransition {
private _fromState: string;
private _toState: string;
private _totalTime: number;

constructor(
public player: AnimationPlayer,
{fromState, toState, totalTime}: {fromState: string, toState: string, totalTime: number}) {
this._fromState = fromState;
this._toState = toState;
this._totalTime = totalTime;
}

_createEvent(phaseName: string): AnimationTransitionEvent {
return new AnimationTransitionEvent({
fromState: this._fromState,
toState: this._toState,
totalTime: this._totalTime,
phaseName: phaseName
});
}

onStart(callback: (event: AnimationTransitionEvent) => any): void {
const event = new AnimationTransitionEvent(this._createEvent('start'));
this.player.onStart(() => callback(event));
}

onDone(callback: (event: AnimationTransitionEvent) => any): void {
const event = new AnimationTransitionEvent(this._createEvent('done'));
this.player.onDone(() => callback(event));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,13 @@ export class AnimationTransitionEvent {
public fromState: string;
public toState: string;
public totalTime: number;
public phaseName: string;

constructor({fromState, toState,
totalTime}: {fromState: string, toState: string, totalTime: number}) {
constructor({fromState, toState, totalTime, phaseName}:
{fromState: string, toState: string, totalTime: number, phaseName: string}) {
this.fromState = fromState;
this.toState = toState;
this.totalTime = totalTime;
this.phaseName = phaseName;
}
}
7 changes: 5 additions & 2 deletions modules/@angular/core/src/core_private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {AnimationPlayer as AnimationPlayer_, NoOpAnimationPlayer as NoOpAnimatio
import {AnimationSequencePlayer as AnimationSequencePlayer_} from './animation/animation_sequence_player';
import * as animationUtils from './animation/animation_style_util';
import {AnimationStyles as AnimationStyles_} from './animation/animation_styles';
import {AnimationTransition} from './animation/animation_transition';
import * as change_detection_util from './change_detection/change_detection_util';
import * as constants from './change_detection/constants';
import * as console from './console';
Expand Down Expand Up @@ -118,7 +119,8 @@ export var __core_private__: {
FILL_STYLE_FLAG: typeof FILL_STYLE_FLAG_,
_ComponentStillLoadingError?: ComponentStillLoadingError,
ComponentStillLoadingError: typeof ComponentStillLoadingError,
isPromise: typeof isPromise
isPromise: typeof isPromise,
AnimationTransition: typeof AnimationTransition
} = {
isDefaultChangeDetectionStrategy: constants.isDefaultChangeDetectionStrategy,
ChangeDetectorStatus: constants.ChangeDetectorStatus,
Expand Down Expand Up @@ -182,5 +184,6 @@ export var __core_private__: {
EMPTY_STATE: EMPTY_STATE_,
FILL_STYLE_FLAG: FILL_STYLE_FLAG_,
ComponentStillLoadingError: ComponentStillLoadingError,
isPromise: isPromise
isPromise: isPromise,
AnimationTransition: AnimationTransition
};
42 changes: 1 addition & 41 deletions modules/@angular/core/src/linker/animation_view_context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import {ViewAnimationMap} from '../animation/view_animation_map';

export class AnimationViewContext {
private _players = new ViewAnimationMap();
private _listeners = new Map<any, _AnimationOutputHandler[]>();

onAllActiveAnimationsDone(callback: () => any): void {
var activeAnimationPlayers = this._players.getAllPlayers();
Expand All @@ -26,19 +25,9 @@ export class AnimationViewContext {
}
}

queueAnimation(
element: any, animationName: string, player: AnimationPlayer,
event: AnimationTransitionEvent): void {
queueAnimation(element: any, animationName: string, player: AnimationPlayer): void {
queueAnimationGlobally(player);

this._players.set(element, animationName, player);

player.onStart(() => this._triggerOutputHandler(element, animationName, 'start', event));
player.onDone(() => {
// TODO: add codegen to remove the need to store these values
this._triggerOutputHandler(element, animationName, 'done', event);
this._players.remove(element, animationName);
});
}

cancelActiveAnimation(element: any, animationName: string, removeAllAnimations: boolean = false):
Expand All @@ -57,33 +46,4 @@ export class AnimationViewContext {
}
return capturedStyles;
}

registerOutputHandler(
element: any, eventName: string, eventPhase: string, eventHandler: Function): void {
var animations = this._listeners.get(element);
if (!animations) {
this._listeners.set(element, animations = []);
}
animations.push(new _AnimationOutputHandler(eventName, eventPhase, eventHandler));
}

private _triggerOutputHandler(
element: any, animationName: string, phase: string, event: AnimationTransitionEvent): void {
const listeners = this._listeners.get(element);
if (listeners && listeners.length) {
for (let i = 0; i < listeners.length; i++) {
let listener = listeners[i];
// we check for both the name in addition to the phase in the event
// that there may be more than one @trigger on the same element
if (listener.eventName === animationName && listener.eventPhase === phase) {
listener.handler(event);
break;
}
}
}
}
}

class _AnimationOutputHandler {
constructor(public eventName: string, public eventPhase: string, public handler: Function) {}
}
Loading

0 comments on commit 374d991

Please sign in to comment.