I want to achieve dynamic client side filtering with data from Angular FireStore.
I've the following service, which is working more or less, but it's really verbose and I think it can be improved. My problem is with the filteredFiles$ part, I think the if logic I'm using can be omitted with proper usage of RxJs operators, but I can't figure out how.
Any help would be appreciated.
Thank you.
The service:
files$: Observable<FileModel[]>;
public filteredFiles$: Observable<FileModel[]>;
public sourceFilter$ = new BehaviorSubject<string | null>(null);
public extensionFilter$ = new BehaviorSubject<string[] | null>(null);
public channelIdFilter$ = new BehaviorSubject<string | null>(null);
public teamIdFilter$ = new BehaviorSubject<string | null>(null);
public idFilter$ = new BehaviorSubject<string[] | null>(null);
private filesCollectionRef: AngularFirestoreCollection<FileModel>;
constructor(
private afs: AngularFirestore,
private userService: UserService
) {
this.filesCollectionRef = this.afs.collection<FileModel>('files', ref =>
ref.where('perm.readers', 'array-contains', this.userService.uid));
this.files$ = this.filesCollectionRef.valueChanges({idField: 'id'});
this.filteredFiles$ = combineLatest([
this.files$,
this.extensionFilter$,
this.channelIdFilter$,
this.teamIdFilter$,
this.idFilter$
]).pipe(
map(([files, extension, channel, teamId, id]) => {
if (extension === null && channel === null && teamId === null && id === null) {
return files;
} else {
if (channel !== null && extension !== null && id !== null) {
return files.filter(
(file) =>
file.channel === channel && extension.includes(file.extension) && id.includes(file.id)
);
}
if (extension !== null && id !== null) {
return files.filter(
(file) =>
extension.includes(file.extension) && id.includes(file.id)
);
}
if (channel !== null && extension !== null) {
return files.filter(
(file) =>
file.channel === channel && extension.includes(file.extension)
);
}
if (id !== null) {
return files.filter(
(file: FileModel) =>
id.includes(file.id)
);
}
if (extension !== null) {
return files.filter(
(file: FileModel) =>
extension.includes(file.extension)
);
}
if (channel !== null) {
return files.filter(
(file: FileModel) =>
file.channel === channel
);
}
}
}
)
);
filterByExtension(extensions: string[]) {
this.extensionFilter$.next(extensions);
}
filterByChannelId(channelId: string | null) {
this.channelIdFilter$.next(channelId);
}
filterByTeamId(teamId: string | null) {
this.teamIdFilter$.next(teamId);
}
filterById(id: string[] | null) {
this.idFilter$.next(id);
}
And in the template:
<li *ngFor="let file of this.fileService.filteredFiles2$ | async">
<app-display-file [file]="file"></app-display-file>
</li>