Working on a CRUD application, and I have gotten stuck at the UPDATE part.
I have three components and one composable.
The composable contains the update function, which I import to my components.
The components consists of a Home component, where I store all my documents from the database, a Detail component, where I output the data from a single document, and then I have the Edit component itself, which is used to update the data stored in the Detail component.
The strange thing is that I have no issues updating the data. It gets updated just fine, both in the application and on the server side.
But I am running into 3 problems:
1: I am getting the error “Cannot read property ‘id’ of null”
2: Since this error occured, I am suddenly seeing duplicates of the buttons inside the template.
3: Instead of redirecting me to the Home component after I update, the Home component appears INSIDE the Details component.
What is going on?
The “Cannot read property ‘id’ of null” error points to the Edit component at “props.playlist.id”
<template>
<div class="edit-song">
<form @submit.prevent="handleSubmit">
<input type="text" required placeholder="title" v-model="title">
<input type="text" required placeholder="description" v-model="description">
<button v-if="!isPending">Update</button>
<button v-else disabled>Updating...</button>
</form>
</div>
</template>
<script>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import useDocument from '../composables/useDocument'
import useCollection from '../composables/useCollection'
export default {
props: ['playlist'],
setup(props) {
const title = ref('')
const description = ref('')
const { updateDoc } = useDocument('playlists', props.playlist.id)
const { error } = useCollection()
const isPending = ref(false)
const router = useRouter();
const handleSubmit = async () => {
await updateDoc({
title: title.value,
description: description.value,
})
isPending.value = false
if(!error.value) {
router.push({ name: "Home" })
}
}
return {
title,
description,
handleSubmit,
isPending,
error
}
}
}
</script>
In case you need them, here is both the Details component and the composable used for updating.
Details: (PlaylistDetails.vue)
<template>
<div v-if="playlist" class="playlist-details">
<div class="playlist-info">
<div class="cover">
<img :src="playlist.coverUrl">
</div>
<h2> {{ playlist.title }}</h2>
<p> {{ playlist.description }} </p>
</div>
</div>
<button @click="handleDelete">Delete</button>
<EditSong :playlist="playlist" />
</template>
<script>
import EditSong from '../components/EditSong'
import useDocument from '../composables/useDocument'
import getDocument from '../composables/getDocument'
import useStorage from '../composables/useStorage'
import { useRouter } from "vue-router";
export default {
props: ['id'],
components: { EditSong },
setup(props) {
const { document: playlist } = getDocument('playlists', props.id)
const { deleteDoc } = useDocument('playlists', props.id)
const router = useRouter();
const { deleteImage } = useStorage()
const handleDelete = async () => {
await deleteImage(playlist.value.filePath)
await deleteDoc()
confirm('Do you wish to delete this content?')
router.push({ name: "Home" });
}
return {
playlist,
handleDelete
}
}
}
</script>
useDocument.js
import { ref } from 'vue'
import { projectFirestore } from '../firebase/config'
const useDocument = (collection, id) => {
const error = ref(null)
const isPending = ref(false)
let docRef = projectFirestore.collection(collection).doc(id)
const updateDoc = async (updates) => {
isPending.value = true
error.value = null
try {
const res = await docRef.update(updates)
isPending.value = false
return res
}catch(err) {
console.log(err.message)
isPending.value = false
error.value = 'Could not update document'
}
}
return {
error,
isPending,
updateDoc
}
}
export default useDocument