尝试在 vuejs 中更新 firebase 文档时,我无法解决此未定义字段问题

Posted

技术标签:

【中文标题】尝试在 vuejs 中更新 firebase 文档时,我无法解决此未定义字段问题【英文标题】:I cannot fix this undefined field issue when trying to update firebase document in vuejs 【发布时间】:2022-01-20 07:04:21 【问题描述】:

我正在尝试通过不断收到此错误来更新我的 firebase 文档'

FirebaseError:使用无效数据调用函数 DocumentReference.update()。不支持的字段值:未定义(在文档课程/1wY9RjpYtNeEDtuaeXmh 中的字段 id 中找到)

这是我的 GameForm vue 组件代码:

<template>
    <div class="game-form">
        <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 >
            <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>

这是我的仪表板页面

<template>
    <div class="dashboard">
        <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"
                        
                        :src="course.image"
                        lazy-src="https://via.placeholder.com/250"
                    >
                    </v-img>
                    <v-card-title> course.subject </v-card-title>
                    <v-card-text>
                        <p class="subtitle-1">tutor:  course.tutor </p>
                        <p class="subtitle-1">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-
        >
            <v-card>
                <v-card-title class="headline">
                    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
            
          >
          <!-- <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>

这是我的 store.js 文件:

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: 
  
)

当我签入开发工具时,userProfile、pId、PTitle 都是未定义的。

我是 vue 和 firebase 的新手,所以我被卡住了。请问可以指导我吗?

【问题讨论】:

【参考方案1】:

这是因为您传递给await coursesCollection.doc(this.course.id).update(data)data 对象中的属性之一是未定义的。

您应该调试您的代码并找出该对象的哪些属性未定义:

            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,
            

【讨论】:

感谢@Renaud。我一遍又一遍地检查,我想我在数据对象中定义了所有这些。我只是无法自己弄清楚。我已经编辑了我的问题以包含更多代码来进一步解释我的问题。再次感谢。 @AbarugoGoodness Renaud 几乎拥有它,id: this.id 是您未定义的值 - “在文档中的字段 id 中找到”。它应该是id: this.course.id,因为在您的setData() 方法中,您不会像使用其他属性那样从this.course 复制它。 哦,我再次检查,'id' 未定义。我如何得到它?有没有不同的方法来获取它,因为我成功检索了其他字段。再次感谢。 非常感谢,@samthecodingman,我将其更改为 >this.course.id,但未定义 @AbarugoGoodness 看起来您从未在文档数据中附加了 ID。您需要使用const docRef = coursesCollection.doc(); data.id = docRef.id; const doc = await docRef.set(data);,而不是const doc = await coursesCollection.add(data)

以上是关于尝试在 vuejs 中更新 firebase 文档时,我无法解决此未定义字段问题的主要内容,如果未能解决你的问题,请参考以下文章

Vuejs 和 Firebase 存储问题。未捕获的类型错误:存储不是函数

Vuejs 和 Firestore - 如何在 Firestore 中的数据更改时进行更新

带有 vuejs 的 Firebase 数据库:default.collection 不是函数

使用 VueJs 在 firebase 中上传和下载存储图像

VueJS - 如何从方法内的函数更新组件数据?

SwiftUI Firebase - 如何查询文档然后更新?