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
Related
HelloWorld.vue
<template>
<div>
<b>Vuejs dynamic routing</b>
<div v-for="item in items" :key="item.id">
<b>{{ item.id }}.</b>
<router-link :to="{ name: 'UserWithID', params: { id: item.id } }">
{{ item.kk }}
</router-link>
<router-link name="twoval"></router-link>
</div>
<br /><br /><br />
<User />
<Usertwo />
</div>
</template>
<script>
import User from "./User.vue";
import Usertwo from "./Usertwo.vue";
import { datalist } from "./datalist";
export default {
name: "HelloWorld",
components: {
User,
Usertwo,
},
data() {
return {
items: datalist,
};
},
};
</script>
User.vue
<template>
<div>
<div v-for="(item, key) in user" :key="key">
{{ item }}
</div>
</div>
</template>
<script>
import { datalist } from "./datalist";
export default {
name: "User",
data() {
return {
lists: datalist,
};
},
computed: {
user: function () {
return this.lists.find((item) => item.id === this.$route.params.id);
},
},
};
</script>
Usertwo.vue
<template>
<div>
<div v-for="usertwo in usertwos" :key="usertwo.mid">
{{ usertwo.doctor }}
</div>
</div>
</template>
<script>
import { datalisttwo } from "./datalisttwo";
export default {
name: "User",
data() {
return {
usertwos: datalisttwo,
};
},
};
</script>
main.js
import Vue from "vue";
import App from "./App.vue";
import VueRouter from "vue-router";
import HelloWorld from "./components/HelloWorld.vue";
Vue.use(VueRouter);
const router = new VueRouter({
routes: [
{ path: "/", name: "User", component: HelloWorld },
{ path: "/:id", name: "UserWithID", component: HelloWorld }
]
});
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App)
}).$mount("#app");
Logic trying to achieve, If i click on router-link id-1 from helloworld.vue component, then in User.vue and Usertwo.vue component. I need to show only array values list which is linked with id-1 only. from two arrray value list based on id.
Similarly on whatever the id, i click on from router-view from helloworld.vue. Same id value, i need to show inside the User and Usertwo.vue component.
Now only issue wit code is, Usertwo array value not loading correctly
I tried below code for that logic, But i couldn't make it.
This is my complete code:- https://codesandbox.io/s/pensive-williamson-n9bi6?file=/src/main.js:0-434
Something like this: https://codesandbox.io/s/white-bird-z6orf
Add props to the components
Send the id from the params to the components
Filter the list
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>
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.
I want to list the array named names of the first object in players using mapState with Vuex. In the current code, the objects in the players are listed according to their titles, but I want to filter them only according to the names in the first object on that page. On second page I want to list them according to the names that I will add to the second object. I hope I was able to explain my problem. How can I do this in the filter? Or what if there is a better way to do this?
Players.vue
<template>
<div class="Players">
<CostumText class="Players-title" tag="h1">Kulüpler</CostumText>
<div class="Players-search">
<input type="text" v-model="search" placeholder="Kulüp ara.." />
<label>Futbolcu ara:</label>
</div>
<div class="Players-inner">
<router-link
:to="players.pathName"
class="Players-inner-wrapper"
v-for="players in filteredList"
v-bind:key="players.id"
>
<div class="Players-inner-cards">
<Clubs class="Players-inner-cards-svg" v-bind:name="players.id" />
<CostumText tag="strong" lang="tr" class="Players-inner-cards-text">
{{ players.title }}
</CostumText>
</div>
</router-link>
</div>
<router-view />
</div>
</template>
<script>
import { mapState } from 'vuex'
import CostumText from '#/components/CostumText'
import Clubs from '#/components/Clubs.vue'
export default {
name: 'Players',
components: {
CostumText,
Clubs
},
data() {
return {
search: ''
}
},
computed: {
...mapState(['players']),
filteredList() {
return this.players.filter((player) =>
player.title.toLowerCase().includes(this.search.toLowerCase())
)
}
},
modules: {}
}
</script>
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
players: [
{
id: 1,
names: ['kerem', 'sirin', 'ali', 'ayse', 'ahmet'],
title: 'Ali',
pathName: 'ali'
},
{
id: 2,
title: 'Ayse',
pathName: 'ayse'
},
{
id: 3,
title: 'Ahmet',
pathName: 'ahmet'
}
]
},
getters: {},
mutations: {},
actions: {},
modules: {}
})
You can modify the filteredList to be
computed: {
...mapState(['players']),
filteredList() {
const filteredPlayers = this.players.filter(player => {
let flag = false;
if(player.names) {
player.names.forEach(name => {
if(name.toLowerCase().includes(this.search.toLowerCase()) flag = true;
});
}
return flag;
});
return filteredPlayers;
},
Here is how you display names
<div class="Players-inner-cards">
<Clubs class="Players-inner-cards-svg" v-bind:name="players.id" />
<CostumText tag="strong" lang="tr" class="Players-inner-cards-text">
{{ players.names.valueOf() }}
</CostumText>
</div>
I have two pages routed:
Home > Results
Inside 'Home', I have a button/link which is not in <route-view/> and I want this button to redirect to Results.vue passing a parameter.
This parameter named activeTab, has to open the desired vue-tabs, which I can't accomplish, because it's getting nothing from variable:
code:
Home.vue
<div class="row">
<Notes refName="Relationship" />
<Notes refName="Support" />
</div>
...
<script>
import Notes from '#/components/Monthlynotes.vue'
export default {
name: 'home',
components: {
Notes
},
</script>
/components/Monthlynotes.vue
<b-card>
<p class="card-text">{{ refName }}</p>
<b-link class="right" :activeTab="refName" href="/results">More</b-link>
</b-card>
...
<script>
export default {
props: {
refName: String,
},
</script>
Results.vue
<vue-tabs type="pills" v-model="tabName">
<v-tab title="Relationship">
<RelDT msg="Results view"/>
</v-tab>
<v-tab title="Support">
<SupDT msg="Results view"/>
</v-tab>
</vue-tabs>
...
<script>
import RelDT from '#/components/DataTable.rel.vue'
import SupDT from '#/components/DataTable.sup.vue'
export default {
name: 'results',
props: {
activeTab: String
},
components:
{
RelDT,
SupDT,
},
data() {
return {
tabName: activeTab
}
}
}
</script>
App
<router-link :to="{name:'results', param:{activeTab}}">Results</router-link>
How can I make this <b-link> route if it was a <route-link />?
Even the b-link component supports the :to property. To be found here
The value of the property will be passed to router.push().
<b-link :to="{name:'results', param:{activeTab}}">Redirect me</b-link>