Use classes as value objects #1257
-
Hi, I'm trying to find out if it's possible to use simple classes as VO's with VueFire and Pinia. A quick simplified example would be: Pinia Store export const useZonesStore = defineStore('zones', () => {
const db = useFirestore();
const items: Ref<Zone[]> = ref([]);
useCollection(collection(db, 'zones').withConverter(ZoneFirestoreConverter), { target: items });
return {
items,
}
}); Converter export const ZoneFirestoreConverter = {
toFirestore(zone: Zone): DocumentData {
return zone.data;
},
fromFirestore(snapshot: QueryDocumentSnapshot, options: SnapshotOptions): Zone {
const item = new Zone(snapshot.data(options) as ZoneData);
return item;
},
}; Value Object export class Zone {
readonly _data: ZoneData;
constructor(_data: ZoneData) {
this._data = _data;
}
get title(): string {
return `${this._data.index}. ${this._data.title}`;
}
get slug(): string {
return slugify(this.type);
}
get link(): string {
return `/content/zones/${this.slug}`;
}
get data(): ZoneData {
return this._data;
}
} In this setup, the data from the store in 👍🏻 thanks! |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 5 replies
-
Does the same problem happen without Pinia? Maybe doing One difference from the default converter is it adds an |
Beta Was this translation helpful? Give feedback.
-
Using it directly in a components setup has the same result. I've made a simple version in your playground, maybe you can find out where its lost in translation. Cant access <script setup lang="ts">
import {
addDoc,
collection,
deleteDoc,
doc,
serverTimestamp,
updateDoc,
} from 'firebase/firestore'
import { ref } from 'vue'
import { useCollection, useFirestore } from 'vuefire'
interface TodoData {
created: Date
finished: boolean
text: string
}
class Todo {
readonly id:string;
readonly data:TodoData;
constructor(id:string, data:TodoData){
this.id = id;
this.data = data;
}
get text():string {
return this.data.text;
}
get isDone():boolean {
return this.data.finished === true;
}
toFirestore():TodoData {
return this.data;
}
}
const db = useFirestore()
const todosWithConverterRef = collection(db, 'todos').withConverter<Todo>({
toFirestore(todoModel:Todo) {
return todoModel.toFirestore()
},
fromFirestore(snapshot, options) {
const todoData = snapshot.data(options) as Omit<TodoData, 'id'>
return markRaw(new Todo(snapshot.id, todoData))
},
})
const todos = useCollection<Todo>(todosWithConverterRef)
const todosRef = collection(db, 'todos')
const newTodoText = ref('')
function addTodo() {
if (newTodoText.value) {
addDoc(todosRef, {
text: newTodoText.value,
finished: false,
created: serverTimestamp(),
})
newTodoText.value = ''
}
}
function updateTodoText(todo: Todo, newText: string) {
updateDoc(doc(db, 'todos', todo.id), {
text: newText,
})
}
function removeTodo(todo: Todo) {
deleteDoc(doc(db, 'todos', todo.id))
}
</script>
<template>
<form @submit.prevent="addTodo">
<input v-model.trim="newTodoText" placeholder="Add new todo" />
<button>Add Todo</button>
</form>
<ul>
<li v-for="todo in todos">
<input
:value="todo.text"
@input="updateTodoText(todo, $event.target.value)"
/>
<button @click="removeTodo(todo)">X</button>
</li>
</ul>
</template>
|
Beta Was this translation helpful? Give feedback.
Does the same problem happen without Pinia? Maybe doing
markRaw(new Zone(...))
helps (imported from vue)One difference from the default converter is it adds an
id
property usingsnapshot.id
but you shouldn't need to keep this behavior with firestore 🤔