I've got a problem with my hobby project, I created a BlogPost.vue in which I render the previously clicked post from a Blog.vue page.
In this BlogPost, I render the clicked post's title, content etc, and on the sidebar, I show a small area in which I render 3 of the latest posts from this same category - the posts from Blog.vue.
When I click on the sidebar's links, any of the 3, the browser does change the slug, and the page does re-render itself, except it re-renders the same post that was clicked. If I refresh the page in the browser (or Ctrl+R or F5 etc), then it does render the correct content clicked from this sidebar.
I have no clue why it does that, I can only suppose that I should be watching the route change then somehow refresh what it renders, but no idea as to how.
Blog.vue, this works great, renders the single post clicked
<script setup lang="ts">
import axios from "axios";
import { ref } from "vue";
import { onMounted } from "vue";
import moment from "moment";
const postsUrl = "http://localhost/wordpress/wp-json/wp/v2/posts";
const posts = ref([] as any);
const isLoading = ref(false);
const errorCaught = ref(false);
var queryOptions = {
_embed: true,
};
const getPosts = () => {
isLoading.value = true;
axios
.get(postsUrl, { params: queryOptions })
.then((response) => {
posts.value = response.data;
console.log(posts.value);
isLoading.value = false;
})
.then(() => {
console.log(isLoading.value);
})
.catch((error) => {
if (error) {
isLoading.value = false;
errorCaught.value = true;
}
});
};
onMounted(async () => {
getPosts();
});
</script>
<template>
<transition name="fadeLoading">
<div v-if="isLoading" class="posts-loading">
<div class="circle"></div>
</div>
</transition>
<transition name="fadeLoading">
<div class="errorCaught" v-if="errorCaught">
There was an error loading news
</div>
</transition>
<div class="blog-container">
<div class="wrapper">
<transition-group name="fadeBlog">
<ul v-if="!isLoading" class="blog-posts-ul" v-for="post in posts">
<div class="posts-card">
<a
><router-link
:to="/blog/ + post.slug"
key="post.id"
class="posts-permalink"
>
</router-link
></a>
<img
v-if="post.featured_media != 0"
class="posts-featuredimage"
:src="post._embedded['wp:featuredmedia'][0].source_url"
:alt="post.title.rendered"
/>
<img v-else src="@/assets/logos/favicon-big.png" />
<div class="posts-date">
<p>
{{ moment(post.date).fromNow() + " " + "ago" }}
</p>
</div>
<div class="posts-text">
<h1 class="posts-title">{{ post.title.rendered }}</h1>
<p v-html="post.excerpt.rendered" class="posts-excerpt"></p>
</div>
</div>
</ul>
</transition-group>
</div>
</div>
</template>
BlogPost.vue, renders the previously clicked one, but does not show the sidebar's clicked content
<script setup lang="ts">
import { ref, watch, onMounted } from "vue";
import { useRoute } from "vue-router";
import axios from "axios";
import moment from "moment";
const route = useRoute();
const postsUrl = "http://localhost/wordpress/wp-json/wp/v2/posts";
const queryOptions = {
slug: route.params.blogSlug,
_embed: true,
};
const post = ref([] as any);
const isLoading = ref(false);
const latestPostsAPI = "http://localhost/wordpress/wp-json/wp/v2/posts";
const latestPosts = ref([] as any);
const errorCaughtLatest = ref(false);
var queryOptionsLatest = {
_embed: true,
per_page:3,
};
const getLatest = () => {
axios
.get(latestPostsAPI, { params: queryOptionsLatest })
.then((response) => {
latestPosts.value = response.data;
console.log(latestPosts.value);
})
.catch((error) => {
if (error) {
errorCaughtLatest.value = true;
}
});
};
const getPost = () => {
isLoading.value = true;
axios
.get(postsUrl, { params: queryOptions })
.then((response) => {
post.value = response.data;
console.log("Pages retrieved!");
})
.catch((error) => {
console.log(error);
})
.then(() => {
isLoading.value = false;
});
};
getLatest();
getPost();
</script>
<template>
<div v-if="!isLoading" class="post-wrapper">
<div class="wrapper">
<div class="post-title">{{ post[0].title.rendered }}</div>
<div class="post-date">
{{ moment(post[0].date).format("MMMM Do YYYY, h:mm, dddd") }}
</div>
<!-- THIS INCLUDES HTML TAGS -->
<div class="post-content" v-html="post[0].content.rendered"></div>
</div>
</div>
<div class="side-container">
<div class="side-wrapper">
<ul v-if="!isLoading" class="blog-posts-ul" v-for="latest in latestPosts">
<div class="posts-card">
<a
><router-link
:to="/blog/ + latest.slug"
key="latest.id"
class="posts-permalink"
>
</router-link
></a>
<img
v-if="latest.featured_media != 0"
class="posts-featuredimage"
:src="latest._embedded['wp:featuredmedia'][0].source_url"
:alt="latest.title.rendered"
/>
<img v-else src="@/assets/logos/favicon-big.png" />
<div class="posts-text">
<div class="posts-title">{{ latest.title.rendered }}</div>
<div class="posts-date">
<p>{{ moment(latest.date).fromNow() + " " + "ago" }}</p>
</div>
<div class="posts-author">
{{ latest._embedded.author[0].name }}
</div>
</div>
</div>
</ul>
</div>
</div>
</template>
Thanks for your time
In my original post as you can see, I'm using axios
to get my post with the params
queryOptions
:
I declare my queryOptions with a const:
const queryOptions = {
slug: route.params.blogSlug,
_embed: true,
};
THEN, I call the getPost method, which uses this constant:
const getPost = () => {
isLoading.value = true;
axios
.get(postsUrl, { params: queryOptions } })
.then((response) => {
post.value = response.data;
console.log("getPost!");
})
.catch((error) => {
console.log(error);
})
.then(() => {
isLoading.value = false;
});
};
And the problem is right there: that queryOptions
, IS a constant, gets defined at the onMounted lifecycle, or even before I'm not 100% sure, but it does NOT get re-defined WHEN I click on any link on this BlogPost.vue component.
SO, the solving is the following:
Change the getPost
method so it does NOT use a pre-defined constant, rather just simply type in the params, like the following:
const getPost = () => {
isLoading.value = true;
axios
.get(postsUrl, { params: { slug: route.params.blogSlug, _embed: true } })
.then((response) => {
post.value = response.data;
console.log("getPost!");
})
.catch((error) => {
console.log(error);
})
.then(() => {
isLoading.value = false;
});
};
If I do it this way, every time the getPost
method runs, which it WILL since I'm watching the blogSlug
change with
watch(() => route.params.blogSlug, getPost);
the getPost
function will always call the latest route.params.blogSlug
, which gets changed before the watcher runs the getPost
as I click on the sidebar link.