I'm using Angular 9.0.4 and Firestore Database and I have some instructors with different locations and category of subject. The concept is that I will select my location and the subject I want (ex. Athens and Maths) and I will take back the instructors with those values. I found AngularFire to do that, but some the "combineLatest" operator that I need is deprecated and despite how hard I'm trying to convert the documentation code with the latest Rxjs I can't find a solution. Below is my code.
home.component.ts
import {Component, OnInit} from '@angular/core';
import {AngularFirestore} from '@angular/fire/firestore';
import 'firebase/firestore';
import {BehaviorSubject, Observable} from 'rxjs';
import {combineLatest, of} from 'rxjs';
import {map} from 'rxjs/operators';
export interface Instructors {
last_name: string;
location: string;
cate: string;
}
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: ['./home.component.scss']
})
export class HomeComponent implements OnInit {
instructors$: Observable<Instructors[]>;
locationFilter$: BehaviorSubject<string | null>;
cateFilter$: BehaviorSubject<string | null>;
constructor(afs: AngularFirestore) {
this.locationFilter$ = new BehaviorSubject(null);
this.cateFilter$ = new BehaviorSubject(null);
this.instructors$ = combineLatest(
this.locationFilter$,
this.cateFilter$
).map(([location, cate]) =>
afs.collection<Instructors>('items', ref => {
let query: firebase.firestore.Query = ref;
if (location) {
query = query.where('location', '==', location);
}
if (cate) {
query = query.where('cate', '==', cate);
}
return query;
}).valueChanges()
);
}
ngOnInit() {
}
filterByLocation(location: string | null) {
this.locationFilter$.next(location);
}
filterByCate(cate: string | null) {
this.cateFilter$.next(cate);
}
}
home.component.html
<div *ngIf="instructors$ | async; let instructors; else loading">
<ul>
<li *ngFor="let instructor of instructors">
{{ instructor.last_name }}
</li>
</ul>
<div *ngIf="instructors.length === 0">No results, try clearing filters</div>
</div>
<ng-template #loading>Loading…</ng-template>
<div>
<h4>Filter by</h4>
<div>
<h5>Size</h5>
<button (click)="filterByLocation('Thessaloniki')">Thessaloniki</button>
<button (click)="filterByLocation('Crete')">Crete</button>
<button (click)="filterByLocation(null)" *ngIf="this.locationFilter$.getValue()">
<em>clear filter</em>
</button>
</div>
<div>
<h5>Color</h5>
<button (click)="filterByCate('Leadership')">Leadership</button>
<button (click)="filterByCate('Maths')">Maths</button>
<button (click)="filterByCate(null)" *ngIf="this.cateFilter$.getValue()">
<em>clear filter</em>
</button>
</div>
</div>
firestore database be like
instructors{
randomId{
name: string;
cate: string;
location: string;
}
}
You are using the map
operator in your code but you need to subscribe to a new stream and in that case, you need to use the switchMap
operator. Starting with RxJS 6.0 (if I'm not mistaken) you need to use pipeable operators.
The following code should work:
this.instructors$ = combineLatest([this.locationFilter$, this.cateFilter$]).pipe(
switchMap(([location, cate]) =>
afs
.collection<Instructors>('items', ref => {
let query: firebase.firestore.Query = ref;
if (location) {
query = query.where('location', '==', location);
}
if (cate) {
query = query.where('cate', '==', cate);
}
return query;
})
.valueChanges()
)
);
Note: You can also use mergeMap
and flatMap
instead of switchMap
. You can read more about them and the difference between them here.