94

I'm trying to return an observable when I get a certain value in a subscriber, but I fail miserably.

This is the code:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<boolean> {
    // get route to be activated
    this.routeToActivate = route.routeConfig.path;

    // get user access levels        
    return this._firebase.isUserAdmin          <-- returns Subscription, not Observable
        .map(user => user.access_level)
        .subscribe( access => {
           // I need to return an observable here
        });
}

There are not many resources on observables in angular 2, so I don't know where to start. Can anyone help with this please?

UPDATE -> Working Version

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<boolean> {
            // get route to be activated
            this.routeToActivate = route.routeConfig.path;

            // get user access levels        
            return this._firebase.isUserAdmin
                .map(user => {
                    let accessLevel = user.access_level;

                    if (accessLevel === 'admin' ) {
                        return true;
                    }

                }).first();
        }
4
  • 1
    You should be googling Observables in rxjs rather than ng2. there are tons of resources Commented Oct 8, 2016 at 18:16
  • Thank you! I tried that but they don't come in the same format Angular 2 uses them. I guess I will have to learn that from scratch then. Commented Oct 8, 2016 at 18:35
  • Can you explain the context for this code further? Since subscribe returns Subscription object to unsubscribe it and thus not chainable, generally you want to do observable.subscribe(...); return observable . Commented Oct 8, 2016 at 18:38
  • 3
    Angular uses rxjs5. Many resources are about rxjs4. I guess this is what you mean with "don't come in the same format" (github.com/ReactiveX/rxjs == V5 vs github.com/Reactive-Extensions/RxJS == V4) Commented Oct 8, 2016 at 18:40

6 Answers 6

105

You can't return an observable from subscribe but if you use map instead of subscribe then an Observable is returned.

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<boolean> {
    // get route to be activated
    this.routeToActivate = route.routeConfig.path;

    // get user access levels        
    return this._firebase.isUserAdminObservable
        .map(user => {
           // do something here
           // user.access_level;
           return true;
         })
        .first(); // for the observable to complete on the first event (usually required for `canActivate`)
        // first needs to be imported like `map`, ...
}

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<boolean> {
    // get route to be activated
    this.routeToActivate = route.routeConfig.path;

    let subject = new Subject();
    // get user access levels        
    this._firebase.isUserAdminObservable
        .map(user => {
          let accessLevel = user.access_level; 
          if (accessLevel === 'admin' ) { 
            subject.emit(true); 
            subject.complete();
          } 
          return user;
        });
     return subject;
}
Sign up to request clarification or add additional context in comments.

15 Comments

Hi, thanks for the answer! I need to do some processing inside subscribe depending on the value and then return true|false
That was it. There are no errors now, but the route still doesn't unlock, although it's sending true. I will try to fiddle with it and get it working, maybe I'll learn something in the process. Thank you very much!
Sorry, I meant the map function seems to take a lambda which can access variables outside it's scope. But I guess that's what lambdas can do - It kind of surprised me. However, solved my problem!
@anytoe right, there is no way to prevent that. You have take care of that yourself.
@GünterZöchbauer, can you update the answer to include RxJS 6.0 code?
|
12

Do this if you want to subscribe to observable, process the result and then return the same result in subscribe:

function xyx(): Observable<any> { 
    const response = someFunctionThatReturnsObservable().pipe(map(result => {
          // here do any processing of the result //
          return result; // return back same result.
       }
    ))
   return response;
}

Comments

11

Couldn't we just use pipe with map from import { map } from 'rxjs/operators';?

import { map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root'
})
export class SearchHostService {
  constructor(private readonly http: HttpClient) {}

  searchForHosts(searchString: string): Observable<Employee[]> {
    return this.http
      .get<Employee[]>('./assets/host-users.json')
      .pipe(
        map(employees =>
          employees.filter(({ displayName }) =>
            displayName.toLowerCase().startsWith(searchString),
          ),
        ),
      );
  }
}

1 Comment

But what if we arent' returning Observable<Employee> but a different value? The original question is calling code that gets an Observable<User> but then he wants to return an Observable<bool>
6

We can convert the Observable object to promise using toPromise method. so the code can be implemented as followed:

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Promise<boolean> {
        // get route to be activated
        this.routeToActivate = route.routeConfig.path;

        // get user access levels        
        return this._firebase.isUserAdmin
            .map(user => {
                return (user.access_level === 'admin');
            }).toPromise();
    }

Comments

2

You don't need map, code below specifies a predicate and a projection function for first.

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<boolean> {
    this.routeToActivate = route.routeConfig.path;      
    return this._firebase.isUserAdminObservable
        .first((_, index) => index === 0, user => {
           // do something here
           // user.access_level;
           return true;
         })
}

More on first

Comments

0

You can create a new observable, and fire the event according to the access level.

canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<boolean> {
    // get route to be activated
    this.routeToActivate = route.routeConfig.path;

    // get user access levels
    return new Observable(subscriber=>{
       this._firebase.isUserAdmin
        .map(user => user.access_level)
        .subscribe(access => {
           // Return an observable!
           // Change your logic here...
           return access === XXX ? subscriber.next(true) : subscriber.next(false);
        }, err => subscriber.error());
    })
}

Reference: https://rxjs-dev.firebaseapp.com/guide/observable

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.