Reputation: 182
I would like to get data from the Cloud Firestore collection (limited to 10), and then after clicking the "load more" button, I would like to get another piece of collection and concat with the previous result.
My code is (base on https://firebase.google.com/docs/firestore/query-data/query-cursors):
import { Component, OnDestroy, OnInit } from '@angular/core';
import { User } from '../../../shared/models/user';
import { UsersService } from '../../../shared/services/users.service';
import { Subscription } from 'rxjs';
@Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.scss']
})
export class UsersComponent implements OnInit, OnDestroy {
users: User[];
lastInResponse: any;
subscription:Subscription;
constructor(private usersService: UsersService) { }
ngOnInit(): void {
this.subscription = this.usersService.getUsers()
.subscribe(
res => {
this.lastInResponse = res[res.length - 1]; // the last object in response
this.users = res;
}
)
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
loadMore() {
this.subscription = this.usersService.loadMoreData('users', this.lastInResponse)
.subscribe(
res => {
this.lastInResponse = res[res.length - 1];
this.users = this.users.concat(res); //concat new values with prevoius
}
);
}
}
It works fine, but I would like to clean my code and subscribe to users collection with a more reactive style, like this:
import { Observable } from 'rxjs';
.
.
.
.
users$: Observable<User[]>;
.
.
.
.
ngOnInit(): void {
this.users$ = this.usersService.getUsers();
}
and then use the "async" pipe in the HTML template.
My question is how could I refactor my code? Especially how to extract the last object in response and then pass it to loadMore() method and finally concat another value with previous.
Thank You.
Upvotes: 0
Views: 228
Reputation: 5602
You can try the following code -
import { Component, OnDestroy, OnInit } from '@angular/core';
import { User } from '../../../shared/models/user';
import { UsersService } from '../../../shared/services/users.service';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import {filter, switchMap, tap} from 'rxjs/operators';
@Component({
selector: 'app-users',
templateUrl: './users.component.html',
styleUrls: ['./users.component.scss']
})
export class UsersComponent implements OnInit, OnDestroy {
// lets setup a behavior subject to hold the users to be rendered; I have initialized the list with empty array; you can initialized to null as per your need
usersToRender$ = new BehaviorSubject<User[]>([]);
// this will be used to ensure that the initial users list will be fetched
initialUsers$: Observable<User[]>;
// this will be used to notify that the user has requested to loadMore data
loadMore$ = new BehaviorSubject<boolean>(false);
fetchMore$: Observable<any>;
lastInResponse: any;
constructor(private usersService: UsersService) { }
ngOnInit(): void {
this.initialUsers$ = this.usersService.getUsers()
.pipe(
tap(res => {
this.lastInResponse = res[res.length - 1];
this.usersToRender$.next(res);
}),
);
this.setupLoadMoreObservable();
}
private setupLoadMoreObservable() {
this.fetchMore$ = this.loadMore$.pipe(
filter(l => l === true),
switchMap(() => {
return this.usersService.loadMoreData('users', this.lastInResponse)
}),
tap(res => {
this.lastInResponse = res[res.length - 1];
this.usersToRender$.next([...this.usersToRender$.getValue(), ...res]);
this.loadMore$.next(false);
})
)
}
ngOnDestroy() {
}
loadMore() {
this.loadMore$.next(true);
}
}
In the template add the following code. Also, use usersToRender$
to render the users
<ng-container *ngIf="initialUsers$ | async"></ng-container>
<ng-container *ngIf="fetchMore$ | async"></ng-container>
Upvotes: 1