Evan
Evan

Reputation: 249

Apply rxjs operators conditionally

I have a multi-select search function using different logic applying operators to process the response.

When user chooses more than one options and clicks search, the program in the if part (apply toArray, map), otherwise in else.

How to apply the different logic and operators by conditions in pipe instead of using if-else outside:

if (criteria.hasOwnProperty('IAAGRE_AGST_CODE') &&
  criteria.IAAGRE_AGST_CODE.length) {
  const cs = criteria.IAAGRE_AGST_CODE.map(s => ({
    ...criteria,
    IAAGRE_AGST_CODE: s,
  }))
  return from(cs).pipe(
    concatMap(c => this.http.post < Agreement[] > (this.pckPath, c)),
    toArray(),
    map((data: any[]) => _.flatten(data)),
    tap((data) => {
      this.entityStore.reset()
      this.entityStore.addEntities(data)
    }),
  )
} else {
  return this.http.post < Agreement[] > (this.pckPath, criteria).pipe(
    tap((data) => {
      this.entityStore.reset()
      this.entityStore.addEntities(data)
    }),
  )
}

Upvotes: 0

Views: 486

Answers (2)

olivarra1
olivarra1

Reputation: 3399

It rather depends, but on your case, I think the most simple is to break it down in two steps. With a ternary condition it should look mostly right:

const entityData$ =
  criteria.hasOwnProperty("IAAGRE_AGST_CODE") &&
  criteria.IAAGRE_AGST_CODE.length
    ? from(cs).pipe(
        concatMap((c) => this.http.post<Agreement[]>(this.pckPath, c)),
        toArray(),
        map((data: any[]) => _.flatten(data))
      )
    : this.http.post<Agreement[]>(this.pckPath, criteria);

return entityData$ = entityData$.pipe(
  tap((data) => {
    this.entityStore.reset()
    this.entityStore.addEntities(data)
  }),
);

If you don't like ternaries, you can always write it as a function.

In this case it works because the optional operators on the pipe are the ones at the end. If they were on the middle, something I've sometimes done is also inline the condition:

return source$.pipe(
  switchMap(() => { /* ... */ }),
  shouldApplyOperator
    ? switchMap(() => { /* ... */ })
    : source$ => source$,
  switchMap(() => { /* ... */ }),
)

An operator is just an Observable enhancer: A function that receives an observable and returns an observable. So if one of the operators is the identity function source$ => source$, then that operator essentially does nothing. If you need to optionally activate more than one, you can use the standalone pipe function to generate one single operator that makes everything, then use the method above to do that.

Upvotes: 1

user17740968
user17740968

Reputation:

Little known trick, but pipe is actually an operator you can use to group other operators. So you can do something like this :


const operators = condition ? pipe(
  concatMap(...),
  toArray(...),
  map(...),
  tap(...)
) : pipe(
  tap(...),
);

return yourObservable.pipe(operators);

Upvotes: 1

Related Questions