Fetching data from firebase before mounted() on Vue

I am trying to fetch data from firebase before mounted() hook is called, but firebase query is asynchronous function and I’m struggling to handle those. Here is my code snippet:

<template>
  <b-table-simple sticky-header="90%" class="table-bordered">
    <b-thead head-variant="dark">
      <b-tr>
        <b-th >氏名</b-th>
        <b-th v-for="date in printables" :key="date" class="text-center" v-html="date"></b-th>
      </b-tr>
    </b-thead>
    <b-tbody v-if="fetched">
      <template v-for="(staff_data, staff_id) in project.assigned_staff">
        <b-tr :key="staff_id">
          <b-th rowspan="6">
            <b-tr>
              <b-th rowspan="6" v-html="staff_data.name.split(/ | /).join('<br>')"></b-th>
              <b-th >[]</b-th>
            </b-tr>
            <b-tr>
              <b-th >出勤</b-th>
            </b-tr>
            <b-tr>
              <b-th >残業</b-th>
            </b-tr>
            <b-tr>
              <b-th >深夜</b-th>
            </b-tr>
            <b-tr>
              <b-th >内容</b-th>
            </b-tr>
            <b-tr>
              <b-th >弁当</b-th>
            </b-tr>
          </b-th>
          <b-td v-for="i in days.length" :key="i">
            <b-form-checkbox @change.native="setBGColor($event)" :ref="`select-${i}`"></b-form-checkbox>
          </b-td>
        </b-tr>
        <b-tr :key="staff_id">
          <b-td v-for="day in days" :key="day">
            <b-form-select @change.native="setBGColor($event)" :ref="`regular-${staff_id}-${day}`" :options="regularTimeOption"></b-form-select>
          </b-td>
        </b-tr>
        <b-tr :key="staff_id">
          <b-td v-for="day in days" :key="day">
            <b-form-select @change.native="setBGColor($event)" :ref="`overtime-${staff_id}-${day}`" :options="overTimeAndLateNightOption"></b-form-select>
          </b-td>
        </b-tr>
        <b-tr :key="staff_id">
          <b-td v-for="day in days" :key="day">
            <b-form-select @change.native="setBGColor($event)" :ref="`latenight-${staff_id}-${day}`" :options="overTimeAndLateNightOption"></b-form-select>
          </b-td>
        </b-tr>
        <b-tr :key="staff_id">
          <b-td v-for="day in days" :key="day">
            <b-form-select @change.native="setBGColor($event)" :ref="`type-${staff_id}-${day}`" :options="typeOption"></b-form-select>
          </b-td>
        </b-tr>
        <b-tr :key="staff_id">
          <b-td v-for="day in days" :key="day">
            <b-form-select @change.native="setBGColor($event)" :ref="`bento-${staff_id}-${day}`" :options="bentoOption"></b-form-select>
          </b-td>
        </b-tr>
      </template>
    </b-tbody>
  </b-table-simple>
<template>
//... 

const project

export default {
 beforeCreate() {
  // fetching "project" which is necessary to determine table structure (laying out <b-tr>, <b-th> and <b-td>)
  // this should be done before "mounted()" hook
  firebase.database().ref(`/project/${projectId}`)
                     .once('value')
                     .then(...)
  // fetching "daily_report" which is data to fill each <b-td>
  // this should be executed after "project" is fetched
  firebase.database().ref(`/daily_report/${projectId}`)
                     .once('value')
                     .then(snapShot => {
                       project = snapShot.val()                 
                       ...
                     })
 },
 mounted() {
   const elRegular = this.$refs[`regular-${postfix}`]
   elRegular.value = report.hours_regular
 },
 //...
}

Desireble order for me is:

  • beforeCreate() initiated
  • fetching project initiated
  • fetching project completed
  • fetching daily_report initiated
  • fetching daily_report completed
  • beforeCreate() finished
  • All the , and are rendered in between created() and mounted()
  • mounted() initiated
  • All the are filled with data from daily_report
  • mounted() completed

But because firebase.database().on() and once() are asynchronous functions, actual order will be:

  • beforeCreate() initiated
  • fetching project initiated
  • fetching daily_report initiated
  • beforeCreate() finished
  • mounted() initiated
  • mounted() completed
  • fetching project completed
  • fetching daily_report completed

The biggest problem here I think is fetching project completed after mounted() initiated. I searched about async/await and beforeRouteEnter guard of vue router, but none of them seems to solve my problem. How can I get promise-based functions done before mounted hook initiated?

Answer

Instead of using an external variable, you should add it to the data section of you component. This way, Vue can use it after the async call to Firebase.

export default {
 data() {
  project: {
   // An empty array so Vue start with no lines,
   // and add them once the array is populated.
   assigned_staff: []
 },
 beforeCreate() {
  // fetching "project" which is necessary to determine table structure
  // (laying out <b-tr>, <b-th> and <b-td>)
  // this should be done before "mounted()" hook
  firebase.database().ref(`/project/${projectId}`)
                     .once('value')
                     .then(...)
  // fetching "daily_report" which is data to fill each <b-td>
  // this should be executed after "project" is fetched
  firebase.database().ref(`/daily_report/${projectId}`)
                     .once('value')
                     .then(snapShot => {

                       // Set the property 'project' of the commponent
                       // to the value of the snapshot
                       this.$set(this, 'project', snapShot.val());

                       ...
                     })
 },
 mounted() {
   const elRegular = this.$refs[`regular-${postfix}`]
   elRegular.value = report.hours_regular
 },
 ...
}