diff --git a/packages/core/test/animation/animation_integration_spec.ts b/packages/core/test/animation/animation_integration_spec.ts
index 551f65315e..fc56c91531 100644
--- a/packages/core/test/animation/animation_integration_spec.ts
+++ b/packages/core/test/animation/animation_integration_spec.ts
@@ -328,6 +328,36 @@ const DEFAULT_COMPONENT_ID = '1';
]);
});
+ // https://github.com/angular/angular/issues/32794
+ it('should support nested animation triggers', () => {
+ const REUSABLE_ANIMATION = [trigger(
+ 'myAnimation',
+ [transition(
+ 'void => *', [style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])];
+
+ @Component({
+ selector: 'if-cmp',
+ template: `
+
+ `,
+ animations: [REUSABLE_ANIMATION],
+ })
+ class Cmp {
+ }
+
+ TestBed.configureTestingModule({declarations: [Cmp]});
+
+ const engine = TestBed.inject(ɵAnimationEngine);
+ const fixture = TestBed.createComponent(Cmp);
+ fixture.detectChanges();
+ engine.flush();
+
+ expect(getLog().length).toEqual(1);
+ expect(getLog().pop() !.keyframes).toEqual([
+ {offset: 0, opacity: '0'}, {offset: 1, opacity: '1'}
+ ]);
+ });
+
it('should allow a transition to use a function to determine what method to run', () => {
let valueToMatch = '';
let capturedElement: any;
diff --git a/packages/platform-browser/animations/src/animation_renderer.ts b/packages/platform-browser/animations/src/animation_renderer.ts
index 59b4f48268..02ae8cf58d 100644
--- a/packages/platform-browser/animations/src/animation_renderer.ts
+++ b/packages/platform-browser/animations/src/animation_renderer.ts
@@ -12,6 +12,12 @@ import {Injectable, NgZone, Renderer2, RendererFactory2, RendererStyleFlags2, Re
const ANIMATION_PREFIX = '@';
const DISABLE_ANIMATIONS_FLAG = '@.disabled';
+// Define a recursive type to allow for nested arrays of `AnimationTriggerMetadata`. Note that an
+// interface declaration is used as TypeScript prior to 3.7 does not support recursive type
+// references, see https://github.com/microsoft/TypeScript/pull/33050 for details.
+type NestedAnimationTriggerMetadata = AnimationTriggerMetadata | RecursiveAnimationTriggerMetadata;
+interface RecursiveAnimationTriggerMetadata extends Array {}
+
@Injectable()
export class AnimationRendererFactory implements RendererFactory2 {
private _currentId: number = 0;
@@ -55,10 +61,17 @@ export class AnimationRendererFactory implements RendererFactory2 {
this._currentId++;
this.engine.register(namespaceId, hostElement);
- const animationTriggers = type.data['animation'] as AnimationTriggerMetadata[];
- animationTriggers.forEach(
- trigger => this.engine.registerTrigger(
- componentId, namespaceId, hostElement, trigger.name, trigger));
+
+ const registerTrigger = (trigger: NestedAnimationTriggerMetadata) => {
+ if (Array.isArray(trigger)) {
+ trigger.forEach(registerTrigger);
+ } else {
+ this.engine.registerTrigger(componentId, namespaceId, hostElement, trigger.name, trigger);
+ }
+ };
+ const animationTriggers = type.data['animation'] as NestedAnimationTriggerMetadata[];
+ animationTriggers.forEach(registerTrigger);
+
return new AnimationRenderer(this, namespaceId, delegate, this.engine);
}
diff --git a/packages/platform-browser/animations/test/animation_renderer_spec.ts b/packages/platform-browser/animations/test/animation_renderer_spec.ts
index 7455b4e00f..8d3e87c82b 100644
--- a/packages/platform-browser/animations/test/animation_renderer_spec.ts
+++ b/packages/platform-browser/animations/test/animation_renderer_spec.ts
@@ -79,6 +79,16 @@ import {el} from '../../testing/src/browser_util';
expect(engine.captures['setProperty'].pop()).toEqual([element, 'prop', 'value']);
});
+ // https://github.com/angular/angular/issues/32794
+ it('should support nested animation triggers', () => {
+ makeRenderer([[trigger('myAnimation', [])]]);
+
+ const {triggers} = TestBed.inject(AnimationEngine) as MockAnimationEngine;
+
+ expect(triggers.length).toEqual(1);
+ expect(triggers[0].name).toEqual('myAnimation');
+ });
+
describe('listen', () => {
it('should hook into the engine\'s listen call if the property begins with `@`', () => {
const renderer = makeRenderer();
@@ -320,8 +330,10 @@ class MockAnimationEngine extends InjectableAnimationEngine {
data.push(args);
}
- registerTrigger(componentId: string, namespaceId: string, trigger: AnimationTriggerMetadata) {
- this.triggers.push(trigger);
+ registerTrigger(
+ componentId: string, namespaceId: string, hostElement: any, name: string,
+ metadata: AnimationTriggerMetadata): void {
+ this.triggers.push(metadata);
}
onInsert(namespaceId: string, element: any): void { this._capture('onInsert', [element]); }