0
votes

My code for accessing a document from firestore is as below.

let data;
this.firestore.collection('groups').doc(tempId).ref.get().then(function(doc) {
  if (doc.exists) {
      data = doc.data();
      console.log("Document data:", doc.data());   // Gives correct data here
  } else {
      console.log("No such document!");
  }
}).catch(function(error) {
    console.log("Error getting document:", error);
});
console.log("Service Data :: " + data); //It says undefined here.

Here, I want to return the data of doc.data() to another component. But, In the console.log("Service Data :: " + data); it says undefined.

So, I'm confused like why data variable does not has the value of doc.data() in it.

2
Ok. Sorry. Took back my aswer. I was unable to test my code today, so sorry. Will get back with a tested solution, but it will rely on valueChanges() and observables rather than Promises, if that's ok.thomi
I'm fine with it as far as code worksVirus

2 Answers

1
votes

this is because second console is executing before first console, the thread will make the request to your firestore and then it will not wait for the response as well as it will executes other lines of code. So your second console is executing before the first console.

let data;
this.firestore.collection('groups').doc(tempId).ref.get().then(function(doc) {
  if (doc.exists) {
      data = doc.data();
      console.log("Document data:", doc.data());   // first console
  } else {
      console.log("No such document!");
  }
}).catch(function(error) {
    console.log("Error getting document:", error);
});
console.log("Service Data :: " + data); //second console

if you want to change this behavior call your second console next to the first one.

If you want pass the data to other component use BehaviourSubject

  public  dataSource = new BehaviorSubject<any>([]);

  this.dataSource.next(doc.data()); 
  console.log("Document data:", doc.data());   // first console

pass this dataSource object to your other component with help of service class and subscribe it as regular Observable. follow this link for more info about angular-behaviorsubject-service

second Component.ts

constructor(private service: Commonservice) { }

someMethod() {
  this.service.dataSource.subscribe((response: any) =>{
    // do something with data
  })
}
1
votes

the .get() method returns a promise, which is executed asynchronously once you call .then(). Because of this, the next line that gets executed is console.log("Service Data :: " + data);. Javascript does not wait for the promise to be resolved and instead just carries on with the next synchronous line which is the second console.

The way I usually go about this is passing the whole promise to the other component or better yet, I use the .valueChanges() of the .doc() which returns an observable, and use the async pipe in the component I'm passing to:

// Get Observable on document. Returns Observable<any>
const group$ = this.firestore.doc('/groups' + tempId).valueChanges();

You then have two options:

  1. Use group$.subscribe();
  2. Pass group$to the component you want and use the async pipe there

First option:

// In your component:
let data;
group$.subscribe(doc => {
  if (doc.exists) {
    data = doc
    console.log("Document data:", doc);   // No need to call data().
  } else {
    console.log("No such document!");
  },
  error => console.log("Error getting document:", error);
)

Second option, passing into the component where you would like the observable to be evaluated and the data displayed:

<div *ngIf="group$ | async as doc">
  Your html here {{ doc.someProperty }} further html here...
</div>

Personally, I prefer the second choice because it goes along with the framework nicely and keeps me from making asynchronous errors.

Have a look at angularfire2's Github repo for docs here. If there is no need to evaluate the observable by hand in code, I would not do this and let the framework handle it.

One final thing: If you use the observable and want to do some error handling when using the async pipe, you probably want to do so when creating the observable:

// Get Observable on document. Returns Observable<any>
// In error case, returns Observable of the error string
const group$ = this.firestore.doc('/groups' + tempId).valueChanges()
  .pipe(
    catchError(error => of(`Error getting document: ${error}`))
  );