feat(core): introduce support for animations

Closes #8734
This commit is contained in:
Matias Niemelä
2016-05-25 12:46:22 -07:00
parent 6c6b316bd9
commit 5e0f8cf3f0
83 changed files with 5294 additions and 756 deletions

View File

@ -27,6 +27,8 @@ import {CompilerConfig} from '../config';
import {CompileBinding} from './compile_binding';
import {Identifiers} from '../identifiers';
import {CompiledAnimation} from '../animation/animation_compiler';
export class CompileView implements NameResolver {
public viewType: ViewType;
public viewQueries: CompileTokenMap<CompileQuery[]>;
@ -48,6 +50,7 @@ export class CompileView implements NameResolver {
public afterContentLifecycleCallbacksMethod: CompileMethod;
public afterViewLifecycleCallbacksMethod: CompileMethod;
public destroyMethod: CompileMethod;
public detachMethod: CompileMethod;
public eventHandlerMethods: o.ClassMethod[] = [];
public fields: o.ClassField[] = [];
@ -66,13 +69,17 @@ 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,
public pipeMetas: CompilePipeMetadata[],
public styles: o.Expression,
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);
@ -84,6 +91,7 @@ export class CompileView implements NameResolver {
this.afterContentLifecycleCallbacksMethod = new CompileMethod(this);
this.afterViewLifecycleCallbacksMethod = new CompileMethod(this);
this.destroyMethod = new CompileMethod(this);
this.detachMethod = new CompileMethod(this);
this.viewType = getViewType(component, viewIndex);
this.className = `_View_${component.type.name}${viewIndex}`;

View File

@ -1,5 +1,5 @@
import {SecurityContext} from '../../core_private';
import {LifecycleHooks, isDefaultChangeDetectionStrategy} from '../../core_private';
import {EMPTY_STATE as EMPTY_ANIMATION_STATE, LifecycleHooks, isDefaultChangeDetectionStrategy} from '../../core_private';
import {isBlank, isPresent} from '../../src/facade/lang';
@ -24,6 +24,7 @@ import {camelCaseToDashCase} from '../util';
import {convertCdExpressionToIr} from './expression_converter';
import {CompileBinding} from './compile_binding';
import {BaseException} from '@angular/core';
function createBindFieldExpr(exprIndex: number): o.ReadPropExpr {
@ -95,36 +96,92 @@ function bindAndWriteToRenderer(boundProps: BoundElementPropertyAst[], context:
var fieldExpr = createBindFieldExpr(bindingIndex);
var currValExpr = createCurrValueExpr(bindingIndex);
var renderMethod: string;
var oldRenderValue: o.Expression = sanitizedValue(boundProp, fieldExpr);
var renderValue: o.Expression = sanitizedValue(boundProp, currValExpr);
var updateStmts = [];
switch (boundProp.type) {
case PropertyBindingType.Property:
renderMethod = 'setElementProperty';
if (view.genConfig.logBindingUpdate) {
updateStmts.push(logBindingUpdateStmt(renderNode, boundProp.name, currValExpr));
updateStmts.push(logBindingUpdateStmt(renderNode, boundProp.name, renderValue));
}
updateStmts.push(
o.THIS_EXPR.prop('renderer')
.callMethod('setElementProperty', [renderNode, o.literal(boundProp.name), renderValue])
.toStmt()
);
break;
case PropertyBindingType.Attribute:
renderMethod = 'setElementAttribute';
renderValue =
renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
renderValue = renderValue.isBlank().conditional(o.NULL_EXPR, renderValue.callMethod('toString', []));
updateStmts.push(
o.THIS_EXPR.prop('renderer')
.callMethod('setElementAttribute', [renderNode, o.literal(boundProp.name), renderValue])
.toStmt()
);
break;
case PropertyBindingType.Class:
renderMethod = 'setElementClass';
updateStmts.push(
o.THIS_EXPR.prop('renderer')
.callMethod('setElementClass', [renderNode, o.literal(boundProp.name), renderValue])
.toStmt()
);
break;
case PropertyBindingType.Style:
renderMethod = 'setElementStyle';
var strValue: o.Expression = renderValue.callMethod('toString', []);
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')
.callMethod('setElementStyle', [renderNode, o.literal(boundProp.name), renderValue])
.toStmt()
);
break;
case PropertyBindingType.Animation:
var animationName = boundProp.name;
var animation = view.componentView.animations.get(animationName);
if (!isPresent(animation)) {
throw new BaseException(`Internal Error: couldn't find an animation entry for ${boundProp.name}`);
}
// it's important to normalize the void value as `void` explicitly
// so that the styles data can be obtained from the stringmap
var emptyStateValue = o.literal(EMPTY_ANIMATION_STATE);
// void => ...
var oldRenderVar = o.variable('oldRenderVar');
updateStmts.push(oldRenderVar.set(oldRenderValue).toDeclStmt());
updateStmts.push(
new o.IfStmt(oldRenderVar.equals(o.importExpr(Identifiers.uninitialized)), [
oldRenderVar.set(emptyStateValue).toStmt()
]));
// ... => void
var newRenderVar = o.variable('newRenderVar');
updateStmts.push(newRenderVar.set(renderValue).toDeclStmt());
updateStmts.push(
new o.IfStmt(newRenderVar.equals(o.importExpr(Identifiers.uninitialized)), [
newRenderVar.set(emptyStateValue).toStmt()
]));
updateStmts.push(
animation.fnVariable.callFn([
o.THIS_EXPR,
renderNode,
oldRenderVar,
newRenderVar
]).toStmt());
view.detachMethod.addStmt(
animation.fnVariable.callFn([
o.THIS_EXPR,
renderNode,
oldRenderValue,
emptyStateValue
]).toStmt());
break;
}
updateStmts.push(
o.THIS_EXPR.prop('renderer')
.callMethod(renderMethod, [renderNode, o.literal(boundProp.name), renderValue])
.toStmt());
bind(view, currValExpr, fieldExpr, boundProp.value, context, updateStmts,
view.detectChangesRenderPropertiesMethod);

View File

@ -45,6 +45,8 @@ import {
CompileTokenMetadata
} from '../compile_metadata';
import {AnimationCompiler} from '../animation/animation_compiler';
const IMPLICIT_TEMPLATE_VAR = '\$implicit';
const CLASS_ATTR = 'class';
const STYLE_ATTR = 'style';
@ -79,6 +81,8 @@ 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: ViewCompileDependency[]) {}
private _isRootNode(parent: CompileElement): boolean { return parent.view !== this.view; }
@ -270,9 +274,11 @@ class ViewBuilderVisitor implements TemplateAstVisitor {
ast.providers, ast.hasViewContainer, true, ast.references);
this.view.nodes.push(compileElement);
var compiledAnimations = this._animationCompiler.compileComponent(this.view.component);
this.nestedViewCount++;
var embeddedView = new CompileView(
this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR,
this.view.component, this.view.genConfig, this.view.pipeMetas, o.NULL_EXPR, compiledAnimations,
this.view.viewIndex + this.nestedViewCount, compileElement, templateVariableBindings);
this.nestedViewCount += buildView(embeddedView, ast.children, this.targetDependencies);
@ -423,7 +429,8 @@ function createViewClass(view: CompileView, renderCompTypeVar: o.ReadVarExpr,
[new o.FnParam(DetectChangesVars.throwOnChange.name, o.BOOL_TYPE)],
generateDetectChangesMethod(view)),
new o.ClassMethod('dirtyParentQueriesInternal', [], view.dirtyParentQueriesMethod.finish()),
new o.ClassMethod('destroyInternal', [], view.destroyMethod.finish())
new o.ClassMethod('destroyInternal', [], view.destroyMethod.finish()),
new o.ClassMethod('detachInternal', [], view.detachMethod.finish())
].concat(view.eventHandlerMethods);
var superClass = view.genConfig.genDebugInfo ? Identifiers.DebugAppView : Identifiers.AppView;
var viewClass = new o.ClassStmt(view.className, o.importExpr(superClass, [getContextType(view)]),

View File

@ -8,6 +8,8 @@ import {CompileDirectiveMetadata, CompilePipeMetadata} from '../compile_metadata
import {TemplateAst} from '../template_ast';
import {CompilerConfig} from '../config';
import {AnimationCompiler} from '../animation/animation_compiler';
export class ViewCompileResult {
constructor(public statements: o.Statement[], public viewFactoryVar: string,
public dependencies: ViewCompileDependency[]) {}
@ -15,13 +17,19 @@ export class ViewCompileResult {
@Injectable()
export class ViewCompiler {
private _animationCompiler = new AnimationCompiler();
constructor(private _genConfig: CompilerConfig) {}
compileComponent(component: CompileDirectiveMetadata, template: TemplateAst[],
styles: o.Expression, pipes: CompilePipeMetadata[]): ViewCompileResult {
var statements = [];
var dependencies = [];
var view = new CompileView(component, this._genConfig, pipes, styles, 0,
var compiledAnimations = this._animationCompiler.compileComponent(component);
var statements = [];
compiledAnimations.map(entry => {
statements.push(entry.statesMapStatement);
statements.push(entry.fnStatement);
});
var view = new CompileView(component, this._genConfig, pipes, styles, compiledAnimations, 0,
CompileElement.createNull(), []);
buildView(view, template, dependencies);
// Need to separate binding from creation to be able to refer to