2
votes

Trying to use the latest version of Firebase and Angular. Overall concept I am trying to achieve is admin authentication. I have achieved login logout functions. I have saved the googleAuth login user to database with UID. Now what I am trying to do is restrict some pages to Logged in and Admin. So I have assigned isAdmin: true as a value in the user object that is stored in the database. Basically I want to see if the user is admin and return true or false for the router params. I have been using Angular and Firebase on and off. I would consider myself a beginner. I can't quite understand how to migrate from AngularFire2 4 -> AngularFire 5. The current error I am getting is TS2345:

ERROR in src/app/admin-auth-guard.service.ts(15,38): error TS2345: Argument of type '(user: User) => AngularFireObject<AppUser>' is not assignable to parameter of type '(value: User, index: number) => ObservableInput<{}>'.
  Type 'AngularFireObject<AppUser>' is not assignable to type 'ObservableInput<{}>'.
    Type 'AngularFireObject<AppUser>' is not assignable to type 'ArrayLike<{}>'.
      Property 'length' is missing in type 'AngularFireObject<AppUser>'.

from what I think I understand the get function is returning an Object, but the error is expecting an observable. All I know about observables are that you can subscribe to them and you should destroy them...

I need to use currently logged in user id to query to database and return the user object matching the uid and then check to see if isAdmin is true or false. Then return that to the route params so I can restrict pages to Authenticated and Admin.

Please point me in the right direction.

admin-auth-guard.service.ts

import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { AuthService } from './auth.service';
import { UserService } from './user.service';
import 'rxjs/add/operator/switchMap';
import 'rxjs/add/operator/map';
import { Observable } from 'rxjs/Observable';

@Injectable()
export class AdminAuthGuardService implements CanActivate {

  constructor(private auth: AuthService, private userService: UserService) { }

  canActivate(): Observable<boolean> {
    return this.auth.user$.switchMap(user => this.userService.get(user.uid)).map(appUser => appUser.isAdmin);
  }

}

user.service.ts

import { Injectable } from '@angular/core';
import { AngularFireDatabase, AngularFireObject } from 'angularfire2/database';
import * as firebase from 'firebase';
import { Observable } from 'rxjs/Observable';
import { AppUser } from './models/app-user';

@Injectable()
export class UserService {

  constructor(private db: AngularFireDatabase) { }

  save(user: firebase.User) {
    this.db.object('/users/' + user.uid).update({
      name: user.displayName,
      email: user.email
    });
  }

  get(uid: string): AngularFireObject<AppUser> {
    return this.db.object('/users/' + uid);
  }

}

this is what I have been using for help: https://github.com/angular/angularfire2/blob/master/docs/version-5-upgrade.md

I don't understand how the annotations work exactly for the functions created. In reference to "observable" from this i assume means this canActivate() function extends from a Observable and should return a boolean (true/false).

Thank you for your time.

8
try get(uid: string): any - El houcine bougarfaoui
@BougarfaouiElhoucine that made one error go away thanks! A new one has popped up. "ERROR in src/app/admin-auth-guard.service.ts(15,101): error TS2339: Property 'isAdmin' does not exist on type '{}'." - alex
See the answer . - El houcine bougarfaoui

8 Answers

10
votes
 get(uid: string): Observable<any> {
    return this.db.object('/users/' + uid).valueChanges();
 }

And :

canActivate(): Observable<boolean> {
   return this.auth.user$.switchMap(user => 
   this.userService.get(user.uid)).map((appUser :any) => appUser.isAdmin);
}

For more information see : info

2
votes

AdminAuthGuard Service.

 canActivate(): Observable<boolean> {
 return this.auth.user$
 .switchMap(user => this.userService.get(user.uid).valueChanges())
 .map(appUser => appUser.isAdmin);
 }    

UserService.ts

get(uid:string):AngularFireObject<AppUsers>{
return this.db.object('/users/'+uid);
}
2
votes

In my case, it works.
AdminAuthGuard Service.

import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
import { AuthService } from './auth.service';
import { map, switchMap } from 'rxjs/operators';
import { Observable } from 'rxjs';
import { UserService } from './user.service';
import * as firebase from 'firebase';

@Injectable()
export class AdminAuthGuard implements CanActivate {

  constructor(private auth: AuthService,
  private userService: UserService) {}

  canActivate(): Observable<boolean> {
    return this.auth.user$.pipe(switchMap((user: firebase.User) =>
      this.userService.get(user.uid)).
      map((appUser) => appUser.isAdmin));
   }

}

UserService.ts

 get(uid: string): Observable<any> {
    return this.db.object('/users/' + uid).valueChanges();
 }
1
votes
Admin-auth-guard.service.ts

canActivate(): Observable<boolean> {
     return this.auth.user$
    .pipe(switchMap(user=> {
        return this.userService.get(user.uid).valueChanges()
        }),
        map(appUser=> appUser.isAdmin));
  }


user.service.ts

get(uid: string): AngularFireObject<AppUser>{
    return this.db.object('/users/'+ uid);
  }
0
votes

user.service.ts

get(uid: string): Observable<any> {
return this.db.object('/users/' + uid).valueChanges();
}

admin-auth-guard.service.ts

canActivate(): Observable<boolean> {
return this.auth.user$.pipe(switchMap(user => this.userService.get(user.uid).pipe(map((appUser: any) => appUser.isAdmin))));
}
0
votes

"FirebaseObjectObservable" replace it with "Observable" and import it from rxjs

import { Observable } from 'rxjs';

0
votes

I get this working with angular 8 only this way:

canActivate(): Observable<boolean> {
    return this.auth.user$.pipe(
        switchMap((user: firebase.User) =>
        this.userService.get(user.uid)
        .pipe(
          map((appUser) => appUser.isAdmin)
        )
      )
    )
  }

and UserService get method

get(uid: string): Observable<any> {
    return this.db.object('/users/' + uid).valueChanges();
}
0
votes

admin-auth-guard.service.ts

canActivate(): Observable<boolean> {
    return this.auth.user$
    .pipe(switchMap(user=> this.userService.get(user.uid).valueChanges()),
    map(appUser=> appUser.isAdmin));
 }

user.service.ts

get(uid: string): AngularFireObject<AppUser>{
  return this.db.object('/users/'+ uid);
}

it can be done like this below and could have directly mapped since both are Observables. But FirebaseObjectObservable is deprecated in latest versions of angular.

get(uid: string): FirebaseObjectObservable<AppUser>{
      return this.db.object('/users/'+ uid);
    }

So when we are using AngularFireObject we have to do .valueChanges() then it is going to return an observable so that we can return a Boolean observable finally in the canActivate()