2

I am using an "auth service" to keep all the user authentication functions. When the user is authenticated, I get the user's id and I fetch the relevant record from the database table, but I fail to get the value of the "role" field. The code I am using in the constructor is this:

constructor(
    private _firebaseAuth: AngularFireAuth,
    private db: AngularFireDatabase,
    private router: Router) {
    this.user = _firebaseAuth.authState;

    this.user.subscribe(
        (user) => {
            if (user) {
                this.userDetails = user;

                this.userEmail = this.userDetails.email;
                this.uid = this.userDetails.uid;

                this.getUserRole(this.uid).subscribe(result => {
                    this.role = result.role;
                    console.log('role >>>>>>>>>>>>>', this.role);
                });

                console.log('uid >>>>>>>>>>>>>', this.uid);
                console.log('role >>>>>>>>>>>>>', this.role);
            }
            else {
                this.userDetails = null;
            }
        }
    );
}

getUserRole(userId: string): Observable<any> {
    return this.db.list('users', ref => ref.orderByChild('uid').equalTo(this.uid)).valueChanges()
        .map(result => result[0]);
}

this.uid has the correct value, but this.role is undefined.

I suppose the problem that the call to the database table is asynchronous. How can i get this value?

Elias
  • 347
  • 2
  • 5
  • 20

3 Answers3

1

I have added some code to make you understand the callbacks of observable. Have a look at it.

this.currUser.subscribe(
        (val) => {
            // This is called as success callback
            this.role = val[0].role;
           // perform task using this.role. You should be writing your code here.
        },
        (error) => {
                // This is called as error callback, It will be executed if any erorrs were thrown from `currUser()`
                // log if there are any errors here
        },
        () => {
            // This is called as complete block
            // This will be executed after success callback or the error callback.
        }
);

UPDATE :

this.user.subscribe(
    (user) => {
        if (user) {
            this.userDetails = user;

            this.userEmail = this.userDetails.email;
            this.uid = this.userDetails.uid;

            this.getUserRole(this.uid).subscribe(result => {
                this.role = result.role;
                console.log('role >>>>>>>>>>>>>', this.role);       // print log 1
            });

            // log 2 will be executed before log 1 this is due to asynchronous behaviour
            console.log('role >>>>>>>>>>>>>', this.role);           // print log 2
        }
        else {
            this.userDetails = null;
        }
    }
);

log 2 will be executed before log 1 this is due to asynchronous behaviour. If you are thinking that your code executes sequentially with line numbers, then you are wrong. getUserRole is asynchronous method.

You can access this.role from other components as well using get or set. Whatever you have done is appropriate.

What you should be doing is fetch this.role only if it is not undefined.

In your external component where your are trying to access this.role, check for undefined first and then access it.

some-other component .ts

if(this.role != undefined){
 let role = this.role
}

DEMO : Check here to see how asynchronous methods work

Amit Chigadani
  • 28,482
  • 13
  • 80
  • 98
  • I understood how the structure should be, but I don't understand what should be done in the `() =>`. Outside `(val) =>` variable `this.role` is still undefined – Elias May 31 '18 at 11:21
  • You cannot use `this.role` until success block is executed. It is an asynchronous operation. Your `console.log(this.role)` outside subscribe block will be executed before your success block gets executed. That is why you get undefined. – Amit Chigadani May 31 '18 at 11:36
  • I understand why I get undefined in `console.log`. If I put the `console.log` in `() =>` nothing happens. This means that `subscribe` is never completed? Is there any other way to get the value? – Elias May 31 '18 at 11:42
  • In your code you say `console.log(this.role)` inside success block works, now you are saying nothing happens. – Amit Chigadani May 31 '18 at 11:45
  • The `console.log(this.role)` was outside the success block. I put in the success block after following your example and at that point nothing happened – Elias May 31 '18 at 12:03
  • What does `val[0]` print inside success callback? – Amit Chigadani May 31 '18 at 12:13
  • `val[0]` inside success callback prints the object which contains the user's data (name, last name, email, role). `var[0].role` prints the user's role. How can I use the value of `var[0].role outside `'subscribe'? This the code `this.currUser.subscribe( (val) => { this.role = val[0].role; }, (error) => { console.log('Error'); }, () => { console.log('this.role:', this.role); // not executed } );` – Elias Jun 01 '18 at 06:10
  • Please add your entire code, where you are trying to access `this.role` or create a plunker for the same. – Amit Chigadani Jun 01 '18 at 06:50
  • I modified the code to use a function: `getUserRole(userId: string): Observable { return this.db.list('users', ref => ref.orderByChild('uid').equalTo(this.uid)).valueChanges() .map(result => result[0]); }` and this is the function call in the constructor `this.getUserRole(this.uid).subscribe(result => { this.role = result; });` I am trying to access `this.role` after the function call – Elias Jun 01 '18 at 07:58
  • Show the full code, where you are trying to access `this.role` along with the above code. Add this code to your question. So that I can suggest to rearrange your code. – Amit Chigadani Jun 01 '18 at 08:42
  • I updated the code in the question. The `console.log('role >>>>>>>>>>>>>', this.role);` prints a value. The second `console.log('role >>>>>>>>>>>>>', this.role);` returns undefined. I want to read the `role` from another component using `get` `set` of store it in local storage – Elias Jun 01 '18 at 10:38
  • I have added some more description. Hope you will get that. – Amit Chigadani Jun 01 '18 at 10:55
  • Thank you for your advice. This asynchronous execution causes the variable not to be undefined or have the wrong value when I read it from the component. The component from which I try to read the value is a sidebar which contains the navigation menu and the variable gets the correct value only when I click a menu item – Elias Jun 01 '18 at 11:25
  • I have added the working demo for asynchronous functions. You can have a look at that. And if your issue is resolved, mark the answer and close it. – Amit Chigadani Jun 01 '18 at 11:42
  • Thank you for your answers, I used the code you posted but still I don't have the results I need. I think will look for another workaround – Elias Jun 01 '18 at 12:34
  • Finally I got it work by calling the function that will get the user role in onNgInit() and use the variable in the html template. I added the code in the answer. – Elias Jun 04 '18 at 11:41
  • Nice that your problem is solved. Well if you had checked the demo link that I have added, It utilizes ngOnInit() as opposed to constructor. It is always a good practice to write starter code in ngOnInit(). – Amit Chigadani Jun 04 '18 at 11:50
  • I am a beginner in angular and I realized that after the issue I faced. Thanks again for your help – Elias Jun 04 '18 at 11:56
0

You cannot access the variable outside .subscribe() method, place it within the subscribe

 this.currUser.subscribe(val => {
    this.role = val[0].role;
    console.log(this.role);
});
Sajeetharan
  • 216,225
  • 63
  • 350
  • 396
0

I created the following function in the user service:

getUserRole(userId: string): Observable<any> {
    return this.db.list('users', ref => ref.orderByChild('uid').equalTo(userId)).valueChanges()
        .map(result => result[0]);
}

then I got the user id in the component:

get uid(): string {
    return this.authService.uid;
}
set uid(value: string) {
    this.authService.uid = value;
}

and called the function in onNgInit():

ngOnInit() {
    if (this.uid != undefined) {
        let uid = this.uid;

        this.userService.getUserRole(this.uid).subscribe(result => {
                    this.role = result.role;
        });
    }
}

Finally I used *ngIf="role == '<role>' in the html template

Elias
  • 347
  • 2
  • 5
  • 20