I'm having an issue trying to get a component to re-render on nav click while on the same component. For example, I'm on /one -- clicking on the nav link for /one does not re-render the component which I would like it to do. Any bright ideas on how to achieve this?
router:
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
import One from '../pages/One.vue'
import Two from '../pages/Two.vue'
const router = new Router({
mode: 'history',
routes: [
{ path: '/one', component: One, name: 'One', meta: {title: 'One'} },
{ path: '/two', component: Two, name: 'Two', meta: {title: 'Two'} },
]
});
export default router;
nav.vue:
<template>
<div>
<ul>
<li v-for="item in nav">
<nav-link class="nav-root-link" v-else :link="item"></nav-link>
</li>
</ul>
</div>
</template>
<script>
import navLink from './navLink.vue'
export default {
components: { navLink },
data () {
return {
groupVisible: [],
nav: [
{ name: 'One', to: {name: 'One'}},
{ name: 'Two', to: {name: 'Two'}},
]
}
},
}
</script>
navLink.vue:
<template>
<router-link class="nav-link" :to="link.to">
<span class="nav-link-name">{{ link.name }}</span>
</router-link>
</template>
<script>
export default {
props: {
link: {
required: true,
type: Object
}
},
}
</script>
Inside nav.vue just add :key
<template>
<div>
<ul>
<li v-for="(item, index) in nav" :key="index">
<nav-link class="nav-root-link" v-else :link="item"></nav-link>
</li>
</ul>
</div>
</template>
Related
My vuejs version is 3.
I implement both of these ways to use keep-alive in my app.
1
<template>
<div id="nav" classs="container is-max-desktop"></div>
<keep-alive>
<router-view />
</keep-alive>
</template>
<style>
#import "~bulma/css/bulma.css";
</style>
2
import { createRouter, createWebHistory } from "vue-router";
import Index from "../views/Index.vue";
import Create from "../views/Create.vue";
import Edit from "../views/Edit.vue";
const routes = [
{
name: "Index",
path: "/",
component: Index,
meta: { KeepAlive: true },
},
{
name: "Edit",
path: "/edit/:id",
component: Edit,
meta: { KeepAlive: true },
},
{
name: "Create",
path: "/create",
component: Create,
meta: { KeepAlive: true },
},
{
path: "/about",
name: "About",
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () =>
import(/* webpackChunkName: "about" */ "../views/About.vue"),
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
export default router;
Simply, I apply many filters on my page but when I came back to that page they are gone. Help me to solve this problem. Thank you.
Keep alive caches components when dynamically switching them which will retain the data's in that particular component while switching. Use the below code for integration,
Maincomponent.vue
<script>
import Component1 from './Component1.vue'
import Component2 from './Component2.vue'
export default {
components: { Component1, Component2 },
data() {
return {
current: 'Component1'
}
}
}
</script>
<template>
<div class="demo">
<label><input type="radio" v-model="current" value="Component1" /> A</label>
<label><input type="radio" v-model="current" value="Component2" /> B</label>
<KeepAlive>
<component :is="current"></component>
</KeepAlive>
</div>
</template>
Component1.vue
<script>
export default {
data() {
return {
count: 0
}
}
}
</script>
<template>
<div>
<p>Current component: A</p>
<span>count: {{ count }}</span>
<button #click="count++">+</button>
</div>
</template>
Component2.vue
<script>
export default {
data() {
return {
msg: ''
}
}
}
</script>
<template>
<div>
<p>Current component: B</p>
<span>Message is: {{ msg }}</span>
<input v-model="msg">
</div>
</template>
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: "/",
name: "HelloWorld",
component: HelloWorld,
children: [{ path: ":id", name: "User", component: User }]
}
]
});
Vue.config.productionTip = false;
new Vue({
router,
render: (h) => h(App)
}).$mount("#app");
HelloWorld.vue
<template>
<div>
<div v-for="item in items" :key="item.id">
<b> id: {{ item.id }}</b>
<router-link :to="`/HelloWorld/${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>
codesandbox:- https://codesandbox.io/s/combined-logic-api-forked-oq808t?file=/src/main.js
in main.js routing file, if i change from path:'/' to path:'/HelloWorld' then output is not displaying. Not sure why and where it is linked with api call..
The reason why i changed from path:'/' to path:'/HelloWorld' because path:'/' in my project, it will consider as login page. So tried changing but getting blank screen.
You should change
const router = new VueRouter({
routes: [
{
path: "/HelloWorld/",
name: "HelloWorld",
component: HelloWorld,
children: [{ path: ":id", name: "User", component: User }]
}
]
});
to
const router = new VueRouter({
mode: 'history',
routes: [
{
path: "/HelloWorld/",
name: "HelloWorld",
component: HelloWorld,
children: [{ path: ":id", name: "User", component: User }]
}
]
});
(So, add mode: history, to the router.)
More on mode: history in Vue Router v3 here
I have a main app page component with a search bar. The Search results that come back is in cards And I am looking to set up a "Click here to view more detail" that would be placed in each card. And it would link to the Details page of the one result clicked. How do I link these components on Vue and if the id could be passed? I am hoping that upon click of the button the component renders on the same page and not a new tab.
Thank you!
App.vue
<template>
<div id="app">
<Header/>
<SearchForm v-on:search="search"/>
<SearchResults
v-if="results.length > 0"
v-bind:results="results"
v-bind:reformattedSearchString="reformattedSearchString"/>
<Pagination
v-if="results.length > 0"
v-bind:prevPageToken="api.prevPageToken"
v-bind:next_page="api.scrollId"
v-on:prev-page="prevPage"
v-on:next-page="nextPage"
/>
</div>
</template>
<script>
import Header from './components/layout/Header';
import SearchForm from './components/SearchForm';
import SearchResults from './components/SearchResults';
import Pagination from './components/Pagination';
import Details from './components/Details'
import axios from 'axios';
export default {
name: 'app',
components: {
Header,
SearchForm,
SearchResults,
Pagination,
Details
},
data() {
return {
results: [],
reformattedSearchString: '',
api: {
baseUrl: 'https://test.org/api/v1/articles?',
max: 25,
q: '',
prevPageToken: '',
scrollId: ''
}
};
},
methods: {
search(searchParams) {
this.reformattedSearchString = searchParams.join(' ');
this.api.q = searchParams.join('+');
const { baseUrl, q, max} = this.api;
const apiUrl = `${baseUrl}&term=${q}&title_like=${q}&recent&max=${max}&full_results`;
this.getData(apiUrl);
},
prevPage() {
const { baseUrl, q, max, prevPageToken } = this.api;
const apiUrl = `${baseUrl}&term=${q}&title_like=${q}&max=${max}&pageToken=${prevPageToken}`;
this.getData(apiUrl);
},
nextPage() {
const { baseUrl, q, max,scrollId } = this.api;
const apiUrl = `${baseUrl}&term=${q}&title_like=${q}&max=${max}&recent&full_results&scroll_id=${scrollId}`;
this.getData(apiUrl);
},
getData(apiUrl) {
axios
.get(apiUrl)
.then(res => {
this.results = res.data.success.data;
this.api.prevPageToken = res.data.success.data.prevPageToken;
this.api.next_page = res.data.scrollId;
})
.catch(error => console.log(error))
}
}
};
</script>
Searchresults.vue
<template>
<div class="container mb-3">
<div class="d-flex mb-3">
<div class="mr-auto">
<h3>Search Results for "{{ reformattedSearchString }}"</h3>
</div>
<div class="btn-group ml-auto" role="group">
<button
#click="changeDisplayMode('grid')"
type="button"
class="btn btn-outline-secondary"
v-bind:class="{ active: displayMode === 'grid' }"
>
<i class="fas fa-th"></i>
</button>
<button
#click="changeDisplayMode('list')"
type="button"
class="btn btn-outline-secondary"
v-bind:class="{ active: displayMode === 'list' }"
>
<i class="fas fa-list"></i>
</button>
</div>
</div>
<div class="card-columns" v-if="displayMode === 'grid'">
<div class="card" v-bind:key="result._gddid" v-for="result in results">
<ArticleGridItem v-bind:result="result"/>
</div>
</div>
<div v-else>
<div class="card mb-2" v-bind:key="result._gddid" v-for="result in results">
<ArticleListItem v-bind:result="result"/>
</div>
</div>
</div>
</template>
<script>
import ArticleListItem from './ArticleListItem';
import ArticleGridItem from './ArticleGridItem';
import Details from './Details';
export default {
name: 'SearchResults',
components: {
ArticleListItem,
ArticleGridItem,
Details,
},
data() {
return {
title: 'Search Results',
displayMode: 'grid'
};
},
methods: {
changeDisplayMode(displayMode) {
this.displayMode = displayMode;
}
},
props: ['results', 'reformattedSearchString']
};
</script>
ArticleListItem.vue
<template>
<div>
<div class="card-body">
<h6 class="card-text">{{ result.title }}</h6>
<p
class="card-subtitle mb-2 text-muted"
>{{ result.publisher }} | {{ result.journal }} | {{ result.year }}</p>
<a :href="'https://test.org/api/articles?docid=' + result._gddid" target="_blank">
<i class="fa fa-download" alt="Download"> </i>
</a>
<router-link>
<v-btn dark to="{name:'Details', params: {id: article._gddid}}">
Click here for more Details
</v-btn>
</router-link>
<router-view></router-view>
</div>
</div>
</template>
<script>
export default {
name: 'ArticleListItem',
props: ['result'],
}
</script>
index.js
import Vue from 'vue';
import Router from 'vue-router';
import Details from '#/components/Details';
Vue.use(Router)
export default new Router({
routes: [
{
path: '/Details/:id',
name: 'Details',
component: Details
}
]
});
main.js
import Vue from 'vue'
import App from './App.vue'
import router from './router/index.js'
import moment from 'moment'
Vue.config.productionTip = false
Vue.filter('formatDate', function (value) {
if (!value) return ''
return moment(value.toString()).format('MM/DD/YYYY hh:mm')
})
new Vue({
router,
render: h => h(App),
}).$mount('#app')
use <router-link> tag for it to work
When using Vue Router, instead of using <a> tags, you can use <router-link> tags.
Here's a basic example similar to the Vue Router documentation for dynamic route matching.
If you have this...
const router = new VueRouter({
routes: [
{ path: '/Details/:id', component: DetailsPage, name: 'Details' }
]
})
You can have router-links declared in, say, your ArticleListItem.vue file like:
<v-btn dark to="{name:'Details', params: {id: article.id}}">
Click here for more Details
</v-btn>
You can't just go to a generic details page. There needs to be some dynamic part of the route (which I've called id in my example) to specify which article's page to go to.
Edit
An additional problem is in your Router setup:
From the Vue Router docs:
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
...needs to be in your main.js file. The declaration of Vue.use in your index.js file isn't doing anything. Just for conciseness. Make it one file:
WhateverYourOneTrueFileIsGoingToBeCalled.js
import Vue from 'vue'
import App from './App.vue'
import VueRouter from 'vue-router';
import Details from '#/components/Details';
import moment from 'moment'
Vue.config.productionTip = false
Vue.filter('formatDate', function (value) {
if (!value) return ''
return moment(value.toString()).format('MM/DD/YYYY hh:mm')
})
Vue.use(VueRouter)
router = new VueRouter({
routes: [
{
path: '/Details/:id',
name: 'Details',
component: Details
}
]
})
new Vue({
router,
render: h => h(App),
}).$mount('#app')
I try to work with named routes in a Vue app.
Exactly the same setup works totally fine in an other Vue project.
If I click on a named router-link, the just disappears.
If I check the element in the browser, there is an empty comment at the place, where the section should be.
The console isn't showing any errors.
Has anyone seen something similar?
Thank's for every help!
Code:
index.js
import Vue from 'vue';
import Router from 'vue-router';
import Home from '#/components/views/home';
import AskQuestion from '#/components/views/ask-question';
import AddQuestion from '#/components/views/add-question';
import CompleteQuestions from '#/components/views/complete-questions';
import Survey from '#/components/views/survey';
Vue.use(Router);
export default new Router({
routes: [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/ask-question',
name: 'AskQuestion',
component: AskQuestion
},
{
path: '/add-question',
name: 'AddQuestion',
component: AddQuestion
},
{
path: '/complete-questions/:surveyId',
name: 'CompleteQuestions',
component: CompleteQuestions
},
{
path: '/survey/:surveyId',
name: 'Survey',
component: Survey
}
]
});
HTML
<router-link :to="{name: 'survey', params: {surveyId: survey.id}}">
<p class="viewQuestions">View questions</p>
</router-link>
App.vue
<template>
<div id="app">
<navigation></navigation>
<router-view/>
</div>
</template>
<script>
import Navigation from '#/components/generel/navigation';
export default {
name: 'app',
components: {
Navigation
}
};
</script>
<style src="./assets/scss/_general.scss" lang="scss"></style>
in route config , set history mode and then go to your link and click that LINK , and then go check URL address bar
export default new VueRouter({
mode:'history',
......
});
for example if you click on this
<router-link :to="{ name: 'home' }"> Home </router-link>
and your rute is
{
path:'/home',
component:Home,
name:'home'
}
if your route workin well , you must see this url in browser address bar
https://localhost/home
Following some tutorials I have created a simple menu like the following:
HTML
Vue.component('main-menu', {
template: `
<div>
<ul class="uk-tab uk-margin-bottom">
<li v-for="tab in tabs" :class="{ 'uk-active': tab.selected }">
<a #click="selectTab(tab.name)" href="#">{{ tab.name }}</a>
</li>
</ul>
<slot></slot>
</div>
`,
data() {
return {
tabs: []
}
},
created() {
this.tabs = this.$children
},
methods: {
selectTab(name) {
for (tab of this.tabs) {
tab.selected = (tab.name == name)
}
}
},
})
Vue.component('tab', {
props: {
name,
active: {
default: false
},
},
template: `
<div v-show="selected">
<ul v-show="subTabs.length > 1" class="uk-subnav uk-subnav-pill">
<li v-for="subTab in subTabs" :class="{ 'uk-active': subTab.selected }">
<a #click="selectSubTab(subTab.name)" href="#">{{ subTab.name }}</a>
</li>
</ul>
<slot></slot>
</div>
`,
data() {
return {
selected: false,
subTabs: []
}
},
created() {
this.selected = this.active
this.subTabs = this.$children
this.selected = this.active
},
methods: {
selectSubTab(name) {
for (subTab of this.subTabs) {
subTab.selected = (subTab.name == name)
}
}
},
})
Vue.component('sub-tab', {
props: {
name,
active: {
default: false
},
},
template: `
<div v-show="selected">
<slot></slot>
</div>
`,
data() {
return {
selected: false
}
},
created() {
this.selected = this.active
},
})
new Vue({
el: '#root',
})
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Static title for now</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.27.4/css/uikit.gradient.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.5/vue.js"></script>
</head>
<body class="uk-margin uk-margin-left uk-margin-right">
<div id="root">
<main-menu>
<tab name="Tab 1" :active="true">
<sub-tab name="Sub 1A" :active="true">
<div>1A</div>
</sub-tab>
<sub-tab name="Sub 1B">
<div>1B</div>
</sub-tab>
</tab>
<tab name="Tab 2">
<sub-tab name="Sub 2A" :active="true">
<div>2A</div>
</sub-tab>
</tab>
<tab name="Tab 3">
<sub-tab name="Sub 3A" :active="true">
<div>3A</div>
</sub-tab>
<sub-tab name="Sub 3B">
<div>3B</div>
</sub-tab>
</tab>
</main-menu>
</div>
<script src="main.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/uikit/2.27.4/js/uikit.min.js"></script>
</body>
</html>
I wanted to add some routing, so whenever I click tab or sub-tab, it will update address, and other way around, whenever I change the address, it will update the state of the page and show the correct div.
I have read the official documentation for vue-router and looked up some more tutorials, but I cannot figure out how to do it. It seems to be designed the way where different components are shown on different links.
What am I missing?
The setup I use combines vuex and route using vuex-router-sync. It seems like quite a bit of code, and while the example bellow that I grabbed from my notes does not include child/sub categories, it's not too difficult to add these.
// ::::: main.js :::::
import Vue from 'vue';
import store from './store'; // 1 - load vuex
import router from './router'; // 2 - load router
import App from './App.vue';
import { sync } from 'vuex-router-sync'; // 3 - load vuex-router-sync
sync(store, router) // 4 - call synch on vuex and route
new Vue({
el: '#app',
store, // 5 - add vuex to app
router, // 5 - add router to app
components: {
App
}
})
// ::::: store.js :::::
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
const store = new Vuex.Store({
state: {
menu: [{
name: 'Home',
path: '/',
component: require('./routes/home')
},
{
name: 'Page1',
path: '/page1',
component: require('./routes/page1')
},
{
name: 'Page2',
path: '/page2',
component: require('./routes/page2')
},
]
},
getters: {
menu: state => {
return state.menu || []
}
}
})
export default store
// ::::: router.js :::::
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
import HomePage from './routes/home.vue'
import Page1 from './routes/page1.vue'
import Page2 from './routes/page2.vue'
const routes = [{
path: '/',
component: HomePage
},
{
path: '/page1',
component: Page1
},
{
path: '/page2',
component: Page2
}
]
const router = new VueRouter({
// mode: 'history',
mode: 'hash',
linkActiveClass: 'is-active',
scrollBehavior: () => ({
y: 0
}),
routes
})
export default router
<!-- App.vue -->
<template>
<div id="App">
<navigation></navigation>
<router-view class="animated"></router-view>
</div>
</template>
<script>
import Navigation from './components/Navigation'
export default {
components: {
Navigation
}
}
</script>
Navigation
import store from '../store'
import {
mapGetters
} from 'vuex'
export default {
data: function() {
return {
error: {}
}
},
computed: {
...mapGetters([
'routePath'
]),
menu() {
return store.getters.menu
},
}
}
<template>
<div>
NAVIGATION
<ul class="navbar_wrap">
<li v-for="(item, index) in menu" :key="index" :class="routePath === item.path ? 'selected' : 'not-selected'">
<router-link :to="item.path" :exact="true" v-if="item.path">
{{item.name}}
</router-link>
</li>
</ul>
</div>
</template>
the routePath getter is defined in store
routePath: state => {
return state.route.path
}