【问题标题】:Angular + rxjs : pattern for collection filtering where filter and collection are streamsAngular + rxjs:集合过滤模式,其中过滤器和集合是流
【发布时间】:2017-11-07 15:36:56
【问题描述】:

有没有更好的方法来写这个。我想加载一些数据,订阅在下拉列表中加载的行为主题,每次更改我想过滤数据。它可以工作,但不确定这是通过嵌套 BehaviorSubject 编写它的最佳方式,还是我应该订阅从 http 加载数据的 forkJoin 的完成事件

ngOnInit() {

//load all the data

let array1: Observable<any[]> = this.service1.getAll();

let array2: Observable<any[]> = this.service2.getAll();    

let observableBatch = [array1, array2];


Observable.forkJoin(observableBatch).subscribe(data => {

  this.items1 = data[0] as Array<any>;

  this.items2 = data[1] as Array<any>   


  //subscribe to BehaviorSubject Observable, filter on every change        
  this.someService.selectedItem$.subscribe((item: any) => {

    if (item) {         

      this.filterItemsByItemId(item.itemId);
    }

   }, (error:any)=>{

  //error

});     


}, (error: any) => {

  //error


});

}

【问题讨论】:

  • 为什么要在订阅中订阅?这很奇怪
  • 这就是我问这个问题的原因?我需要加载一堆数组,并在加载后通过订阅加载应用程序加载的行为主题来过滤它们。你能告诉我一个更好的方法而不是问我这个问题吗
  • 为什么要使用嵌套订阅?因为我没有看到你在第二次订阅中使用item1items2
  • items1 和 items2 根据最初发出值但我的更改的行为主题值进行过滤,因此我需要在过滤它们之前加载数组。 this.filterItemsByItemId(item.itemId);在那个方法里面是 this.items1.filter
  • 我只是想知道也许我应该移动到完成的事件来订阅而不是嵌套,顺便说一句,这并不奇怪,这不是一个简单的英雄应用程序,这个应用程序有数百万行代码和复杂的组件...大声笑

标签: angular rxjs


【解决方案1】:

你有一个嵌套订阅的事实真的很奇怪。 通常我使用以下方法(假设您只加载一次所有数据):

@Component({
 changeDetection: ChangeDetectionStrategy.OnPush
})
export class FooComponent {
  dataOne$: Observable<any[]>; // depending on if you use the unfiltered collections or not this members can be removed
  dataTwo$: Observable<any[]>;

  selected$: Observable<any>;    
  filteredOne$: Observable<any[]>;
  filteredTwo$: Observable<any[]>;

  constructor(){
   this.dataOne$ = this.service1.getAll()
     .share(); // avoid multiple requests for this data
   this.dataTwo$ = this.service2.getAll()
     .share(); // avoid multiple requests for this data

   this.selected$ = this.someService.selected$; // this should be an exposed observable, not the Subject itself

   filteredOne$ = this.selected$
    .withLatestFrom(this.dataOne$,(selected,elements)=>({selected,elements}))
    .map(data => elements.filter(element => //add filter logic using data.selected));
   filteredTwo$ = this.selected$
    .withLatestFrom(this.dataTwo$,(selected,elements)=>({selected,elements})
    .map(data => elements.filter(element => // add filter logic using data.selected));    
  }
}

}

我用这个存档的:

  • 推送更改检测
  • 主要使用async 管道从可观察对象中提取数据

通过这种方法,您可以使用模板中的selected$ 流来呈现当前值,并使用filtered$ 流来呈现相应集合的过滤元素。

如果数据集合可以发出新值,那么我通常使用 static combineLatest 运算符而不是 withLatestFrom。两者的区别在here解释。

相信我:无论您调用订阅,您的代码都可以重构为(ab)使用async管道:D

【讨论】:

  • 看起来好多了,我不知道最新的From,我现在要去看看。是的,过滤器是公开的可观察对象,而不是订阅。它是应用程序标题中的下拉菜单,因此当它更改时它会发出/推送新值,并且订阅的组件将过滤数据。非常感谢
  • 在这种情况下,在流中推送新值应该触发集合的重新过滤:)
  • 是的,我还没有遇到过这种模式,我一直在用 rxjs 学习新东西,非常感谢。
  • 很高兴它有帮助,我们都是这样开始的 ;)
  • 顺便说一句,不要将此视为绝对真理。可能有更好的方法来处理相同的问题。 Rx 是一个充满可能性的世界
【解决方案2】:

我没有看到你的数据加载有问题,困扰我的是behaviorSubject。当您在 Angular 中有输入绑定时,为什么要这样做?

我猜你的下拉列表是&lt;select&gt;。这给了

<select #myDropdown (change)="changeValue(myDropdown.value)">
  <option value="0">0</option>
</select>

在你的组件中:

changeValue(value) {
  // Let's say you request an endpoint
  this.myService.myMethod(value).subscribe(data => /* do your thing here */);

  // Want to filter your data ? No problem 
  this.items1 = this.items1.filter(item => item['your property here'] === value);
  this.items2 = this.items2.filter(item => item['your property here'] === value);
}

这似乎更容易和更短,不是吗?

【讨论】:

  • 是的,我知道,它是组件外部的下拉菜单,在应用程序中发生了很大变化,因此我需要订阅它。它位于顶部栏中,当更改菜单更改时,用户过滤器,角色过滤器因此需要是那种类型。
  • 仍然看不到 BehaviorSubject 的需要,我有一个类似的应用程序,但我不使用它。但无论如何,如果你想使用它,我看不出你的代码有什么问题,即使它只是一个例子!
  • 因为它是一个路由组件,所以我无法将顶部栏中的组件输入到另一个模块中另一个组件内的组件。这是一个复杂的应用程序,所以不仅仅是页面上的简单组件,我正在使用输入绑定将 selectedItem 绑定到其他组件
  • 应用服务无法解决问题?我总是有一个SessionService 来存储我所有的用户数据/选择,例如主题、帐户等。而且我没有看到绑定,所以这可能解释了混乱!
  • 是的,这就是我正在做的,但使用行为主题,因此价值被推送到组件并且它们做出相应的反应。
猜你喜欢
  • 1970-01-01
  • 2016-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-10-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多