Reputation: 1573
I'm trying to animate some route changes in Angular. The animation should height animate between nothing and the height of the content. This works very well when the content is known ahead of time, see this plunkr: http://plnkr.co/edit/FOZSZOJaSpLbrs2vl6Ka
It does however not work very well, the moment an http request has to happen. See this plunkr: http://plnkr.co/edit/vyobBtb5KDO7nkYLLsto (Here the http request has been simulated by using a delayed rx operation)
I would be perfectly fine with the animation not starting until content has been loaded, but i doubt that is possible.
What i assume is going wrong, is that Angular is measuring the height of the "twoComponent" right when it's inserted, but before it has finished loading, which then causes this.
Overall not correctly working code:
//our root app component
import {Component, NgModule, VERSION} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {RouterModule, Routes} from '@angular/router';
import {HashLocationStrategy} from '@angular/common';
import {animate, animateChild, query, style, transition, trigger} from '@angular/animations';
import {Observable} from 'rxjs/Observable';
import {of} from 'rxjs/observable/of';
import 'rxjs/add/operator/delay';
@Component({
selector: 'my-app',
template: `
<div>
<h2>Hello {{name}}</h2>
<a routerLink="">Go to one</a>
<a routerLink="two">Go to two</a>
<div [@routeAnimations]="prepareRoute(outlet)"
style="background-color:pink"
id="main-router-outlet">
<router-outlet #outlet="outlet"></router-outlet>
</div>
</div>
`,
animations: [
trigger('routeAnimations', [
transition('* => *', [
query(':enter', [
style({height: 0, opacity: 0, width: 0}),
], {optional: true}),
query(':leave', [
style({height: '*', width: '*'}),
animate('200ms', style({opacity: 0})),
animate('500ms', style({height: 0, width: 0})),
], {optional: true}),
query(':enter', [
animate('500ms', style({height: '*', width: '*'})),
animate('500ms', style({opacity: 1})),
animateChild(),
], {optional: true}),
]),
]),
],
styles: [
`#main-router-outlet ::ng-deep > * {
display: block;
} `
]
})
export class App {
name:string;
constructor() {
this.name = `Angular! v${VERSION.full}`
}
public prepareRoute(outlet: RouterOutlet) {
return outlet.activatedRouteData['animation'] || '';
}
}
@Component({
template: `one component<br>more`
})
export class oneComponent {
}
@Component({
template: `two component
<div *ngFor="let s of dynamicContent|async">
{{s}}
</div>
`
})
export class twoComponent {
public dynamicContent: Observable<string[]>;
ngOnInit() {
this.dynamicContent = of(['foo', 'bar', 'baz'])
.delay(200);
}
}
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
RouterModule.forRoot([{
path: '',
component: oneComponent,
data: {
animation: 'one',
}
},
{
path: 'two',
component: twoComponent,
data: {
animation: 'two',
}
}], { useClass: HashLocationStrategy })
],
declarations: [ App, oneComponent, twoComponent ],
bootstrap: [ App ]
})
export class AppModule {}
Upvotes: 2
Views: 4622
Reputation: 52867
Use a Resolver: Demo
Resolver
import { Injectable } from '@angular/core';
import { Resolve, ActivatedRouteSnapshot,ActivatedRoute } from '@angular/router';
import { ContactsService } from './contacts.service';
@Injectable()
export class ArrResolve implements Resolve<string[]> {
constructor() {}
resolve(route: ActivatedRouteSnapshot) {
return of(['foo', 'bar', 'baz'])
.delay(200);
}
}
AppModule
@NgModule({
imports: [
BrowserModule,
BrowserAnimationsModule,
RouterModule.forRoot([{
path: '',
component: oneComponent,
data: {
animation: 'one',
}
},
{
path: 'two',
component: twoComponent,
data: {
animation: 'two',
},
resolve: {
arr: ArrResolve
}
}], { useClass: HashLocationStrategy })
],
declarations: [ App, oneComponent, twoComponent ],
bootstrap: [ App ],
providers: [ArrResolve]
})
export class AppModule {}
Two Component
@Component({
template: `two component
<div *ngFor="let s of dynamicContent|async">
{{s}}
</div>
`
})
export class twoComponent {
public dynamicContent: Observable<string[]>;
constructor(private route:ActivatedRoute) {
}
ngOnInit() {
this.dynamicContent = of(this.route.snapshot.data.arr);
}
}
Upvotes: 4