Adam Youngers
Adam Youngers

Reputation: 6749

Where should route meta data be loaded in a Vue app?

I'm in the process of setting up a VueJs SPA. I'm using vue-router and I'm trying to find the best solution to the following problem. I have a series of routes. Each of which needs to call an API to get the meta data for the given ID.

/industry/:id/overview
/industry/:id/top-stories
/industry/:id/top-tweets
/brand/:id/overview
/brand/:id/top-stories
/brand/:id/top-tweets

I've been looking at using created or beforeRouteEnter/beforeRouteUpdate and I'm a bit lost. Ideally, I would only fetch new data when a new /industry/:id is reached, not when navigating between pages within the same ID. Also, I'd like to avoid having to define the fetch to grab data in every page component. Also don't want to over complicate this, so my question is, Is there a standard method for tackling this issue?

Clarification: When I say meta here, I mean data returned from an API about the given industry or brand which I pull using the ID in the route. The api call includes the name of the industry/brand which I want to have on page as soon as the page is presented to the user.

Upvotes: 0

Views: 700

Answers (1)

Palash Sharma
Palash Sharma

Reputation: 682

I have something similar. I tackle this using the following approach:

I use the same component for all /industry/:id Vue likes to reuse components wherever it can so if two routes (for example /industry/:id/overview and /industry/:id/top-stories) are using the same component it will stay the same.

What does change, however, is the route meta. So if you add a page key to the meta object in the route objects, and probably add a computed property called page that return this.$route.meta.page, you can use v-if attributes to conditionally render any component. So you might have something like <div v-if="page === 'overview'"></div><div v-else-if="page==='top-stories'"></div>

What this allows you to do is fetch all the data from the API during created or mounted lifecycle and store it as the state. Since the route change doesn't reload the component the state stays the same.

Here is a code example

// router.js

const Project = () =>
import(/* webpackChunkName: "projects" */ "./views/projects/_id");

export default new Router({
mode: "history",
routes: [
{
    path: "/projects/:project_id/views",
    name: "ViewProject",
    component: Project,
    meta: {
        page: "views",
    }
  },
  {
    path: "/projects/:project_id/export",
    name: "ExportProject",
    component: Project,
    meta: {
        page: "exports"
    }
  },
  {
    path: "/projects/:project_id/recommendations",
    name: "ProjectRecommendations",
    component: Project,
    meta: {
        page: "recommendations"
    }
  },
]
});

And here is the template

<template>
    <div v-if="project">
        <h1>{{ project.name }}</h1>
        <router-link :to="/project/someid/views">Views</router-link>
        <router-link :to="/project/someid/exports">Exports</router-link>
        <router-link :to="/project/someid/recommendations">Recommendations</router-link>
        <ul v-if="page==='views">
            <li v-for="(view, i) in project.views" :key="i">{{ views }}</div>
        </ul>
        <ul v-else-if="page==='exports">
            <li v-for="(export, i) in project.exports" :key="i">{{ export }}</div>
        </ul>
        <ul v-else-if="page==='recommendations">
            <li v-for="(recommendation, i) in project.recommendations" :key="i">{{ recommendation }}</div>
        </ul>
    </div>
</template>

<script>
export default {
    data() {
        return {
            project: null
        }
    },
    computed: {
        page() {
            return this.$route.meta.page;
        }
    },
    mounted() {
        this.getProject()
    },
    methods: {
        getProject() {
            axios
                .get(`/projects/someid`)
                .then(res => this.project = res.data)
        }
    }
}
</script>

Upvotes: 1

Related Questions