Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filter all 'null' values from an Observable<T>

I have a service with a subject:

@Injectable() export class UserService() {
    private currentUserSubject = new BehaviorSubject<User>(null);
    public currentUser = this.currentUserSubject.asObservable().distinctUntilChanged(); 

    ... // emitting new User    
}

Have a component I inject this service into and subscribing on updates:

@Component() export class UserComponent {
    constructor(private userService: UserService) {
        this.userService.currentUser
            .subscribe((user) => {
                // I want to see not null value here
            })
    }
}

I want to apply something to Observable<User> to filter all null values and get into subscribe only when User is actually loaded.

like image 489
fasth Avatar asked Mar 27 '17 10:03

fasth


5 Answers

Add a filter operator to your observable chain. You can filter nulls explicitly or just check that your user is truthy - you will have to make that call depending on your needs.

Filtering out null users only:

public currentUser = this.currentUserSubject
                         .asObservable()
                         .filter(user => user !== null)
                         .distinctUntilChanged(); 
like image 93
snorkpete Avatar answered Oct 25 '22 23:10

snorkpete


Another way to check the value exists:

public currentUser = this.currentUserSubject
                         .asObservable()
                         .filter<User>(Boolean)
                         .distinctUntilChanged(); 
like image 31
ackuser Avatar answered Oct 26 '22 01:10

ackuser


with rxjs@6 and typescript the recommended (readable/maintainable) way is to define the following type guard:

export function isNonNull<T>(value: T): value is NonNullable<T> {
  return value != null;
}

and augment a subject with pipe and filter:

subject.pipe(filter(isNonNull))
like image 25
Amit Portnoy Avatar answered Oct 25 '22 23:10

Amit Portnoy


It's as simple as:

filter(user => !!user),

So would filter these falsy values (link to Falsy on MDN). or alternatively cast to a boolean like this:

filter(user => Boolean(user)),

If user evaluates to false, the filter will not be passed.


Update (after Mick his comment on type checking):

If you want to add some more proper typescript solution you could use a type predicate (user-defined type guard). See documentation on this here on https://www.typescriptlang.org/ .

Assume you have a class User. Example:

const isUser = (user: null|User): user is User => {
  // Using an instance of check here, which is not the most performant
  // but semantically most correct.
  // But you could do an alternative check and return a boolean instead
  return user instanceof User;
}

If you now do:

filter(user => isUser(user));

Typescript will understand that you have an object of type User after the filter.

like image 11
Wilt Avatar answered Oct 26 '22 00:10

Wilt


Another option for rxjs@6 is to create another wrapping operator of skipWhile.

// rxjs.utils.js
export const skipNull = () => <T>(source: Observable <T>): Observable<T> => source.pipe(skipWhile(value => value === null));

Usage:

this.observable.pipe(skipNull()).subscribe(...)
like image 3
noamyg Avatar answered Oct 25 '22 23:10

noamyg