【问题标题】:Angular rxjs filter observableAngular rxjs 过滤器可观察
【发布时间】:2020-09-10 08:08:20
【问题描述】:

我正在尝试使用一系列过滤器过滤可观察的产品,但我真的不知道如何..

让我解释一下,我想将过滤结果设置为过滤产品。 对于过滤,对于每个过滤器,我必须检查产品的过滤器数组是否包含过滤器的名称,以及产品值数组是否包含过滤器 ID。

目前,过滤器有效,但仅适用于最后选择的过滤器,我想用我的 selectedFilters 数组中的所有过滤器过滤产品列表。我可以有一个或多个过滤器。

StackBlitz

export class ProductsFilterComponent extends BaseComponent implements OnInit {
    @Select(FiltersState.getAllFilters) filters$: Observable<any>;
    @Input() products$: Observable<Product[]>;
    filteredProducts$: Observable<Product[]>;
    public selectedFilters = [];

    constructor(
        private store: Store) { super(); }

    ngOnInit() {
        this.store.dispatch(new GetAllFilters());
    }

    private filterProducts() {
        this.filteredProducts$ = this.products$.pipe(
            map(
                productsArr => productsArr.filter(
                    p =>
                        p.filters.some(f => this.selectedFilters.some(([selectedF]) => selectedF === f.name.toLowerCase()) // Filter name
                            && f.values.some(value => this.selectedFilters.some(([, filterId]) => filterId === value)) // Filter id
                        )
                )
            )
        );
        this.filteredProducts$.subscribe(res => console.log('filtered:', res));
    }
}

这是产品对象的结构

这里是 selectedFilters 的结构

提前非常感谢您:-)。

【问题讨论】:

  • 你能创建一个最小的可重现的 sn-p 吗?即 StackBlitz。以便人们更好地理解您的问题
  • 如果您使用的是ngrx,听起来应该有一些像filteredProducts这样的状态,它同时使用商店中的产品和过滤器。
  • @SupunDeSilva 感谢您对我的问题感兴趣。当然,这是我的代码:-)
  • 好吧,如果可能,添加GetAllFiltersFiltersState 的声明。因此,如果有问题,更容易发现。此外,您还有 BaseComponent 不确定它是否与问题相关。总而言之,让其他用户更容易找到问题,而无需做出假设。

标签: angular filter rxjs observable rxjs-pipeable-operators


【解决方案1】:

这是一个stackblitz,其中包含一个有效的示例。

总结一下:

  1. 我们监听产品和过滤器的变化,并使用combineLatest 将它们组合成一个可观察对象:
    const productsAndFilters: Observable<[Product[], ProductFilter[]]> = combineLatest([
      this.products$,
      this.filters$.pipe(startWith(initialFilters)),
    ]);
  1. 然后我们通过管道将组合的 observable 导入一个实际执行过滤工作的函数
    this.filteredProducts$ = productsAndFilters.pipe(
      map(([products, filters]) => {
        return AppComponent.filterProducts(products, filters);
      }),
    )
  1. 每次products$ 发出一个新值,我们都会更新filteredProducts$。每次filters$ 发出一个新值时,我们也会更新它们。

  2. 请注意,combineLatest 在每个可观察对象至少发出一次之前不会发出。出于这个原因,如果您的“selectedFilters”在开始时没有发出任何内容,您可以使用startWith 运算符创建一个具有起始值的可观察对象。这确保combineLatest 将在products$ 发出一个值时立即发出一个值。


我还将过滤代码移到了一个静态函数中:

  private static filterProducts(products: Product[], filters: ProductFilter[]) {
    // we can combine filters into a single function
    // so that the code a tiny bit more readable
    const matchesAllFilters = (product: Product) => filters.every(
        ([filterName, filterValue]) => product.filters
          .some(f => f.name === filterName &&
                     f.values.some(value => value === filterValue))
    );

    return products.filter(matchesAllFilters);
  }

在我的示例中,我假设您希望所有过滤器都得到满足。如果您希望满足“至少一个”过滤器,可以将filters.every 更改为filters.some

【讨论】:

  • 非常感谢您的帮助和解释!我想我明白你想要什么,但是我没有让逻辑起作用。我创建了一个 stackBlitz stackblitz.com/edit/…
  • 更新过滤器列表后,我会记录过滤后的产品,并且所有产品的结果总是相同
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2020-09-05
  • 2017-07-01
  • 2017-06-11
  • 1970-01-01
  • 1970-01-01
  • 2018-07-01
  • 2018-11-23
相关资源
最近更新 更多