Load Firestore data in components before rendering with Vuefire
P粉790187507
2023-08-29 15:05:47
<p>I'm trying to populate a people table with names and profile pictures. The name is derived from the Firestore database and is used by the image to find related images in the Firebase bucket. </p>
<p>I have been watching hours of videos and have gone through almost a hundred articles so my example code contains snippets of each code as I have been trying every combination and getting a mix but not Successful results. </p>
<p>In the current state which returns minimal errors, I am able to successfully populate the table with the names, however within the same component it fails to extract the profile picture. The value used for the profile picture was updated, but it was updated from a placeholder value to <code>undefined</code>. </p>
<p><strong>GamePlanner.vue</strong></p>
<pre class="brush:vue;toolbar:false;"><template>
<div>
<Field />
<Bench />
<!-- <Suspense>
<template #default> -->
<PlanSummary />
<!-- </template>
<template #fallback>
<div class="loading">Loading...</div>
</template>
</Suspense> -->
</div>
</template>
</pre>
<p><strong>PlanSummary.vue</strong></p>
<pre class="brush:vue;toolbar:false;"><template>
<div class="summaryContainer">
<div class="inningTableContainer">
<table class="inningTable">
<tbody>
<!-- <Suspense> -->
<!-- <template #default> -->
<PlayerRow v-for="player in players.value" :key="player.id" :player="(player as Player)" />
<!-- </template> -->
<!-- <template #fallback>
<tr data-playerid="0">
<img class="playerImage" src="https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50" />
Loading players...
<span class="playerNumber">00</span>
</tr>
</template> -->
<!-- </Suspense> -->
</tbody>
</table>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, onErrorCaptured, ref } from "vue";
import { useFirestore, useCollection } from "vuefire";
import { collection } from "firebase/firestore";
import { Player, Inning } from "@/definitions/GamePlanner";
import PlayerRow from "./PlayerRow.vue";
const db = useFirestore();
const gamePlanID = "O278vlB9Xx39vkZvIsdP";
// const players = useCollection(collection(db, `/gameplans/${gamePlanID}/participants`));
// const players = ref(useCollection(collection(db, `/gameplans/${gamePlanID}/participants`)));
// Unhandled error during execution of scheduler flush
// Uncaught (in promise) DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.
const players = ref();
players.value = useCollection(collection(db, `/gameplans/${gamePlanID}/participants`));
// Seeminly infinite loop with "onServerPrefetch is called when there is no active component instance to be associated with."
// Renders 5 (??) undefined players
// One error shown: Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'substring')
// const players = computed(() => useCollection(collection(db, `/gameplans/${gamePlanID}/participants`)));
// onErrorCaptured((error, vm, info) => {
// console.log("Error loading Summary component: ", error, "vm: ", vm, "info: ", info);
// throw error;
// });
</script>
</pre>
<p><strong>PlayerRow.vue</strong></p>
<pre class="brush:vue;toolbar:false;"><template>
<tr :key="player2.id" :data-playerid="player2.id">
<td>
<img class="playerImage" :src="playerPictureURL" />
{{ player2.nickname || player2.firstName " " player2.lastName }}
<span class="playerNumber">{{ player2.playerNumber }}</span>
</td>
</tr>
</template>
<script lang="ts" setup>
import { ref, PropType, computed, onMounted, watch } from "vue";
import { useFirebaseStorage, useStorageFileUrl } from "vuefire";
import { ref as storageRef } from 'firebase/storage';
import { Player, Inning } from "@/definitions/GamePlanner";
const fs = useFirebaseStorage();
const props = defineProps({
'player': { type: Object as PropType<Player>, required: true },
// 'innings': Array<Inning>
});
const player2 = ref(props.player);
// const innings = computed(() => props.innings);
// const playerPictureURL = computed(() => {
// const playerPictureFilename = `${player2.value.firstName.substring(0,1)}${player2.value.lastName}.png`.toLowerCase();
// const playerPictureResource = storageRef(fs, `playerPictures/${playerPictureFilename}`);
// return useStorageFileUrl(playerPictureResource).url.value as string;
// });
// const playerPictureURL = ref(() => {
// const playerPictureFilename = `${player2.value.firstName.substring(0,1)}${player2.value.lastName}.png`.toLowerCase();
// const playerPictureResource = storageRef(fs, `playerPictures/${playerPictureFilename}`);
// return useStorageFileUrl(playerPictureResource).url.value as string;
// });
const playerPictureURL = ref("https://www.gravatar.com/avatar/205e460b479e2e5b48aec07710c08d50");
async function getPlayerPictureURL() {
console.log("PlayerRow.ts getPlayerPictureURL");
const playerPictureFilename = `${player2.value.firstName.substring(0,1)}${player2.value.lastName}.png`.toLowerCase();
const playerPictureResource = await storageRef(fs, `playerPictures/${playerPictureFilename}`);
playerPictureURL.value = await useStorageFileUrl(playerPictureResource).url.value as string;
}
onMounted(() => {
console.log("PlayerRow.ts onMounted");
getPlayerPictureURL();
});
watch(playerPictureURL, (newVal, oldVal) => {
console.log("PlayerRow.ts watch playerPictureURL");
console.log("newVal: " newVal);
console.log("oldVal: " oldVal);
});
</script>
</pre>
<p>我的印象是 <code><Suspense></code> 需要包装 <code><PlayerRow></code> 组件,因为我正在使用 <code>storageRef</code> 和 < code>useStorageUrl</code> 方法,但它似乎引入了更多问题。根据 vuefire 文档并检查代码本身的定义,它们似乎并不是异步的,但是尝试立即调用它们不会产生立即/实际结果。</p>
<p><strong>相关软件包版本</strong></p>
<pre class="brush:json;toolbar:false;">{
"vue": "^3.2.45"
"firebase": "^9.15.0",
"typescript": "^4.9.3",
"vite": "^4.0.0",
"vue-router": "^4.1.6",
"vue-tsc": "^1.0.11",
"vuefire": "3.0.0-beta.6"
}
</pre></p>
According to this documentation we should use
useCollection
containingcollection
as parameter like this:I see that you are using it the right way, but you can assign it directly to the
players
variable instead of assigning it to theref
. When you try to use this reactive object as the value of aref
object, this is not supported.You can solve your problem by changing this line:
To:
For more information on this topic, you can browse the following Documents