3

In my vue app, I am fetching some data from firebase in the created-hook. I want the code afterwards to be exectuted after the data was finished with loading. But I can not make it work.

Here is my code:

<template>
    <div>
        <h1>Test 2</h1>
    </div>
</template>

<script>
import { institutesCollection } from '../firebase';
export default {
    name: 'Test2',
    data() {
        return {
            settings: null,
        }
    },
    async created() {
        await institutesCollection.doc('FbdYJ5FzQ0QVcQEk1KOU').onSnapshot(
            await function(doc) {
                this.settings = doc.data();
                console.log(this.settings);
            }
        )
        console.log('Done with fetching Data');
        console.log(this.settings)
    },
    methods: {
        
    }
}
</script>

But one the console, first 'Done with fetching Data' is displayed, then null (because this.settings is still null) and only after that the Objekt settings is printed. But I need this.settings to be defined there (and not null anymore) to work with there.

What I tried so far:

  1. Putting in awaits
  2. Adding a loaded parameter
  3. Adding the code afterwards in a then()

Nothing worked.

Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
Sumny
  • 95
  • 5

2 Answers2

3

The onSnapshot() method is not an asynchronous method. As explained in the doc, it "attaches a listener for DocumentSnapshot events".

So, you should use it if you want to set a continuous listener to the Firestore document: if something changes in the document (e.g. a field gets a new value) the listener will be triggered. Note that "an initial call using the callback you provide creates a document snapshot immediately with the current contents of the single document" (see the doc).

You get the Firestore document data only in the callback function that you pass to onSnapshot(), as follows:

created() {
    institutesCollection.doc('FbdYJ5FzQ0QVcQEk1KOU').onSnapshot(doc => {
           this.settings = doc.data();
           console.log(this.settings);
      });
}

As Nilesh Patel mentioned, note that you need to use an arrow function in order to use this in this callback. Also note that the created function does NOT need to be async.

Finally, note that it is a good practice to call the unsubscribe function returned by onSnapshot() when you destroy the Vue.js component. Use the beforeDestroy hook as follows:

// ...
data() {
    return {
        settings: null,
        firestoreListener: null
    }
},
created() {
    this.firestoreListener  = institutesCollection.doc('FbdYJ5FzQ0QVcQEk1KOU').onSnapshot(doc => {
           this.settings = doc.data();
           console.log(this.settings);
      });
},
beforeDestroy() {
  if (this.firestoreListener) {
    this.firestoreListener();
  }
},

On the other hand, if you just want to read the document data ONLY ONCE in the created Vue.js hook, you should use the get() method, as follows:

async created() {
    const doc = await institutesCollection.doc('FbdYJ5FzQ0QVcQEk1KOU').get();
    if (doc.exists) {
             this.settings = doc.data();
    } else {
             // doc.data() will be undefined in this case
             console.log("No such document!");
    }
}

Note that, in this case, the created function NEEDS to be async, since get() is asynchronous.


More details on the difference between get() and onSnapshot() in this SO answer.

Renaud Tarnec
  • 79,263
  • 10
  • 95
  • 121
1

try using arrow function

  async created() {
        await institutesCollection.doc('FbdYJ5FzQ0QVcQEk1KOU').onSnapshot(
            doc => {
                this.settings = doc.data();
                console.log(this.settings);
            }
        )
        console.log('Done with fetching Data');
        console.log(this.settings)
  },
Nilesh Patel
  • 3,193
  • 6
  • 12