2

I'm not understanding something about getting firebase data into vue... I'm happy to delete the question or quit my job as a programmer once I get an answer ;)

I get that the data is async, so vue is trying to render the component before the data has been received. But, I don't see how to make it wait. Examples would be greatly appreciated. I've tried reading the docs, but I'm just not understanding.

It's a tiny tiny app that displays our company's parking lot and saves the user's tap (car) location to firebase (we're tired of forgetting where we parked). Grateful for any help!

Oh, and I'm using set() because only one car location needs to be saved at a time, so it's ok that the data is overwritten each time the user taps a new place on the screen.

<template>
  <div id="app">
    <span id="marker" class="fas fa-times" :style="{ left: leftCoord, top: topCoord }"></span>
    <img @click="saveCoordinates($event)" src="./assets/parking-lot.jpg">
  </div>
</template>

<script>
import firebase from 'firebase'

const config = {
  apiKey: 'MY-API-KEY',
  authDomain: 'MY-APP.firebaseapp.com',
  databaseURL: 'https://MY-APP.firebaseio.com',
  projectId: 'MY-APP',
  storageBucket: 'MY-APP.appspot.com',
  messagingSenderId: '1234567890'
}
const app = firebase.initializeApp(config)

const db = app.database()
const locationRef = db.ref('location')

export default {
  name: 'app',
  firebase: {
    location: locationRef
  },
  mounted () {
    this.leftCoord = this.location.leftCoord
    this.topCoord = this.location.topCoord
  },
  data () {
    return {
      leftCoord: '',
      topCoord: ''
    }
  },
  methods: {
    saveCoordinates: function (clickEvent) {
      const coordinates = {
        leftCoord: clickEvent.layerX,
        topCoord: clickEvent.layerY
      }
      this.location.set(coordinates)
    }
  }
}
</script>
9
  • 1
    There is something I don't fully understand: you say that "vue is trying to render the component before the data has been received" but in the code you shared there is nowhere a call to read the database, just a button that fires a write. Could you clarify? What is the exact problem? Commented May 15, 2018 at 14:34
  • In the mounted hook, I'm trying to set the left and top coordinates for the marker, using the reference from the firebase db, and then apply those changes with the style binding in the marker in the template. The initial error I'm receiving is that .set() is not a function. Commented May 15, 2018 at 14:39
  • 1
    Ok, I see, thanks for pointing that! In the mounted hook what do you get if you do console.log(this.location) ? Commented May 15, 2018 at 14:45
  • 1
    And what if you do this.leftCoord = locationRef.leftCoord Commented May 15, 2018 at 14:46
  • 1
    Hold on, I am looking for a solution Commented May 15, 2018 at 15:07

3 Answers 3

2

This will do the trick:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">

    <script src="https://www.gstatic.com/firebasejs/4.11.0/firebase.js"></script>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vuefire/dist/vuefire.js"></script>
</head>
<body>


<div id="app">

    <span id="marker" class="fas fa-times" :style="{ left: leftCoord, top: topCoord }"></span>
    <img @click="saveCoordinates($event)" src="./assets/parking-lot.jpg">

    <img @click="readCoords" src="./assets/parking-lot.jpg">

</div>

<script>

    var config = {
        apiKey: ".........",
        authDomain: ".........",
        databaseURL: ".........",
        projectId: ".........",
        storageBucket: "........."
    };


    /* global Vue, firebase */
    var db = firebase.initializeApp(config).database()
    var todosRef = db.ref('todos')

    const locationRef = db.ref('location')

    new Vue({
        el: '#app',
        name: 'app',
        firebase: {
            location: {
                source: locationRef,
                asObject: true
            }

        },
        mounted() {

            locationRef.once('value', snapshot => {
                this.leftCoord = this.location.leftCoord
                this.topCoord = this.location.topCoord
            })

        },
        data() {
            return {
                leftCoord: '',
                topCoord: ''
            }
        },
        methods: {
            saveCoordinates: function (clickEvent) {
                console.log(clickEvent.layerX)
                const coordinates = {
                    leftCoord: clickEvent.layerX,
                    topCoord: clickEvent.layerY
                }
                locationRef.set(coordinates)
            },

            readCoords: function (clickEvent) {
                console.log(this.location.leftCoord);
                console.log(this.location.topCoord);
            }
        }
    })


</script>
</body>
</html>

Apparently there is no "native" way in vuefire to wait into a mounted hook that the data is loaded, see https://github.com/vuejs/vuefire/issues/69

I've added a second image link with a new function readCoords just for testing purpose.


Note that you could also do in the mounted hook

locationRef.once('value', snapshot => {
    this.leftCoord = snapshot.val().leftCoord
    this.topCoord = snapshot.val().topCoord
})
Sign up to request clarification or add additional context in comments.

2 Comments

Perfect! Thanks for the help!
You're welcome (I've learnt something too :-), which makes SO so good)
1

I add a second answer, which is not based anymore on the mounted hook but on the callback function of the object binding.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">

    <script src="https://www.gstatic.com/firebasejs/4.11.0/firebase.js"></script>
    <script src="https://unpkg.com/vue/dist/vue.js"></script>
    <script src="https://unpkg.com/vuefire/dist/vuefire.js"></script>
</head>
<body>


<div id="app">

    <span id="marker" class="fas fa-times" :style="{ left: leftCoord, top: topCoord }"></span>
    <img @click="saveCoordinates($event)" src="./assets/parking-lot.jpg">

</div>

<script>

    var config = {
        apiKey: ".........",
        authDomain: ".........",
        databaseURL: ".........",
        projectId: ".........",
        storageBucket: "........."
    };


    /* global Vue, firebase */
    var db = firebase.initializeApp(config).database()
    var todosRef = db.ref('todos')

    const locationRef = db.ref('location')

    new Vue({
        el: '#app',
        name: 'app',
        firebase: {
            location: {
                source: locationRef,
                asObject: true,
                readyCallback: function () {
                    this.leftCoord = this.location.leftCoord
                    this.topCoord = this.location.topCoord
                }
            }

        },
        data() {
            return {
                leftCoord: '',
                topCoord: ''
            }
        },
        methods: {
            saveCoordinates: function (clickEvent) {
                console.log(clickEvent.layerX)
                const coordinates = {
                    leftCoord: clickEvent.layerX,
                    topCoord: clickEvent.layerY
                }
                locationRef.set(coordinates)
            }
        }
    })


</script>
</body>
</html>

1 Comment

That's really fantastic. Thank you for your help!
1

If location is an object, you need to bind like this:

export default {
  name: 'app',
  firebase: {
    location: {
      source: db.ref('location'),
      asObject: true,
      readyCallback: function () {}
    }
  },
  mounted () {
    this.leftCoord = this.location.leftCoord
    this.topCoord = this.location.topCoord
  },
  data () {
    return {
      leftCoord: '',
      topCoord: ''
    }
  },
  methods: {
    saveCoordinates: function (clickEvent) {
      const coordinates = {
        leftCoord: clickEvent.layerX,
        topCoord: clickEvent.layerY
      }
      this.$firebaseRefs.location.set(coordinates)
    }
  }
}

Note that when setting, we need to use this.$firebaseRefs.location.set(coordinates)

Document https://github.com/vuejs/vuefire

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.