17

I have a child component with animations

child.component.ts

@Component({
  selector: 'child',
  animations:[
    // animations
   ]
})
export class ChildComponent { ... }

And a parent component, which has 2 of the child components in the html

parent.component.hmtl.ts

...
<child></child>
<child></child>
...

Stackblitz Example

What I want to achieve is to stagger the animations of the child components in the parent component. Therefore second child component should start animation after X seconds.

animateChild() sounds like it might work but I cant figure out how I can use it. Is that the right solution? If so an example would be really helpful.

Thanks in advance

EDIT: animateChild() does not seem to work for this case. Appearantly it only works on animations that are defined in the parent component.

EDIT2: I think there might be a workaround by adding a delay to the animations inside of the child components.

child.component.ts

@Component({
  selector: 'child',
  animations:[
    animate(x),
    // animations
   ]
})
export class ChildComponent { ... }

The x would be a variable that would increase for every child component. This workaround looks kinda messy to me

EDIT3: the anwers so far are more or less the solution i mentioned in my second edit. While those do work I still consider those as workarounds.

I am looking for a solution that only involves the parent component therefore the child component should stay the way it is like in this non working example

Flyii
  • 1,105
  • 1
  • 12
  • 21

3 Answers3

12

As per angular documentation in animation function.

The second argument, delay, has the same syntax as duration. For example:

Wait for 100ms and then run for 200ms: '0.2s 100ms'

Full reading here

So, our goal is to pass the second parameter something like this

animate('2000ms {{delay}}ms'

first, lets add input parameter to your child component so we can accept the input values from the parent ones:

export class ChildComponent implements OnInit {
  @Input() delay: number = 0;
  constructor() { }    
}

Now, lets pass down the parameter value form the parent component

<p>child1</p>
<app-child [delay]="0"></app-child>

<p>child2</p>
<app-child [delay]="1000"></app-child>
<p>child2 should have a delay</p>

In your child component we need to pass this parameter to the animation trigger so it would look like this

<p [@childAnimation]="{value:'',params:{delay:delay}}">
    IIIIIIIIIIIIIIIIII
</p>

And finally we can change our animation to support this parameter value

animations: [
    trigger('childAnimation', [
      transition(':enter', [
        animate('2000ms {{delay}}ms', style({ transform: 'translateX(80%)' })),
        animate('2000ms', style({ transform: 'translateX(0)' })),
      ], { params: { delay: 0 } })
    ])
  ]

all good now, you should working delay to the inputs now.

Check out demo here

Just code
  • 13,553
  • 10
  • 51
  • 93
  • 1
    this answer does not really fullfill my expectation(see edit3) but it works and looks like it is the most clean one – Flyii Dec 12 '18 at 07:28
  • @Flyii as per your edit3 the above solution works with parent one too – Just code Dec 12 '18 at 08:01
  • im not sure what you mean. Your solution introduces an input to the child component and modifying its animation with it. i was hoping for a solution which would look like this non working example: https://stackblitz.com/edit/angular-q75u1p?file=src%2Fapp%2Fapp.component.ts – Flyii Dec 12 '18 at 08:22
  • @Flyii that is not possible as far as I know, the problem is you do not know how many directive has been reused. – Just code Dec 12 '18 at 08:24
3

These aren't the only options, but they are ones I know. Angular 7 compatible.

Working Stackblitz Demo

Option 1: Host Binding & States

Within your child component, declare your animation state as a host-binding.

@HostBinding('@animationState') animstate : string = 'preanimationstate';

Then use a normal input within your child component to get the delay:

@Input('delay') delay : number;

Pass in the delay like so:

<child-component [delay]="500"></child-component>

So now in the OnInit you can just use a timeout:

let self = this;
window.setTimeout(function () {
    self.animstate = 'newstate';
}, self.delay);

Reasoning:

It seems like animate child could also be your saving grace, but this way is pretty straight forward and simple as well. Hope it helps.

Option 2: Move animation to HostComponent and manually apply state

You could also move your animation definition (if it is using states and transitions), from child.component.ts to parent.component.ts, then start all of your child components off with an initial state modifier like this:

<child [@animstate]="initial"></child>>

Then get your elements via ViewChildren:

@ViewChildren(ChildComponent) childComponents as QueryList<ChildComponent>;

This is accessible with AfterViewInit. Apply your states within the after view init using a for/each loop and a window.setTimeout Function.

Community
  • 1
  • 1
Terrance00
  • 1,658
  • 1
  • 20
  • 29
1

Just in case anyone stumbles on this issue again: If you want both animations to play at the same time you simply need to use group and animateChild. Basically, in the plunker linked in the first post you have to replace outerTransition with the following:

const outerTransition = transition('void => *', [
  style({opacity: 0}),
  group([
    animate(2000, style({opacity: 1})),
    query('@inner', [
      animateChild()
    ]),
  ]),
]);
Ismoil Shifoev
  • 5,512
  • 3
  • 24
  • 32