Reputation: 747
I have a question about angular loops. I would like to delay the rendering process on a ng-for. For example,
<div *ngFor="let data of items | delay">
<app-my-component [data]="data"></app-my-component>
</div>
Delay 10ms for each render. The "items" array comes from another component that I can't change it. Also, the "items" array size can be increased and decreased. Maybe, I can redirect it with observable struct. I have tried observable with async but I can't achieve. Can someone show me an example ?
Upvotes: 4
Views: 2351
Reputation: 11345
You can make use of async pipe to load an observable
//define your delay observable
this.delayItems=of(items).pipe(delay(1000))
in your template
<div *ngFor="let data of delayItems | async">
<app-my-component [data]="data"></app-my-component>
</div>
Update
import { Component } from '@angular/core';
import { from, timer, Observable } from 'rxjs';
@Component({
selector: 'my-app',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
arr = [1, 2, 3, 4, 5]
arr$:Observable<any[]>
constructor() {
this.arr$=from(this.arr).pipe(
concatMap(item=>timer(1000).pipe(mapTo(item))),
scan((acc,curr:number)=>acc.concat(curr),[])
)
}
}
html
<div *ngFor="let i of arr$ | async">
<b>{{ i }}</b>
</div>
Upvotes: 1
Reputation: 2947
Actually this is interesting question and couldn't found exact question on the SO.
This is my solution, I wrote helper methods for easier understanding and future code reuse:
My solution on Live StackBlitz editor Link
import { from, timer, Observable } from 'rxjs';
import {
scan,
map,
concatMap,
share,
} from 'rxjs/operators';
const timeOf = (interval: number) => <T>(val: T) =>
timer(interval).pipe(map(x => val));
const timed = (interval: number) => <T>(source: Observable<T>) =>
source.pipe(
concatMap(timeOf(1000)),
map(x => [x]),
scan((acc, val) => [...acc, ...val]),
)
And the usage is:
arr = [1, 2, 3, 4, 5]
arr$ = from(this.arr)
.pipe(
timed(1000),
)
As for HTML will be:
<div *ngFor="let i of arr$ | async">
<b>{{ i }}</b>
</div>
As for explanation:
from(this.arr)
will emit each of the array items separately and then I am using concatMap()
for each emitted item to the timer(1000)
, which is essentialy delaying each emitted item for 1 second. Finally, I am using scan()
operator to combine each emitted item back in the final accumulated array.
Upvotes: 2