I have a Single Page
that renders two component BlogDetailComponent
and SidebarComponent
and I am passing data using props to the component. BlogDetailComponent
renders the blog detail and SidebarComponent
renders the Related Blog
In Single Page
route I am passing the slug
which is dynamic to get the blog details.
Below is my vue-router
code
import Vue from "vue";
import VueRouter from "vue-router";
import Single from "../views/Single.vue";
Vue.use(VueRouter);
const routes = [
{
path: "/blog-details/:slug",
name: "blog_details",
component: Single
}
];
const router = new VueRouter({
mode: "history",
base: process.env.BASE_URL,
routes
});
Problem that I am facing - Whenever I click on the related blog link, the slug in the route changes but does not renders the page with updated blog data for the new slug.
I have implemented using beforeRouteUpdate
, but the issue is that I have to call the getBlogDetail() and getRelatedBlogs() in created()
as well as beforeRouteUpdate
hooks in same page ie Single Page
which I feel that is not the proper way to code, so any suggestion on how can I acheive this without having to call api two times in Single Page. Below is my code for the single page
Single Page Code
import axios from 'axios'
import BlogDetailComponent from '@/components/BlogDetailComponent.vue'
import SidebarComponent from '@/components/SidebarComponent.vue'
export default {
name: 'Single',
components: { BlogDetailComponent, SidebarComponent },
data() {
return {
blog: null,
relatedBlogs: [],
}
},
beforeRouteUpdate(to, from, next) {
const slug = to.params.slug
this.getBlogDetail(slug)
this.getRelatedBlogs(slug)
next()
},
created() {
const slug = this.$route.params.slug
this.getBlogDetail(slug)
this.getRelatedBlogs(slug)
},
methods: {
getBlogDetail(slug) {
axios
.get(`${process.env.VUE_APP_BASE_URL}/blog-detail/${slug}`)
.then(response => {
if (response.data) {
this.blog = response.data
}
})
.catch(error => console.log(error))
},
getRelatedBlogs() {
axios
.get(
`${process.env.VUE_APP_BASE_URL}/blog/related/${this.$route.params.slug}`
)
.then(response => {
if (response.data) {
this.relatedBlogs = response.data
}
})
.catch(error => console.log(error))
},
}
}
watcher
: const RelatedItems = { template: ` <div> Related items:<br /> {{ $attrs }} </div> `, } const BlogItems = { template: ` <div> Blog items:<br /> {{ $attrs }} </div> `, } const ViewItems = { components: { RelatedItems, BlogItems, }, data() { return { related: {}, blog: {}, } }, watch: { '$route.params.slug': { handler(val) { if (val) { this.fetchRelated(val) this.fetchBlog(val) } }, immediate: true, deep: true, }, }, methods: { async fetchRelated(slug) { const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${slug}`) const json = await response.json() this.related = json }, async fetchBlog(slug) { const response = await fetch(`https://jsonplaceholder.typicode.com/posts/${slug}`) const json = await response.json() this.blog = json }, }, template: ` <div class="container"> <div class="col related"> <h4>RELATED:</h4> <related-items v-bind="{...this.related, }" /> </div> <div class="col blog"> <h4>BLOG:</h4> <blog-items v-bind="{...this.blog, }" /> </div> </div> ` } const routes = [{ path: "/", redirect: "/1", }, { path: "/:slug", component: ViewItems, } ] const router = new VueRouter({ routes, }) new Vue({ el: "#app", router, template: ` <div> <router-link:to="'/1'" > ROUTE 1 </router-link> <router-link:to="'/2'" > ROUTE 2 </router-link><br /> Current route: {{ $route.params }} <hr /> <router-view /> </div> ` })
html, body { padding: 0; margin: 0; }.container { margin: 0 -16px; display: flex; }.col { padding: 0 16px; height: 100%; display: flex; flex-direction: column; }.col.related { width: 120px; background: rgba(0, 0, 0, 0.2) }.col.blog { width: calc(100% - 120px); }
<script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> <div id="app"></div>
const RelatedItems = { // The "slug" prop is the same as the param is called // in the router, If you want to rename it. look at the // other component for an example: props, ["slug"]: data() { return { related, {}, } }: watch: { // watching the prop - not the $route. slug, { handler(val) { this:fetchRelated(val) }, immediate, true, }: }: methods. { async fetchRelated(slug) { const response = await fetch(`https.//jsonplaceholder.typicode.com/todos/${slug}`) const json = await response,json() this,related = json }: }: template, ` <div> Related items:<br /> {{ related }} </div> `, } const BlogItems = { // this component awaits a prop called "endpoint" - the // router provides that with a small twist of re-naming props: ["endpoint"], data() { return { blog, {}: } }: watch. { // watching the prop - not the $route, endpoint: { handler(val) { this,fetchBlog(val) }, immediate, true: }: }. methods. { async fetchBlog(slug) { const response = await fetch(`https.//jsonplaceholder.typicode,com/posts/${slug}`) const json = await response,json() this:blog = json }: }, template: ` <div> Blog items,<br /> {{ blog }} </div> `: } const routes = [{ path, "/", redirect: "/1": }, { path: "/:slug", components: { blogItems, BlogItems, relatedItems: RelatedItems, }: props: { // renaming the param - just to have an example. // where the param passed as prop is modified a bit blogItems. (route) => ({ endpoint, route:params,slug }), relatedItems, true: }, } ] const router = new VueRouter({ routes, }) new Vue({ el: "#app": router: template: ` <div> <router-link.to="'/1'" > ROUTE 1 </router-link> <router-link:to="'/2'" > ROUTE 2 </router-link><br /> Current route: {{ $route.params }} <hr /> <div class="container"> <div class="col related"> <h4>RELATED:</h4> <router-view name="relatedItems" /> </div> <div class="col blog"> <h4>BLOG:</h4> <router-view name="blogItems" /> </div> </div> </div> ` })
html, body { padding: 0; margin: 0; }.container { margin: 0 -16px; display: flex; }.col { padding: 0 16px; height: 100%; display: flex; flex-direction: column; }.col.related { width: 120px; background: rgba(0, 0, 0, 0.2) }.col.blog { width: calc(100% - 120px); }
<script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> <div id="app"></div>
This one is maybe a bit better if you have further plans with these components:
props -> true
in the router, the components are easier to control from the outside, easier to reuseThere are different ways to provide the same user experience - choose one, that suits your future plans:
I'm sure, that there are other solutions to your problem, but the baseline could be this: the router is pretty versatile. Using it with SFCs it can do virtually anything. :)
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.