VUE JS router link wrong path - javascript

I am new to vue and I am working on a project to fetch wordpress REST api.
so I have "Posts" page and "Pages" page in my project, each one of them has different api endpoint and router links to every post/page in the loop with the slug name of the specific post/page.
When I press on the READ MORE router link button inside "Posts" page it sends me to the "Post" component with the post endpoint.
When I press on the READ MORE router link button inside Pages page it also sends me to the "Post" component but I need to get the "Page" component to fetch the page data from the api.
I noticed that if I set the page component before the post component inside index.js so both Posts and Pages will open the page component.
How can I set the "Page" component to the "Pages" router link and the "Post" component to "Posts" router link?
index.js:
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
},
{
path: '/posts',
name: 'Posts',
component: () => import(/* webpackChunkName: "posts" */ '../views/Posts.vue')
},
{
path: '/pages',
name: 'Pages',
component: () => import(/* webpackChunkName: "pages" */ '../views/Pages.vue')
},
{
path: '/:pageSlug',
name: 'Page',
component: () => import(/* webpackChunkName: "page" */ '../components/Page/Page.vue')
},
{
path: '/:postSlug',
name: 'Post',
component: () => import(/* webpackChunkName: "post" */ '../components/Post/Post.vue')
},
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
router.afterEach((to) => { // (to, from)
// Add a body class specific to the route we're viewing
let body = document.querySelector('body');
const slug = !(to.params.postSlug)
? to.params.pageSlug
: to.params.postSlug;
body.classList.add('vue--page--' + slug);
});
export default router
Pages.vue:
<template>
<div>
<template v-if="pages">
<div class="pages">
<div class="row">
<div class="column" v-for="(page, index) in pages" :key="index">
<div class="card">
<div class="card-image">
<img
v-if="page._embedded['wp:featuredmedia']"
:src="page._embedded['wp:featuredmedia'][0].source_url"
/>
</div>
<div class="card-content" v-html="page.excerpt.rendered"></div>
<div class="card-action">
<h3>{{ page.title.rendered }}</h3>
</div>
</div>
<router-link :to="page.slug" tag="div" key="page.id">
READ MORE </router-link> //this is where I think the problem is..
</div>
</div>
</div>
</template>
<div id="loaded" v-else>Loading...</div>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
// Wordpress Pages Endpoint
postsUrl: "https://my-domain.com/wp-json/wp/v2/pages",
queryOptions: {
per_page: 6, // Only retrieve the 6 most recent blog pages.
page: 1, // Current page of the collection.
_embed: true //Response should include embedded resources.
},
// Returned Pages in an Array
pages: []
};
},
methods: {
// Get Recent Pages From WordPress Site
getRecentMessages() {
axios
.get(this.postsUrl, { params: this.queryOptions })
.then(response => {
this.pages = response.data;
console.log("Pages retrieved!");
console.log(this.pages);
})
.catch(error => {
console.log(error);
});
},
},
mounted() {
this.getRecentMessages();
}
};
</script>
Posts.vue:
<template>
<div>
<template v-if="posts">
<div class="posts">
<div class="row">
<div class="column" v-for="(post, index) in post" :key="index">
<div class="card">
<div class="card-image">
<img
v-if="post._embedded['wp:featuredmedia']"
:src="post._embedded['wp:featuredmedia'][0].source_url"
/>
</div>
<div class="card-content" v-html="post.excerpt.rendered"></div>
<div class="card-action">
<h3>{{ post.title.rendered }}</h3>
</div>
</div>
<router-link :to="post.slug" tag="div" key="post.id">
READ MORE </router-link>
</div>
</div>
</div>
</template>
<div id="loaded" v-else>Loading...</div>
</div>
</template>
<script>
import axios from "axios";
export default {
data() {
return {
// Wordpress Posts Endpoint
postsUrl: "https://my-domain.com/wp-json/wp/v2/posts",
queryOptions: {
per_page: 6, // Only retrieve the 6 most recent blog posts.
page: 1, // Current page of the collection.
_embed: true //Response should include embedded resources.
},
// Returned Posts in an Array
posts: []
};
},
methods: {
// Get Recent Posts From WordPress Site
getRecentMessages() {
axios
.get(this.postsUrl, { params: this.queryOptions })
.then(response => {
this.posts= response.data;
console.log("Posts retrieved!");
console.log(this.posts);
})
.catch(error => {
console.log(error);
});
},
},
mounted() {
this.getRecentMessages();
}
};
</script>

OK
So based on documentation here:
https://router.vuejs.org/guide/essentials/named-routes.html
I just changed the router links in "Posts" and "Pages" components to piont to their specific component
for Posts.vue
You need to change :
<router-link :to="post.slug" tag="div" key="post.id">READ MORE </router-link>
With:
<router-link :to="{ name: 'Post', params: { postSlug: post.slug }}">READ MORE </router-link>
And the same for Pages.vue:
<router-link :to="{ name: 'Page', params: { pageSlug: page.slug }}">READ MORE </router-link>

Related

How to render different content in Vue contitionally

I am confused with Vue Routes and sending params as props over all files. I have following structure
When I click on card(ProjectDashboard) I want to be redirected to card detail page(ProjectOverview). On Card detail page is sidebar navigation. When I click on navi1 or navi2 I want to display different component on dashboard. Everything stay fixed only dashboard component will be different.
router.js
const router = new VueRouter( {
mode: 'history',
routes: [
{
path: '/',
name: 'ProjectsDashboard',
component: ProjectsDashboard,
},
{
path: '/project/:id',
name: 'project',
props: true,
component: ProjectOverview,
},
{
path: '/project/:id/something',
name: 'project',
props: true,
component: ProjectOverview,
},
{
path: '/:catchAll(.*)',
component: error404,
name: 'NotFound'
}
],
} );
ProjectDashboard.vue
<template>
<div class="m-3">
<div class="text-center m-2">DASHBOARD</div>
<div v-for="item in customJsonAllClient" :key="item.id">
<router-link :to="{name:'project', params:{id:item.id, bexioId:item.bexioId, gitlabId:item.gitlabId, title:item.title}}">
<b-card-group deck>
<b-card
bg-variant="light"
text-variant="dark"
class="text-center"
>
<b-card-text>
{{item.title}}
</b-card-text
>
</b-card>
</b-card-group>
</router-link>
</div>
</div>
</template>
<script lang="ts">
import { BCard, BCardText, BLink, BCardGroup } from 'bootstrap-vue';
import Vue from 'vue';
import { VServices } from '#libTs/vue-base';
import { jsonDataService } from './../services/customJsonData.service';
const SERVICES = VServices({
json: jsonDataService,
});
export default Vue.extend({
name: 'ProjectsDashboard',
components: {
BCard,
BCardText,
BLink,
BCardGroup
},
data() {
return {
customJsonAllClient: [],
};
},
async mounted() {
await SERVICES.json.getCustomJsonAllClients();
this.customJsonAllClient = SERVICES.json.customJsonAllClient;
},
});
</script>
ProjectOverview.vue
<template>
<div>
<navbar></navbar>
<site-navbar :id="projectData.id"></site-navbar>
<div class="sidenav-overlay"></div>
<div class="app-content content">
<div class="content-overlay"></div>
<div class="header-navbar-show"></div>
<div class="content-wrapper">
<b-card bg-variant="light" text-variant="dark" class="text-center">
<b-card-text>
{{ title }}
</b-card-text>
</b-card>
<div v-if="jsonClientsProjects">
<b-card v-for="item in jsonClientsProjects" :key="item.id">
<b-card-text>Project id {{ item.id }}</b-card-text>
<b-card-text>Project name {{ item.name }}</b-card-text>
<b-card-text>Description {{ item.description }}</b-card-text>
<b-card-text>
Project Created: {{ date.getCreatedDate(item.created_at) }}
</b-card-text>
<b-card-text>
Last Project Update :
{{ date.getDateLastUpdate(item.last_activity_at) }}
</b-card-text>
</b-card>
</div>
<div v-else>
<b-card>
<b-card-text>Project id {{ projectData.id }}</b-card-text>
<b-card-text>Project name {{ projectData.name }}</b-card-text>
<b-card-text>Description {{ projectData.description }}</b-card-text>
<b-card-text>
Project Created: {{ date.getCreatedDate(projectData.created_at) }}
</b-card-text>
<b-card-text>
Last Project Update :
{{ date.getDateLastUpdate(projectData.last_activity_at) }}
</b-card-text>
</b-card>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { BCard, BCardText, BLink, BCardGroup } from 'bootstrap-vue';
import Vue from 'vue';
import { VServices } from '#libTs/vue-base';
import Navbar from './navbar.vue';
import SiteNavbar from './site-navbar.vue';
import { dateService } from './../services/date.service';
import { bexioDataService } from './../services/bexioData.service';
import { gitlabDataService } from './../services/gitlabData.service';
import { jsonDataService } from './../services/customJsonData.service';
const SERVICES = VServices({
date: dateService,
bexio: bexioDataService,
gitlab: gitlabDataService,
json: jsonDataService,
});
export default Vue.extend({
name: 'ProjectOverview',
props: { id: String, bexioId: String, gitlabId: String, title: String },
components: {
BCardGroup,
BCard,
BCardText,
BLink,
Navbar,
SiteNavbar,
},
data() {
return {
customJsonAllClient: [],
jsonClientsProjects: [],
projectData: [],
date: SERVICES.date,
};
},
created() {},
async mounted() {
await SERVICES.json.getCustomJsonAllClients();
this.customJsonAllClient = SERVICES.json.customJsonAllClient;
this.getCorrectUserData();
await SERVICES.gitlab.getClientsProjects(this.projectData.gitlabId);
this.jsonClientsProjects = SERVICES.gitlab.jsonClientsProjects;
this.getCorrectUserData();
},
methods: {
getCorrectUserData() {
const url = window.location.pathname;
const pathArray = url.split('/');
let last = pathArray[pathArray.length - 1];
let obj = this.customJsonAllClient.find((x) => x.id == last);
this.projectData = obj;
},
},
});
</script>
siteNavbar.vue
<ul class="navigation navigation-main">
<li class="nav-item active">
<router-link to="/project/:id"> this link should switch to ProjectOverview </router-link>
</li>
<li class="nav-item active">
<router-link to="/project/:id/something"> this link should switch to some other component </router-link>
</li>
</ul>
router link is on first main vue app.
I can sent data from ProjectDashboard as a param and it show correct data on its detail page but I don't know how I should set up router link in sidebar that it always show correct data for current prop param and how I can render only component which I need.
PS: is it possible to add active class for navi link somehow with routs or I have to create separate function in methods for this?
what you want to do is using nested routes.
Check out:
https://router.vuejs.org/guide/essentials/named-views.html#nested-named-views
and
https://router.vuejs.org/guide/essentials/nested-routes.html
for active class watch:
https://router.vuejs.org/api/#active-class

How to make nested routing in Vuejs?

HelloWorld.vue
import axios from "axios";
export const router = () => axios.get("https://fakestoreapi.com/products");
<template>
<div>
<div v-for="item in items" :key="item.id">
<b> id: {{ item.id }}</b>
<router-link
:to="`/${item.id}`"
>
{{ item.title }}
</router-link>
</div><!-- end v-for -->
<router-view></router-view>
</div>
</template>
<script>
import { router } from "./router";
export default {
name: "HelloWorld",
components: {},
data() {
return {
items: [],
};
},
mounted() {
router().then((r) => {
this.items = r.data;
});
},
};
</script>
User.vue
import axios from "axios";
export const routerid = (itemId) =>
axios.get("https://fakestoreapi.com/products/" + itemId);
<template>
<div>
<div v-if="item">
<h1>Price: {{ item.price }}</h1>
</div>
<tabs />
</div>
</template>
<script>
import { routerid } from "./routerid";
import tabs from "./tabs";
export default {
name: "User",
components: {
tabs,
},
data() {
return {
item: null,
};
},
mounted() {
this.loadData();
},
computed: {
routeId() {
return this.$route.params.id;
},
},
watch: {
routeId() {
console.log("Reload (route change)");
this.loadData();
}, //reload when route id changes
},
methods: {
loadData() {
console.log("Reloading, ID", this.routeId);
if (!this.routeId) return; // no ID, leave early
routerid(this.$route.params.id).then((item) => {
this.item = item.data;
});
},
},
};
</script>
tabs.vue
import axios from "axios";
export const tabsandcontent = async (itemId) =>
await axios.get("https://fakestoreapi.com/products?limit=" + itemId);
<template>
<div>
<div v-if="item">
<h1>description: {{ item.description }}</h1>
</div>
</div>
</template>
<script>
import { tabsandcontent } from "./tabsandcontent";
export default {
name: "User",
components: {},
data() {
return {
item: null,
};
},
mounted() {
this.loadData();
},
computed: {
tabsandcontent() {
return this.$route.params.id;
},
},
watch: {
tabsandcontent() {
console.log("Reload (route change)");
this.loadData();
}, //reload when route id changes
},
methods: {
loadData() {
console.log("Reloading, ID", this.tabsandcontent);
if (!this.tabsandcontent) return; // no ID, leave early
tabsandcontent(this.$route.params.id).then((item) => {
this.item = item.data;
});
},
},
};
</script>
main.js
import Vue from "vue";
import App from "./App.vue";
import VueRouter from "vue-router";
import HelloWorld from "./components/HelloWorld";
import User from "./components/User";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{
path: "/HelloWorld",
name: "HelloWorld",
component: HelloWorld,
children: [{ path: ":id", name: "User", component: User }]
}
]
});
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App)
}).$mount("#app");
code:- https://codesandbox.io/s/combined-logic-api-forked-41lh0f?file=/src/main.js
can you please answer this, In main.js routing I changed from path: "/" to path: "/HelloWorld" then all of sudden output not reflecting... because in my project path:'/' indicates login page??? In this scenario what changes, i need to make, to make logic work
also where is the relation between path:'/' and api call??
You have same name for the variables in tabs component (In watch and computed). And In tabsandcontent.js, you have missed to fetch description for the specific item as performed in routerId.js.
Have a look at modified version which is working as you expected.
https://codesandbox.io/embed/combined-logic-api-forked-ji5oh4?fontsize=14&hidenavigation=1&theme=dark
First thing first, I want you to know that I don't understand what are you asking for. But I'm going to try to answer.
Your first question:
In main.js routing I changed from path: "/" to path: "/HelloWorld" then all of sudden output not reflecting.
Yes, you will not see your HelloWorld.vue component. You can see your page however if you type <your-url>/HelloWorld. Usually the / path is used for something like "Home" page.
However, I've tried checking out your codesandbox. And take a look at your HelloWorld.vue component.
I think you are confused because when you changed the path from / to /HelloWorld apart from the HelloWorld.vue not showing up. It somehow broken the link which causes the API in tabs.vue not functioning.
If that's the case, you just have to simply add HelloWorld/${item.id} in tabs.vue,
<template>
<div>
<div v-for="item in items" :key="item.id">
<b> id: {{ item.id }}</b>
<router-link
:to="`HelloWorld/${item.id}`" // --> Notice this line
>
{{ item.title }}
</router-link>
</div><!-- end v-for -->
<router-view></router-view>
</div>
</template>
This however, isn't a common thing to do routing. You should add your App URLs to main.js. Which also isn't common, but I'm assuming this is just a little reproduction code you made for StackOverflow.
Here are my CodeSandbox edits.
https://codesandbox.io/s/combined-logic-api-forked-jttt8p
I will update the answer again later, I'm still not on my personal laptop.

Why does every component in my page appears twice?

I have created a sort of First page for my project, but I don't know why it renders everything twice on my page:
here is my First.vue component that is used by the router as the first page:
<template>
<h1>Bienvenue</h1>
<router-view></router-view>
<div class="routing" v-if="this.$route.path == '/'">
<router-link to='/app'>Go to application</router-link>
<br>
<router-link to='/test'>Go to test</router-link>
</div>
</template>
and here is what I get on the page when I npm run serve
Does anyone knows where it comes from?
UPDATE
When I delete the router-view element, the components appear once but when I click on one of the links, it changes the URL of the page but the page in itself is not showing the component.
And when I try to put everything in my router-view, like this:
<template>
<router-view>
<div class="routing" v-if="this.$route.path == '/'">
<h1>Bienvenue</h1>
<router-link to='/app'>Go to application</router-link>
<br>
<router-link to='/test'>Go to test</router-link>
</div>
</router-view>
</template>
it appears once, but like the other case, when I click on a link, it is just changing the URL and not the page.
Here is my index.js to show you how my routes are defined:
import {createRouter, createWebHistory} from 'vue-router'
import App from '../App.vue'
import Test from '../Views/Test.vue'
import First from '../Views/First.vue'
export const router = createRouter({
history: createWebHistory(),
routes: [
{
path:'/',
name: 'First',
component: First,
},
{
path:'/test',
name: 'Test',
component: Test
},
{
path:'/app',
name: 'App',
component: App
}
]
})
Just to clarify what results I expect from my app:
When I run the project, I want to launch on a page with just a header saying 'Hello' at the top of the page and two links where I can either click on 'Go to the application' or 'Go to test'.
Then, when I click on one of the links, I want to see the content of the component (so either test or the app), but I don't want to see the header and the links anymore.
I am not sure but try this:
{
path: "/app",
component: () => import("../App.vue"),
name: "App"
},
{
path: "/test",
component: () => import("../Views/Test.vue"),
name: "Test"
},
{
path: "/",
component: () => import("../Views/First.vue"),
name: "First"
}
Update with sample code or you can refer to live code here.
// App.vue
const First = {
template: `<div class = "container" id="app">
<h1>Hi from First</h1>
<hr>
<router-link to="/foo">Foo</router-link>
<router-link to="/bar">Bar</router-link>
</div>
`
}
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }
const routes = [
{
path:'/',
name: 'First',
component: First,
default: First
},
{
path:'/foo',
name: 'Foo',
component: Foo
},
{
path:'/bar',
name: 'Bar',
component: Bar
}
]
const router = new VueRouter({
routes
})
const app = new Vue({
router
}).$mount('#app')
// main.js
<div id="app">
<!-- route outlet -->
<!-- component matched by the route will render here -->
<router-view></router-view>
</div>

Vue router doesn't route to specified location

I'm using vue with vue-router and route doesn't work when routing to children route.
{
path: '/user',
name: 'User',
component: User,
children: [
{
path: 'profile',
component: Profile,
},
],
}
Programmatically routing to /user/profile
<template>
<div>
<button #click="goToUserProfile()">create new</button>
</div>
</template>
<script>
export default {
methods: {
goToUserProfile() {
this.$router.push('/user/profile') // Routing doesn't work
.catch(console.log);
},
},
};
</script>
give a route name "Profile" for "/user/profile"
{
path: '/user',
name: 'User',
component: User,
children: [
{
path: 'profile',
name: "Profile",
component: Profile,
},
],
}
navigation use route name
this.$router.push({name: "Profile"});
your User component should declare like this
User.vue
<template>
<div>
<p>this is user component</p>
<!-- your Profile component will replace this route-view -->
<route-view />
</div>
</template>
demo
https://codesandbox.io/s/falling-bash-6dl7m
Do your ensure that you put <router-view></router-view> in User Component template to make the nested (children) routes displayed.
<template>
<div>
<button #click="goToUserProfile()">create new</button>
<router-view></router-view> <!-- placeholder for children routes -->
</div>
</template>
Then you can access by both
this.$router.push('/user/profile') and this.$router.push({ name: 'UserProfile' })
As Vue router document states:
To render components into this nested outlet, we need to use the children option in VueRouter.
https://router.vuejs.org/guide/essentials/nested-routes.html
Hope this help.

Vue-router nested route not loading my component page

I am using Vue-router and I was needing to use a nested route, so what I did I wrote Children Key word to reference for a child component page, so my problem is when I click on Link my current URL replaced with nested route and with an ID but not load my component, and when I change Router-Linke to another component that is not nested it will load component, so I ask what are my mistakes?
route.js
{
path: '/targets',
name: 'target',
component: () =>
import ( /* webpackChunkName: "target" */ '#/views/admin/Target.vue'),
meta: {
middleware: [
auth
],
title: "Targets"
},
children:[
{
path: '/targets/:id/details',
name: 'target-details',
props: true,
component: () =>
import ( /* webpackChunkName: "target" */ '#/views/admin/TargetDetails.vue'),
meta: {
middleware: [
auth
],
title: "TargetDetails"
}
},
]
},
target.vue
<template>
<div>
<li class="clearfix" v-for="domain in domains" :key="domain.domain_id">{{ domain.domain }}
<router-link class="more"
:to="{
name: 'target-details',
params: {
id: domain.domain_id
}
}" >Target details <i class="fa fa-angle-right"></i>
</router-link>
</li>
</div>
</template>
TargetDetails.vue
<template>
<div class="page-output">
<h1>Target Details</h1>
</div>
</template>
Nested routes are designed to facilitate component nesting (look at the 1st "picture")
You need to include <router-view> in your target.vue component...
If you don't want content of TargetDetails.vue rendered inside target.vue, dont use children config and make a target.vue top level route instead.

Categories