I'm loading some product category data from Prismic into my Nuxt project, and I'm looking for some clarification regarding best practices. Specifically, how to handle the state in which the data is still being fetched, and there's no content to display in the sidebar.
Right now, here's what I've got working:
I have a sidebar component (Sidebar.vue
):
<template>
<div class="xl:flex-shrink-0 xl:w-64 border-r border-gray-200 pt-5 pb-4 bg-white overflow-y-auto ">
<h3 class="text-xl font-bold text-gray-800">{{ navigation.heading }}</h3>
<div class="mt-5 flex-grow flex flex-col">
<nav class="flex-1 px-2 space-y-1 bg-white" aria-label="Sidebar">
<div v-for="(category, index) in navigation.categories" :key="index">
<SidebarItem v-if="!category.subcategories.length" :category="category"/>
<SidebarSection v-else-if="category.subcategories.length" @click="toggleExpansion(index)" :category="category"/>
</div>
</nav>
</div>
</div>
</template>
<script lang="ts">
export default {
data() {
return {
navigation: new Navigation('Categories')
}
},
async mounted() {
const that = this
this.fetchCategories()
.then(function() {
that.navigation = that.$store.getters.navigation
})
},
methods: {
...mapActions({ fetchCategories: 'fetchCategories' })
}
}
</script>
As you can see, I have a navigation
property which will contain all of the data required to populate the sidebar. At the moment, I have initialised a placeholder instance (new Navigation('Categories')
), as without this, Vue reports that navigation
is undefined.
This doesn't feel like an ideal way to go about this. What would be the most appropriate way to handle this intermediary state before the data is loaded, without providing a placeholder instance?
Nuxt's fetch
hook could be useful here. It exposes $fetchState
(includes pending
, error
, and timestamp
) to use imperatively or in your template. Your template could check the pending
flag before rendering the navigation
data:
<template>
<div v-if="!$fetchState.pending" 👈
class="xl:flex-shrink-0 xl:w-64 border-r border-gray-200 pt-5 pb-4 bg-white overflow-y-auto">
<h3 class="text-xl font-bold text-gray-800">{{ navigation.heading }}</h3>
<div class="mt-5 flex-grow flex flex-col">
<nav class="flex-1 px-2 space-y-1 bg-white" aria-label="Sidebar">
<div v-for="(category, index) in navigation.categories" :key="index">
<SidebarItem v-if="!category.subcategories.length" :category="category"/>
<SidebarSection v-else-if="category.subcategories.length" @click="toggleExpansion(index)" :category="category"/>
</div>
</nav>
</div>
</div>
</template>
<script lang="ts">
export default {
// BEFORE:
// async mounted() {
// const that = this
// this.fetchCategories()
// .then(function() {
// that.navigation = that.$store.getters.navigation
// })
// },
// AFTER:
async fetch() {
await this.fetchCategories()
this.navigation = this.$store.getters.navigation
},
}
</script>