I need to animate an ngFor
list as it is populated and shown. Each element should have a transition, let's say something like this.
How can I do that?
BrowserAnimationsModulelinkExports BrowserModule with additional dependency-injection providers for use with animations.
Perform below steps to make it work: npm install @angular/animations@latest --save. import "BrowserAnimationsModule" from "@angular/platform-browser/animations" in your root NgModule (Without this, your code will compile and run, but animations will trigger an error)
It has a few issues because ngFor
does a few redundant add/removes which cause items to be animated which shouldn't:
import {Component} from 'angular2/core';
import { Component, Directive, OnDestroy, Input } from 'angular2/core';
@Component({
selector: 'my-app',
template: `<div (click)="$event.preventDefault()">
<button type="button" (click)="pushItem()">Push</button>
<button type="button" (click)="removeItemLast()">Remove Last</button><br/>
<button type="button" (click)="unshiftItem()">Unshift</button>
<button type="button" (click)="removeItemFirst()">Remove First</button><br/>
<ul>
<li class="my-animation" *ngFor="#item of items">
{{item.title}}
</li>
</ul>
</div>`
})
export class AppComponent {
private count:number = 1;
public items: Array<any>;
constructor() {
console.clear();
this.items = [];
this.items.push(this.newItem());
this.items.push(this.newItem());
}
pushItem() {
this.items.push(this.newItem());
},
removeItemLast() {
if(this.items.length > 0) this.items.splice(this.items.length - 1, 1);
},
unshiftItem() {
this.items.unshift(this.newItem());
},
removeItemFirst() {
if(this.items.length > 0) this.items.splice(0, 1);
},
newItem() {
return {title: 'Item' + this.count++};
}
}
@keyframes MyAnimation {
0% {
padding-left: 100px;
}
100% {
padding-left: 0px;
}
}
.my-animation {
animation: MyAnimation 1s;
}
Plunker example (RC.x) from https://github.com/angular/angular/issues/7239 demonstrates the issue.
Update
This was fixed a long time ago
working Demo on stackblitz
There is now the guide to Angular's animation system. This helps if we want to do fancy things, like only do the animation for elements added after the component has initialized, not the ones that are present already. I've modified the previous answer to do it the Angular 2 way.
Plunker: http://plnkr.co/edit/NAs05FiAVTlUjDOZfEsF?p=preview
import {
Component,
trigger, transition, animate, style, state
} from '@angular/core';
@Component({
selector : 'my-app',
animations: [
trigger('growShrinkStaticStart', [
state('in', style({ height: '*', 'padding-top': '*', 'padding-bottom': '*', 'margin-top': '*', 'margin-bottom': '*' })),
transition('* => void', [
style({ height: '*', 'padding-top': '*', 'padding-bottom': '*', 'margin-top': '*', 'margin-bottom': '*' }),
animate("0.5s ease", style({ height: '0', 'padding-top': '0', 'padding-bottom': '0', 'margin-top': '0', 'margin-bottom': '0' }))
]),
transition('void => false', [
/*no transition on first load*/
]),
transition('void => *', [
style({ height: '0', 'padding-top': '0', 'padding-bottom': '0', 'margin-top': '0', 'margin-bottom': '0' }),
animate("0.5s ease", style({ height: '*', 'padding-top': '*', 'padding-bottom': '*', 'margin-top': '*', 'margin-bottom': '*' }))
])
])
],
template : `<div (click)="$event.preventDefault()">
<button type="button" (click)="pushItem()">Push</button>
<button type="button" (click)="removeItemLast()">Remove Last</button><br/>
<button type="button" (click)="unshiftItem()">Unshift</button>
<button type="button" (click)="removeItemFirst()">Remove First</button><br/>
<ul style="background: light-blue">
<li *ngFor="let item of items"
[@growShrinkStaticStart]="animationInitialized.toString()"
(@growShrinkStaticStart.done)="animationInitialized = true"
style="background-color:pink; border:1px dashed gray; overflow:hidden">
<h3>{{item.title}}</h3><p>{{item.description}}</p>
</li>
</ul>
<div>Footer</div>
</div>`
})
export class AppComponent
{
private count: number = 1;
public items: Array <{ title: string, description: string }> ;
private animationInitialized: boolean = false;
constructor() {
this.items = [];
this.items.push(this.newItem());
this.items.push(this.newItem());
}
pushItem() {
this.items.push(this.newItem());
}
removeItemLast() {
if (this.items.length > 0)
this.items.splice(this.items.length - 1, 1);
}
unshiftItem() {
this.items.unshift(this.newItem());
}
removeItemFirst() {
if (this.items.length > 0)
this.items.splice(0, 1);
}
newItem() {
let d: string = "";
let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz0123456789 . ! ? ";
for (let i = 0; i < Math.floor(Math.random() * 50000); i++)
d += possible.charAt(Math.floor(Math.random() * possible.length));
return { title : 'Item' + this.count++, description: d };
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With