ngrx:使用前一个选择器中的变量进行比较

时间:2018-02-08 15:04:30

标签: angular rxjs ngrx-effects

我创建了一个糟糕的勒芒更新轮询机制。它检查URL上的更新,然后调度消息以更新对象。这项工作,但现在我只想在对象发生变化时调度此更新,因为该对象可能已经是最新的。为了能够验证这些对象,我创建了一个哈希:如果哈希值相等,那么对象也是如此。

现在,这种效果很好:

@Effect()
pollBatStore = this.actions$
  .ofType(BatStoresActions.POLL_BAT_STORE)
  .map((action: BatStoresActions.PollBatStore) => action.payload)
  .mergeMap((upd: BatUpdateJSON) => this.batStoreStorage.getBatStoreFromUrl(upd['_links'].pollUrl.href))
  .map((batStore: BatStore) => {
    return new UpdateBatStore(batStore);
  });

但显然,当更新通知进入时,这将始终在商店中更新我的对象。所以我想将我从API获取的对象与我在ngrx商店中的对象进行比较。现在,我如何组合对象以便比较它们的属性?

我试过这个,但它不起作用:

@Effect()
pollBatStore = this.actions$
  .ofType(BatStoresActions.POLL_BAT_STORE)
  .map((action: BatStoresActions.PollBatStore) => action.payload)
  .mergeMap((upd: BatUpdateJSON) => this.batStoreStorage.getBatStoreFromUrl(upd['_links'].pollUrl.href))
  .withLatestFrom((bs: BatStore) => this.batStoreRepository.getBatStoreByApiKey(bs.apiKey))
  .map(([batStore: BatStore, batStoreInMemory: BatStore]) => {
    if (batStore.apiHash !== batStoreInMemory.apiHash) {
      return new UpdateBatStore(batStore);
    } else {
      return {type: 'NO_ACTION'}
    }
  });

我认为我以错误的方式使用.withLatestFrom,但我需要来自我的对象的apiKey属性才能选择它。所以我想我需要另一个选择器,但哪一个呢?我无法弄清楚。

更新:这似乎现在正在运作,但我认为这是一个糟糕的解决方案。特别是通过和检查“假”'返回值两次。但它做了我想要的:当ngrx商店中的哈希与我的REST api告诉我相同时,它不会更新。

@Effect()
pollBatStore = this.actions$
  .ofType(BatStoresActions.POLL_BAT_STORE)
  .map((action: BatStoresActions.PollBatStore) => action.payload)
  .switchMap((upd: BatUpdateJSON) => {
    return this.batStoreRepository.getBatStoreByApiKey(upd.apiKey)
      .take(1)
      .map((bs: BatStore) => {
        console.log('Found batstore in mem: ', bs);
        if (upd.apiHash !== bs.apiHash) {
          return bs;
        } else {
          return false;
        }
      });
  })
  .switchMap(result => {
    console.log('Result from hash check ', result);
    if (result instanceof BatStore) {
      return this.batStoreStorage.getBatStoreFromUrl(result.links['self'].href)
        .take(1)
        .map(newBs => {
          console.log('New object: ', newBs);
          return newBs;
        });
    } else {
      return Observable.of(false);
    }
  })
  .map(result => {
    console.log('Final result', result);
    if (result instanceof BatStore) {
      return new UpdateBatStore(result);
    } else {
      return new NoAction();
    }
  });

1 个答案:

答案 0 :(得分:0)

几个月后,我发现forkJoin可能是更好的解决方案。起初我没有得到这个,因为从ngrx商店中选择一个对象会返回一个未完成的Observable(source)。所以forkJoin也从未完成。添加.take(1)解决了这个问题。

所以我改写了我的效果,现在它看起来像这样:

@Effect()
pollBatStore = this.actions$
  .ofType(BatStoresActions.POLL_BAT_STORE)
  .pipe(
    map((action: BatStoresActions.PollBatStore) => action.payload),
    mergeMap((upd: BatUpdateJSON) => forkJoin(
      of(upd),
      this.batStoreRepository.getBatStoreByApiKey(upd.apiKey)
    )),
    filter(([upd, batStore]) => {
      return upd.apiHash !== batStore.apiHash;
    }),
    mergeMap(([upd, batStore]) => {
      return this.batStoreStorage.getBatStoreFromUrl(upd._links.pollUrl.href);
    }),
    map(newBatStore => new UpdateBatStore(newBatStore))
  );

我不确定这是否是正确答案,但我认为无论如何分享可能会有所帮助。我仍然想知道的一件事是我认为效果应该总是返回一个动作。此代码可以正常运行,但如果链在过滤器处停止会发生什么?