0

I am using Angular 8.

I have created a function called getUserInfo() in a service class which returns informations about the user using the cloud service Firestore.

The problem is that when I show the result in the service class, it is defined and works perfectly, but inside the component, the result is undefined.

I think I know what the error is: when I call the function inside the component, it is returning undefined because the function takes some time to retrieve the data from the database, but I don't know how to fix it or what to use instead?

Does RxJS fix it?

I already tried router Resolver but it didn't work either.

This is the function i use in service class

getUserInfo() {
    this.db.collection('users').doc(`${this.cookie.get("id")}`).ref.get().then(doc => {
    return doc.data()
})}

The following is the component where I would like to use the service class function:

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth/auth.service';

@Component({
    selector: 'app-super-admin',
    templateUrl: './super-admin.component.html',
    styleUrls: ['./super-admin.component.css']
})
export class SuperAdminComponent implements OnInit {
    user;
    constructor(private authService: AuthService) { }

    ngOnInit() {
        this.user = this.authService.getUserInfo();
    }
}
Ferie
  • 1,358
  • 21
  • 36
Shakar Bakr
  • 57
  • 1
  • 10

4 Answers4

1

The better way is to use Rxjs just because it is commonly used with angular so I would definitely recommend that. However if you are looking for solutions without it you can always use Promises.
So the problem you are facing is that the code is being executed before the data is received from the server. You can remedy that by promisifying your call to firestore inside the service and returning a Promise, then you can invoke the Promise inside your component.
So the code becomes: The service Class

getUserInfo() {
let promise =  new Promise( (resolve,reject) => {
    this.db.collection('users').doc(`${this.cookie.get("id")}`).ref.get().then(doc => {
      resolve(doc.data());
    }).catch(er => {
      reject(er);
    })
}
return promise;
);

Then in the component ts file you can do:

   ngOnInit() {
        this.authService.getUserInfo().then(data => {
          this.user = data;
        });
    }

Hope this helps!

0

The short answer is: yes, RxJS is solving your issue.

As you suspect, the answer from the server takes some time to return something to your application.

Your component needs to subscribe to this asynchronous answer and only perform the action required when the response is received.

So, you need to get rid of the Promise in your service, and just return the call to the Firebase stuff

getUserInfo() {
    return this.db.collection('users').doc(`${this.cookie.get("id")}`).ref.get();
}

While, in your component, you need to do something like that:

ngOnInit() {
    this.authService.getUserInfo().subscribe(response => {
        /* Use `response` as the answer from the server and do whatever you need to do */
        console.log(response); // Use this to inspect the response from the server
    });
}
Ferie
  • 1,358
  • 21
  • 36
  • it gives me erorr: property subscribe does not exist on type void :( – Shakar Bakr Sep 26 '19 at 16:43
  • Your service must `return` something, so check the code. This error is happening if you are not returning anything from it. – Ferie Sep 26 '19 at 16:48
  • i did that before but this console is just for that to show me the data inside the service so even when i return the data it gives me the error i said . that console is just for testing to see if it has data in it or not – Shakar Bakr Sep 26 '19 at 16:56
  • 1
    Please provide a Stackblitz example, so we check your code https://stackblitz.com – Ferie Sep 26 '19 at 16:58
  • @Ferie this code would only work if ``this.db.collection('users').doc(`${this.cookie.get("id")}`).ref.get(); `` returns an observable which it might not be doing. Since it isn't a call with HttpClient module of angular. – Muhammad Hamza Shujaat Sep 29 '19 at 03:01
0
this.user = this.authService.getUserInfo();

Yes, you are right. At the point, authService.getUserInfo() is not resolved yet.(Also, there is no return statement in your getUserInfo() method).

I can think of 2 ways to solve this.

this.authService.getUserInfo().then(u => this.user = u);

The RxJS way:

getUserInfo() {
 return from(this.db.collection('users').doc(`${this.cookie.get("id")}`).ref.get());
}

/* ... */

this.authService.getUserInfo()
 .subscribe(u => this.user = u);

Here is more on how to convert a promise into an observable.

Andrei Gătej
  • 11,116
  • 1
  • 14
  • 31
  • none of them fixed it! the first way gives me error when i add then to this.authService.getUserInfo() and says property then does not exist on type void and for the second one i tried your code it says the same thing. by the way i don't know a thnig about RxJS :( so i would appreciate any solution without it – Shakar Bakr Sep 26 '19 at 16:59
0

I recreated your sceanario in this stackblitz, this is working fine

util.service.ts

import { Injectable } from '@angular/core';

@Injectable()
export class UtilService {

  constructor() { }

  getUserInfo() {
    return new Promise((res,rej) => {
      this.getDelayedData((data) => {
        res(data)
      });
    });
  }

  getDelayedData(call){
   setTimeout(()=> {
     call('here your data');
    }, 1500)
  }
}

app.component.ts

import { Component } from '@angular/core';
import { UtilService } from './util.service';
@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: [ './app.component.css' ]
})
export class AppComponent  {
  name = 'Angular';
  constructor(public util: UtilService) {
    this.util.getUserInfo().then((data : string) => {
      this.name = data;
      console.log(data)
    })
  }
}
Raghul Selvam
  • 314
  • 2
  • 6