6

I've got an Observable that listens to some user input from a text box. If the observed string's length is >=3 (filter), it executes some HTTP call (switchMap).

Now I'd like to detect somehow if the user input has been filtered. Reason:

  • If the HTTP call has been done, it should show the results.

  • If the user input got filtered (== is invalid), it should clear the results.

Here's the code I'd like to have (see: ifFiltered):

this.userInput.valueChanges
    .filter(val => val && val.length >= 3)
    .ifFiltered(() => this.results = [])
    .switchMap(val => getDataViaHTTP())
    .subscribe(val => this.results = val);

I know, I could place that logic within the filter function for this simple example. But what if I have 10 different filters?

Did I miss any method that satisfies my needs?

Thanks in advance!

5 Answers 5

4

Either use partition like here RxJS modeling if else control structures with Observables operators

Or instead of filter use map and pipe the object if the former filter condition is true or null otherwise. so you can catch the null where ever you want in your chain with a filter.

Last option call some function in the else part of the filter function

Sign up to request clarification or add additional context in comments.

2 Comments

Looks a bit hacky, but still quite nice using o.map(val => (val && val.length >= 3)?val:null).subscribe(val => this.results = val==null ? [] : val)
What is "the else part of the filter function"?
4

We've had a similar case and tried it with partition as mentioned above but found it much handier to use throw here. So for your code

this.userInput.valueChanges
    .do(val => {
      if (!val || val.length < 3) {
        throw new ValueTooShortError();
      }
    })
    .switchMap(val => getDataViaHTTP())
    .do(val => this.results = val)
    .catch(() => this.results = [])
    .subscribe();

Comments

3

I suggest having a common event stream, creating two filtered streams, and merging the two before subscription:

var o = this.userInput.valueChanges;

var empty= o.filter(t=> t.length < 3)
.map(t=>[])

var nonempty = o.filter(t=> t.length >= 3)
    .switchMap(t=> getDataViaHTTP());

empty.merge(nonempty).subscribe(val => this.results = val);

2 Comments

Looks quite good, but how would you do that with 5 filters? I don't want to write each filter in a positive and negative way.
You could save the merged filter, and combine it with other streams in any number of ways. The positive/negative filters is only in the beginning.
2

I found another nice solution for my use case using Validators:

(I know that this is no solution using Observables as the question stated. Instead it's using Angular2 features to workaround the problem nicely.)

this.userInput.validator = Validators.compose([
    Validators.required,
    Validators.minLength(3)
]);

this.userInput.valueChanges
    .filter(val => this.userInput.valid)
    .switchMap(val => getDataViaHTTP())
    .subscribe(val => this.results = val);

Now I can use the userInput.valid property and/or the userInput.statusChanges Observable to keep track of the input value.

Comments

0

May be it's late, but wanted to post for the members still seeking a more cleaner approach to validate IF EMPTY inside .map:

of(fooBar).pipe(
      map(
        (val) =>
          ({
            ...val,
            Foo: (val.Bar
              ? val.Foo.map((e) => ({
                  title: e.Title,
                  link: e.Link,
                }))
              : []) as fooModal[],
          }));

This code returns a empty array if val.bar is missing, but it's just an example you can use any validation & expression instead.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.