Angular 动态过滤具有多个条件的 RxJS observable
Angular dynamically filter RxJS observable with multiple condition
我想使用来自 Angular FireStore 的数据实现动态客户端过滤。
我有以下服务,它或多或少地工作,但它真的很冗长,我认为它可以改进。我的问题是 filteredFiles$ 部分,我认为正确使用 RxJs 运算符可以省略我正在使用的 if 逻辑,但我不知道如何。
如有任何帮助,我们将不胜感激。
谢谢。
服务:
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);
}
并且在模板中:
<li *ngFor="let file of this.fileService.filteredFiles2$ | async">
<app-display-file [file]="file"></app-display-file>
</li>
您可以使用 else if
而不是多个 if
。
pipe(
map(([files, extension, channel, teamId, id]) => {
if (extension === null && channel === null && teamId === null && id === null) {
return files;
} else {
const isChannel = Boolean(channel !== null);
const isExtension = Boolean(extension !== null);
const isId = Boolean(id !== null);
if (isChannel && isExtension && isId) {
return files.filter((file) =>
file.channel === channel && extension.includes(file.extension) && id.includes(file.id));
} else if (isExtension && isId) {
return files.filter(
(file) => extension.includes(file.extension) && id.includes(file.id));
} else if (isChannel && isExtension) {
return files.filter(
(file) => file.channel === channel && extension.includes(file.extension));
} else if (isId) {
return files.filter((file: FileModel) => id.includes(file.id));
} else if (isExtension) {
return files.filter((file: FileModel) => extension.includes(file.extension));
} else if (isChannel) {
return files.filter((file: FileModel) => file.channel === channel);
}
}
}
)
这里可以调整多项内容。
假设您永远不会将值 null
推送到任何可观察对象,您可以将 BehaviorSubject
替换为 ReplaySubject(1)
(缓冲区 1
)。这样您就不必提供初始值,但仍然能够缓冲一个值并在订阅后立即发出它。
但是,如果您可以按 null
,那么您可以就地使用三元运算符来检查该值是否已定义。
尝试以下方法
files$: Observable<FileModel[]>;
public filteredFiles$: Observable<FileModel[]>;
public sourceFilter$ = new ReplaySubject<string>(1);
public extensionFilter$ = new ReplaySubject<string[]>(1);
public channelIdFilter$ = new ReplaySubject<string>(1);
public teamIdFilter$ = new ReplaySubject<string>(1);
public idFilter$ = new ReplaySubject<string[]>(1);
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.channel$,
this.teamId$,
this.extension$,
this.id$
).pipe(map(
([files, channel, teamId, extensions, ids]) =>
files.filter(file =>
(channel ? file.channel === channel : true) &&
(teamId ? file.teamId === teamId : true) &&
(extensions ? extensions.includes(file.extension) : true) &&
(ids ? ids.includes(file.id) : true)
)
));
}
我想使用来自 Angular FireStore 的数据实现动态客户端过滤。
我有以下服务,它或多或少地工作,但它真的很冗长,我认为它可以改进。我的问题是 filteredFiles$ 部分,我认为正确使用 RxJs 运算符可以省略我正在使用的 if 逻辑,但我不知道如何。
如有任何帮助,我们将不胜感激。
谢谢。
服务:
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);
}
并且在模板中:
<li *ngFor="let file of this.fileService.filteredFiles2$ | async">
<app-display-file [file]="file"></app-display-file>
</li>
您可以使用 else if
而不是多个 if
。
pipe(
map(([files, extension, channel, teamId, id]) => {
if (extension === null && channel === null && teamId === null && id === null) {
return files;
} else {
const isChannel = Boolean(channel !== null);
const isExtension = Boolean(extension !== null);
const isId = Boolean(id !== null);
if (isChannel && isExtension && isId) {
return files.filter((file) =>
file.channel === channel && extension.includes(file.extension) && id.includes(file.id));
} else if (isExtension && isId) {
return files.filter(
(file) => extension.includes(file.extension) && id.includes(file.id));
} else if (isChannel && isExtension) {
return files.filter(
(file) => file.channel === channel && extension.includes(file.extension));
} else if (isId) {
return files.filter((file: FileModel) => id.includes(file.id));
} else if (isExtension) {
return files.filter((file: FileModel) => extension.includes(file.extension));
} else if (isChannel) {
return files.filter((file: FileModel) => file.channel === channel);
}
}
}
)
这里可以调整多项内容。
假设您永远不会将值
null
推送到任何可观察对象,您可以将BehaviorSubject
替换为ReplaySubject(1)
(缓冲区1
)。这样您就不必提供初始值,但仍然能够缓冲一个值并在订阅后立即发出它。但是,如果您可以按
null
,那么您可以就地使用三元运算符来检查该值是否已定义。
尝试以下方法
files$: Observable<FileModel[]>;
public filteredFiles$: Observable<FileModel[]>;
public sourceFilter$ = new ReplaySubject<string>(1);
public extensionFilter$ = new ReplaySubject<string[]>(1);
public channelIdFilter$ = new ReplaySubject<string>(1);
public teamIdFilter$ = new ReplaySubject<string>(1);
public idFilter$ = new ReplaySubject<string[]>(1);
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.channel$,
this.teamId$,
this.extension$,
this.id$
).pipe(map(
([files, channel, teamId, extensions, ids]) =>
files.filter(file =>
(channel ? file.channel === channel : true) &&
(teamId ? file.teamId === teamId : true) &&
(extensions ? extensions.includes(file.extension) : true) &&
(ids ? ids.includes(file.id) : true)
)
));
}