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,
}