Home > Software design >  I cannot fix this undefined field issue when trying to update firebase document in vuejs
I cannot fix this undefined field issue when trying to update firebase document in vuejs

Time:12-20

I am trying to update my firebase document by keep getting this error'

FirebaseError: Function DocumentReference.update() called with invalid data. Unsupported field value: undefined (found in field id in document courses/1wY9RjpYtNeEDtuaeXmh)

This is my GameForm vue component code:

<template>
    <div >
        <v-btn @click="dialog = !dialog" color="primary" v-if="!course">Add new game</v-btn>
        <v-btn @click="dialog = !dialog; setData()" color="primary" v-else>Edit game</v-btn>

        <v-dialog v-model="dialog" persistent width="600px">
            <v-card>
                <v-card-title v-if="!course">Add new game</v-card-title>
                <v-card-title v-else>Edit game</v-card-title>
                <v-card-text>
                    <v-form
                        v-model="valid"
                        lazy-validation
                        ref="form"
                    >
                        <v-text-field
                            v-model="subject"
                            :rules="fieldRules"
                            label="Subject"
                            required
                            outline
                        >
                        </v-text-field>
                        
                        <v-textarea
                            v-model="description"
                            :rules="fieldRules"
                            label="Game developer"
                            outlined
                            required
                        >
                        </v-textarea>

                        <v-text-field
                            v-model="duration"
                            :rules="fieldRules"
                            label="Duration"
                            outlined
                            required
                        >
                        </v-text-field>

                        <v-textarea
                            v-model="payment"
                            :rules="fieldRules"
                            label="Payment"
                            outlined
                            required
                        >
                        </v-textarea>

                        <v-textarea
                            v-model="years_exp"
                            :rules="fieldRules"
                            label="Experience"
                            outlined
                            required
                        >
                        </v-textarea>

                        <v-textarea
                            v-model="tutor"
                            :rules="fieldRules"
                            label="Tutor"
                            outlined
                            required
                        >
                        </v-textarea>

                          <v-textarea
                            v-model="amount"
                            :rules="fieldRules"
                            label="Amount"
                            outlined
                            required
                        >
                        </v-textarea>

                        <!-- <v-textarea
                            v-model="years_exp"
                            :rules="fieldRules"
                            label="Game description"
                            outlined
                            required
                        >
                        </v-textarea> -->

                        <v-file-input
                            accept="image/*"
                            label="File input"
                            v-model="file"
                            show-size
                        >
                        </v-file-input>
                        <p>Current image: <a v-if="oldImage" :href="oldImage">link</a></p>

                        <v-btn
                            elevation="2"
                            color="primary"
                            @click="storeGame"
                            :loading="isLoading"
                            v-if="!course"
                        >
                            Store
                        </v-btn>
                        <v-btn
                            elevation="2"
                            color="primary"
                            @click="updateGame"
                            :loading="isLoading"
                            v-else
                        >
                            Update
                        </v-btn>
                    </v-form>
                </v-card-text>
                <v-card-actions>
                    <v-spacer></v-spacer>
                    <v-btn
                        color="blue darken-1"
                        text
                        @click="dialog = false"
                    >
                        Close
                    </v-btn>
                </v-card-actions>
            </v-card>
        </v-dialog>
    </div>
</template>

<script>
import { coursesCollection, auth, storage } from '../firebase'
export default {
    name: 'GameForm',
    props: ['course', 'index'],
    data() {
        return {
            isLoading: false,
            valid: false,
            dialog: false,
            subject: '',
            description: '',
            tutor: '',
            payment: 0,
            duration: '',
            amount: 0,
            years_exp: 0,
            file: null,
            fieldRules: [
                v => !!v || 'this field is required'
            ],
            oldImage: ''
        }
    },
    methods: {
        resetForm() {
            this.$refs.form.reset()
        },
        async storeGame() {
            try {
                this.isLoading = false
                // upload file
                const fileRef = 'uploads/courses/'   this.file.name
                const snapshot = await storage.ref(fileRef).put(this.file)
                let data = {
                    userId: auth.currentUser.uid,
                    subject: this.subject,
                    description: this.description,
                    tutor: this.tutor,
                    payment: this.payment,
                    duration: this.duration,
                    amount: this.amount,
                    years_exp: this.years_exp,
                    image: fileRef
                }
                const doc = await coursesCollection.add(data)
                this.$emit('course:added', data)
                await this.resetForm()
                this.isLoading = false
                this.dialog = false
            } catch(e) {
                console.log(e)
            }
        },
        async updateGame() {
            try {
                this.isLoading = false
                let data = {
                    id: this.id,
                    userId: auth.currentUser.uid,
                    subject: this.subject,
                    description: this.description,
                    tutor: this.tutor,
                    payment: this.payment,
                    duration: this.duration,
                    amount: this.amount,
                    years_exp: this.years_exp,
                }
                
                if(this.file) {
                    // delete oldImage
                    const fileRefOld = this.course.img
                    await storage.ref(fileRefOld).delete()
                    // upload file
                    const fileRef = 'uploads/courses/'   this.file.name
                    const snapshot = await storage.ref(fileRef).put(this.file)
                    data.image = fileRef
                } else {
                    data.image = this.course.image
                }
                const doc = await coursesCollection.doc(this.course.id).update(data)
                // await this.resetForm()
                this.isLoading = false
                this.dialog = false
                data.index = this.index
                // this.$emit('course:updated', data)
                alert('Game updated!')
            } catch(e) {
                // console.log(this.course.id);
                console.log(e)
            }
        },
        setData() {
            if(this.course) {
                this.subject =  this.course.subject,
                this.description =  this.course.description,
                this.tutor =  this.course.tutor,
                this.payment =  this.course.payment,
                this.duration =  this.course.duration,
                this.amount =  this.course.amount,
                this.years_exp =  this.course.years_exp,
                this.oldImage = this.course.image
            }
        }
    },
    mounted() {
        this.setData()
    }
}
</script>

This is my Dashboard Page

<template>
    <div >
        <h1>Hi, {{ userProfile.name }}</h1>
        <GameForm @course:added="addGame" />
        <v-row>
            <v-col md="4" v-for="(course, index) in courses" :key="course.id">
                <v-card>
                    <v-img
                        v-if="course.image"
                        height="250"
                        :src="course.image"
                        lazy-src="https://via.placeholder.com/250"
                    >
                    </v-img>
                    <v-card-title>{{ course.subject }}</v-card-title>
                    <v-card-text>
                        <p >tutor: {{ course.tutor }}</p>
                        <p >duration: {{ course.duration }}</p>
                        <p>{{ course.description }}</p>
                    </v-card-text>
                    <v-card-actions>
                        <GameForm :course="course" :index="index" @course:updated="updateGame" />
                        <v-btn color="red" @click="deleteConfirm(course.id, course.subject)" text>Delete</v-btn>
                    </v-card-actions>
                </v-card>
            </v-col>
        </v-row>

        <v-dialog
            v-model="deleteDialog"
            max-width="400"
        >
            <v-card>
                <v-card-title >
                    Delete Game?
                </v-card-title>
                <v-card-text>Are you sure you want to delete <b>{{ pTitle }}</b>?</v-card-text>
                <v-card-actions>
                    <v-btn text color="red" @click="deleteGame">Delete</v-btn>
                    <v-btn @click="deleteDialog = false" text color="secondary">Close</v-btn>
                </v-card-actions>
            </v-card>
        </v-dialog>
<br><br><br><br><br>
        <v-row>
        <v-layout>
      <v-flex xs12 sm6>
        <v-card hover>
          
          <v-responsive
            height="auto"
          >
          <!-- <v-img 
            src="https://raw.githubusercontent.com/ijklim/simon-game/gh-pages/assets/img/bg--game-pad.jpg"
          >

          </v-img> -->
          <Play />
          </v-responsive>

          <v-card-title>
            <h2>v-card-title</h2>
          </v-card-title>
          
          <v-card-text>
            line 1<br>
            line 2<br>
            line 3
          </v-card-text>
          
          <v-card-actions>
            <v-btn color=success>Click #1</v-btn>
            <v-btn text color=primary>Click #2</v-btn>
            <v-spacer></v-spacer>
            <v-btn icon><v-icon>b</v-icon></v-btn>
          </v-card-actions>
          
        </v-card>
      </v-flex>
    </v-layout>
    </v-row>
    </div>
</template>

<script>
import { mapState } from 'vuex'
import GameForm from '@/components/GameForm'
import Play from './Play'
import { auth, storage, coursesCollection } from '../firebase'
export default {
    components: {
        GameForm,
        Play
    },
    data() {
        return {
            courses: [],
            pId: null,
            pTitle: null,
            deleteDialog: false,
            userProfile: {}
        }
    },
    computed: {
        ...mapState(['userProfile'])
    },
    methods: {
        async addGame(doc) {
            let img = ''
            if(doc.image) {
                img = await storage.ref().child(doc.image).getDownloadURL()
            }
            this.courses.push({
                id: doc.id,
                subject: doc.subject,
                description: doc.description,
                tutor: doc.tutor,
                payment: doc.payment,
                duration: doc.duration,
                amount: doc.amount,
                years_exp: doc.years_exp,                
                image: img,
                img: doc.image
            })
        },
        async updateGame(doc) {
            let img = ''
            if(doc.image) {
                img = await storage.ref().child(doc.image).getDownloadURL()
            }
            if (doc.id) {
                console.log('u', doc.tutor),
                this.courses.splice(doc.index, 0, {
                id: doc.id,
                subject: doc.subject,
                description: doc.description,
                tutor: doc.tutor,
                payment: doc.payment,
                duration: doc.duration,
                amount: doc.amount,
                years_exp: doc.years_exp,                
                image: img,
                img: doc.image
            })
            }
        },
        async getGames() {
            try {
                const querySnapshot = await coursesCollection.where('userId', '==', auth.currentUser.uid).get()
                querySnapshot.forEach( async (doc) => {
                    let img = ''
                    if(doc.data().image) {
                        img = await storage.ref().child(doc.data().image).getDownloadURL()
                    }
                    this.courses.push({
                        id: doc.data().id,
                        subject: doc.data().subject,
                        description: doc.data().description,
                        tutor: doc.data().tutor,
                        payment: doc.data().payment,
                        duration: doc.data().duration,
                        amount: doc.data().amount,
                        years_exp: doc.data().years_exp,
                        image: img,
                        img: doc.data().image
                    })
                })
            } catch(e) {
                console.log(e)
            }
        },
        async deleteConfirm(id, subject) {
            this.deleteDialog = true
            this.pId = id
            console.log(this.pId)
            this.pTitle = subject
        },
        async deleteGame() {
            try {
                await coursesCollection.doc(this.pId).delete()
                alert('Course deleted!')
                // remove game from array
                this.courses.splice(this.courses.findIndex(x => x.id == this.pId), 1)
                this.deleteDialog = false
                // reset
                this.pId = null
                this.pTitle = null
            } catch(e) {
                console.log(e)
            }
        }
    },
    async mounted() {
        await this.getGames()
    }
}
</script>

This is my store.js file:

import Vue from 'vue'
import Vuex from 'vuex'
import * as fb from '../firebase'
import router from '../router/index'


Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    userProfile: {},
    isAuthenticated: false
  },
  mutations: {
    setUserProfile(state, val, authState) {
      state.isAuthenticated = !state.isAuthenticated
      state.userProfile = val
    }
  },
  actions: {
    async register({ dispatch}, form) {
      // sign up user
      const { user } = await fb.auth.createUserWithEmailAndPassword(form.email, form.password)

      // create user profile object
      await fb.usersCollection.doc(user.uid).set({
        name: form.name
      })

      // fetch user profile
      dispatch('fetchUserProfile', user)
    },
    async login({ dispatch }, form) {
      // sign in user
      const { user } = await fb.auth.signInWithEmailAndPassword(form.email, form.password)

      // fetch user profile
      dispatch('fetchUserProfile', user)
    },
    async fetchUserProfile({ commit }, user) {
      // fetch user profile
      const userProfile = await fb.usersCollection.doc(user.uid).get()

      // set user profile
      commit('setUserProfile', userProfile.data())

      // change route or redirect
      router.push('/dashboard')
    },
    async logout({ commit }) {
      await fb.auth.signOut()
      commit('setUserProfile', {})
      router.push('/')
    }
  },
  modules: {
  }
})

When i check in dev tools, userProfile, pId, PTitle are all undefined.

I am new to vue and firebase, so I am stuck. Could you direct me please?

CodePudding user response:

This is because one of the properties in the data object you pass to await coursesCollection.doc(this.course.id).update(data) is undefined.

You should debug your code and find which property(ies) of this object is(are) undefined:

            let data = {
                id: this.id,
                userId: auth.currentUser.uid,
                subject: this.subject,
                description: this.description,
                tutor: this.tutor,
                payment: this.payment,
                duration: this.duration,
                amount: this.amount,
                years_exp: this.years_exp,
            }
  • Related