【问题标题】:How to update Observable / BehaviorSubject from code?如何从代码更新 Observable / BehaviorSubject?
【发布时间】:2017-11-24 12:01:37
【问题描述】:

我需要更新可观察对象的属性。我设法让它工作,但我真的不喜欢我是怎么做到的。

next(filter$: BehaviorSubject<GridCollectionFilter>, collection: GridCollectionModel<any>) {
  let filter = filter$.value;

  if (!filter.page) {
    filter.page = 1;
  }

  filter.page++;

  if (filter.page > collection.pageCount) {
    filter.page = collection.pageCount;
  }

  this.updateFilter(filter$);
}

first(filter$: BehaviorSubject<GridCollectionFilter>) {
  filter$.value.page = 1;
  this.updateFilter(filter$);
}

last(filter$: BehaviorSubject<GridCollectionFilter>, collection: GridCollectionModel<any>) {
  filter$.value.page = collection.pageCount;
  this.updateFilter(filter$);
}

updateFilter(filter$: BehaviorSubject<GridCollectionFilter>, filter?: GridCollectionFilter) {
  filter$.next(_.clone(filter || filter$.value));
}

在这种情况下,是否有更优雅的方式来更新 filter$ 值对象?

我真的不喜欢每次我只想通过代码更新被观察对象的一些属性时通过执行filter$.next(_.clone(filter$.value)) 将可观察对象的克隆喂给自己的想法。我正在使用 _.clone() 来破坏引用(否则 rxjs distinctUntilChanged() 不会因为明显的原因触发)。

是否有某种 rxjs 扩展可以缩短我正在做的事情?我正在考虑类似于filter$.commitValueChanges() 的东西,它会在内部执行filter$.next(_.clone(filter$.value))

我对 observables 很陌生,所以我不确定我一开始是否以完全错误的方式考虑这个问题。我知道当我想触发更改侦听器时,我应该提交新状态。我不确定您是否应该直接修改 filter$.value ?或者您通常应该有两个对象 - filter(过滤器)和 filter$(可观察的过滤器)然后绝对不要直接改变 filter$,而只需在每次更改时将 filter 提交到 .next()filter 上发生了吗?但这似乎是多余的,因为我们现在有两个对象,我们正在通过应用程序更改一个对象,并且必须通过附加各种事件处理程序来手动处理,而所有这些只是为了能够更新另一个对象 - filter$。这对我来说似乎太过分了。

谢谢。

【问题讨论】:

    标签: angular rxjs observable angular2-observables behaviorsubject


    【解决方案1】:

    要从您的代码中删除 _clone(),您应该使用 distinctUntilKeyChanged()。它将检查对象中的任何键是否被更改。

    DistinctUntilKeyChanged API

    但更好的方法是:

    filter$: BehaviorSubject<GridCollectionFilter>;
    collection: GridCollectionModel<any>;
    currentFilter: GridCollectionFilter; 
    
    ngOnInit() {
      this.filter$.subscribe(filter => {
        this.currentFilter = filter;
        // Here for example invoke rest api with filter to change collection
      })
    }
    
    next() {
      if (!this.currentFilter.page) {
        this.currentFilter.page;
      }
    
      this.currentFilter.page++;
    
      if (this.currentFilter.page > this.collection.pageCount) {
        this.currentFilter.page = collection.pageCount;
      }
    
      this.filter$.next(this.currentFilter);
    }
    
    first() {
      this.currentFilter.page = 1;
      this.filter$.next(this.currentFilter);
    }
    
    last(filter$: BehaviorSubject<GridCollectionFilter>, collection: GridCollectionModel<any>) {
      this.currentFilter.page = collection.pageCount;
      this.filter$.next(this.currentFilter);
    }
    

    【讨论】:

    • 我喜欢distinctUntilKeyChanged() 位,这很有帮助,我没有意识到这一点。但我不会非常喜欢添加额外的 currentFilter 字段,因为它会增加额外的复杂性并以某种方式复制代码。我试图避免拥有两个过滤器对象,并且我认为一个应该能够做到这一点。拥有其中两个会让人觉得既麻烦又麻烦。
    • 我粘贴的方法实际上是在服务类中,而不是在组件中。 filter$ observable 是由自定义 BaseGridComponent 创建的,然后事件处理程序方法(下一个、最后一个和其他一些我没有粘贴的)应该更新 filter$ 如果有意义的话. filter$.value 是被观察到的,当它发生变化时,过滤器对象被发送到后端以获取数据。
    • 所以把它移到组件 BaseGridComponent 中。在您通过 BaseGridComponent 扩展的网格中,您开始侦听过滤器更改,然后调用 rest api 重新加载集合。你怎么看?
    • 我对此进行了更多思考,稍后我会测试一些想法,然后再回来。与此同时,也许我会在这里得到更多的答案/想法。
    • 好的,我观看了有关 Angular 中 RxJs 的完整课程,现在我对整个事情以及您的建议有了更好的理解(有一个用于更新 filter$ 的单独过滤器)似乎默认方式。谢谢。
    【解决方案2】:

    对于它的价值,我完全理解您的问题 - 因为我正在努力解决完全相同的问题。我遇到了许多 Angular 更改检测问题,因为我直接修改了主题的值。现在我只是_.cloneDeep 值,修改它,然后用.next() 将它反馈给主题。我也很欣赏主题的某种捷径来检测更改并发布新对象。暂时还没有找到更好的解决方案。

    【讨论】:

      【解决方案3】:

      如果您唯一的问题是您需要克隆对象,那么这些技巧之一可能会对您有所帮助:

      • distinctUntilChanged 运算符默认比较对象身份。但是它需要一个可选的compare 函数,您可以使用它来实现您想要的任何比较登录。

        .distinctUntilChanged((a, b) => a.page === b.page)
        

        您还可以使用它来比较具有相同属性的两个不同实例

        .distinctUntilChanged((a, b) => JSON.stringify(a) === JSON.stringify(b))
        
      • 如果您可以只创建浅层副本,则可以使用以下其中一种:

        Object.assign({}, filter)
        

        或者这个:

        { ...filter }
        

        这两个都是浅拷贝。

      • 如果您需要深拷贝并且不担心性能(似乎您的对象非常小),您可以使用JSON

        JSON.parse(JSON.stringify(filter))
        

        这样您就不需要任何第三方库。

      【讨论】:

      • 其实不仅仅是克隆。我不介意拥有_.clone()(通常是lodash),因为我所有的应用程序都默认拥有它,因为它是一个非常有用的库。只是觉得用它自己的克隆的可观察值喂养.next() 对于像更新一两个属性这样简单的事情来说有点开销。我觉得有一个名为.nextSelf() 的简单方法会减少代码量。但在做出判断之前,我需要更多地研究可观察的东西。现在我只是在寻找简化一点的方法。
      • 我想我不明白你的代码应该做什么。
      • 我不想让每个人都厌烦整个事情,因为我认为围绕我的问题的真正含义会产生太多噪音。我只是想知道是否有某种filter$.commitValueChanges() 可以完全取代对filter$.next(_.clone(filter$.values)) 的需求。我会稍微更新一下我的问题。无论如何,感谢您写下答案。
      猜你喜欢
      • 1970-01-01
      • 2017-01-22
      • 2019-04-21
      • 1970-01-01
      • 2020-03-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多