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>
Related
I got Vue2 app with vue-router with routings configured like that:
export default {
path: "/",
redirect: "/dashboard",
component: AdminLayout,
meta: {
requiresAuth: true
},
children: [
{
path: "/dashboard",
name: "Dashboard",
component: Dashboard
},
{
path: "/add/user",
name: "InviteUser",
component: InviteUser
},
{
path: "/groups",
name: "Groups",
component: Groups
},
...
In app, we got two different types of users - admin and normal user. Some of those routings should be accessible for both, but the problem is that user should see different layout base on its type (permission) - AdminLayout for admins and UserLayout for normal users.
Is there any way to show app which template should user see based on boolean from vuex with keeping route path?
on /dashboard admin will see dashboard with AdminLayout
on /dashboard normal user will see dashboard with UserLayout
My main routing cofig:
import Vue from "vue";
import VueRouter from "vue-router";
import SessionRoutes from "./session.js";
import AdminRoutes from "./admin.js";
import defaultRoutes from "./default";
Vue.use(VueRouter);
const routes = [AdminRoutes, SessionRoutes, defaultRoutes];
const router = new VueRouter({
mode: "history",
routes
});
export default router;
you can set a condition in the layout part on the current page for example for when you use nuxt:
<script>
export default {
layout: (ctx) => (ctx.isAdmin ? 'adminLayout' : 'userLayout'),
}
</script>
but I opine you don't use Nuxt.js and I think below solution is suitable for your question:
Using the meta-object in our route
set dynamic component on app.vue page
code for about.vue page
import Home from '../views/Home.vue'
import About from '../views/About.vue'
import LayoutA from '../layouts/LayoutA.vue'
import LayoutB from '../layouts/LayoutB.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home,
meta: { layout: LayoutA }
},
{
path: '/about',
name: 'About',
component: About,
meta: { layout: LayoutB }
}
]
const router = new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes
})
export default router;
<!--app.vue page -->
<template>
<div id="app">
<component :is="this.$route.meta.layout || 'div'">
<router-view />
</component>
</div>
</template>
<script>
export default {
name: "App",
};
</script>
<!--about page-->
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<script>
export default {
name: "About"
};
</script>
To get a different layout working for the dashboard and all child routes your layouts have to use the router view component. I use a wrapper component to handle this in one of my projects following this approach.
Excerpt from my router config:
…
{
component: MainFrame,
name: 'main',
path: '/',
redirect: 'dashboard',
children: [
{
alias: 'dashboard',
component: Dashboard,
name: 'dashboard',
path: '', // Default child route → be equivalent to /dashboard
},
{
path: "add/user",
name: "InviteUser",
component: InviteUser
},
{
path: "groups",
name: "Groups",
component: Groups
},
],
},
…
In your MainFrame component you can wrap your layout in a dynamic component. So you can switch your layout easely based on a Vuex getter:
MainFrame.vue
<template>
<component :is="layout">
<!-- you can use slots too if you like -->
</component>
</template>
<script>
import AdminLayout from './AdminLayout'
import UserLayout from './UserLayout'
export default {
computed: {
layout() {
return this.$store.getters['isAdminUser'] ? AdminLayout : UserLayout
}
}
}
</script>
And your layouts have to use <router-view />. It works as a wrapper for all nested routes and therefore you have only one place to handle the layout for the dashboard and child pages:
AdminLayout.vue
<template>
<div class="auth-view">
<header>…</header>
<aside>…</aside>
<main>
<transition name="fade" mode="out-in">
<router-view />
</transition>
</main>
<footer>…</footer>
</div>
</template>
UserLayout.vue
<template>
<div class="guest-view">
<router-view />
</div>
</template>
EDIT:
This approach can be used for deeper nesting too. Assuming your user route should have another child routes you can solve this on the router config using a <router-view /> within a render function:
…
// Let's split down user related routes
{
component: { render: (h) => h('router-view') },
name: 'usersView',
path: 'users',
redirect: 'ListUsers',
children: [
// Show users
{
path: '', // route: /users/
name: 'ListUsers',
component: ListUsers
},
// Show a single user
{
path: ':id', // route: /users/{userId}
name: 'ViewUser',
component: ViewUser
},
// Invite new users
{
path: 'add', // route: /users/add
name: 'InviteUser',
component: InviteUser
},
]
}
…
Sorry for the heavy text. All of my router-views work, except for one, which shows blank. I do not see any console errors of warnings and the format is identical between views - the only difference is the template. This was working, but I started a new project because my package.json and dependencies got messy. I've read through the code ad nauseum and I just can't work out why it wont show. The code is condensed as there's lots. If you prefer, here's a link to a sandbox: https://codesandbox.io/s/condescending-monad-5o8qw
<template>
<div class="review-movie-detail">
<div class="movie-image">
<img :src="(`https://image.tmdb.org/t/p/original/${movie.poster_path}`)" alt="Movie Poster" />
</div>
<table class="movie-rating-details">
<tr> <h2>{{movie.original_title}}</h2> </tr>
<p> </p>
<tr>Gore rating: <span class="emojiRatings" >{{getGoreEmoji()}} </span></tr>
<tr><input v-model = "goreRating" type="range" min="1" max="100" class="slider" id="myRange"></tr>
<tr> <div class="star-rating"> <star-rating v-model="rating"> </star-rating></div></tr>
<tr><b-button class="block-button">Submit review</b-button></tr>
</table>
</div>
</template>
<script>
import { ref, onBeforeMount } from 'vue';
import env from '#/env.js'
import { useRoute } from 'vue-router';
import StarRating from 'vue-star-rating'
export default {
components : {StarRating},
setup () {
const movie = ref({});
const route = useRoute();
onBeforeMount(() => {
fetch(`https://api.themoviedb.org/3/movie/${route.params.id}?api_key=${env.apikey}`)
.then(response => response.json())
.then(data => {
movie.value = data;
});
});
return {
movie
}
},
data() {
return {
goreRating: '50',
shockRating : '50',
jumpRating: '50',
plotRating: '50',
supernaturalRating: '50',
rating: '3.5'
}
},
methods: {
getGoreEmoji() {
let emojiRating = ["🩸", "🩸🩸", "🩸🩸🩸", "🩸🩸🩸🩸", "🩸🩸🩸🩸🩸", "🩸🩸🩸🩸🩸🩸"]
return emojiRating[(Math.floor(this.goreRating/20))]
},
}
}
and here is my router:
import { createRouter, createWebHistory } from 'vue-router'
import Home from '../views/Home.vue'
import MovieDetail from '../views/MovieDetail.vue'
import ReviewMovie from '../views/ReviewMovie.vue'
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/movie/:id',
name: 'Movie Detail',
component: MovieDetail
},
{
path: '/movie/:id/review',
name: 'Review Movie',
component: ReviewMovie
}
]
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
})
export default router
and app.Vue to display the router view...
<template>
<div>
<header>
<GoBack />
<router-link to="/">
<h1><span>Horror</span>Hub</h1>
</router-link>
</header>
<main>
<router-view></router-view>
</main>
</div>
</template>
<script>
import GoBack from "#/./components/GoBack"
export default {
components: {
GoBack
}
}
</script>
How can I find the root cause of this issue (pun intended)?
As you are using Vue 3, you need to use vue-star-rating#next
npm install vue-star-rating#next
or
yarn add vue-star-rating#next
in package.json, it should be
"vue-star-rating": "^2.1.0"
and use the new new syntax as well
<star-rating v-model:rating="rating"></star-rating>
Here is the working codesandbox:
https://codesandbox.io/s/little-sea-xccm4?file=/src/views/ReviewMovie.vue
hi im using laravel with vuejs2
and this is my App.vue code
<template>
<div>
<Navbar></Navbar>
<Applications></Applications>
{{page_title}}
<router-view></router-view>
</div>
</template>
<script>
import Navbar from '../components/Navbar'
import Applications from '../components/Applications'
export default {
name: 'App',
components: {
Navbar, Applications
},
data: function () {
return {
page_title: 'Main',
}
}
}
</script>
every thing working so good so far ..
so in my routes.js i did this code
{ path: '/settings', component: Settings},
and in my Settings.Vue i did this code
<template>
<div>
setting components
{{page_title}}
</div>
</template>
<script>
export default {
props: ['page_title'],
name: 'Settings',
}
</script>
but i cant access page_title from the props what i want i access page_title from App.vue in Settings.vue
thanks a lot
You must pass data to <router-view>:
const Page = {
template: "<div>Title: {{page_title}}</div>",
props: ["page_title"]
};
const router = new VueRouter({
routes: [{
path: "/",
component: Page
}]
});
const app = new Vue({
router,
template: "<router-view :page_title='page_title'></router-view>",
data() {
return {
page_title: "It's magic!"
};
}
}).$mount("#app");
<script src="https://unpkg.com/vue#2.6.11/dist/vue.min.js"></script>
<script src="https://unpkg.com/vue-router#3.1.6/dist/vue-router.min.js"></script>
<div id="app"></div>
I've looked at the various other questions on Stackoverflow regarding routes not working, and none of them point to why my routes aren't working.
Here is my router:
import Vue from 'vue'
import Router from 'vue-router'
import Foo from '#/components/Forms/foo'
import Bar from '#/components/Forms/bar'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/foo',
component: Foo
},
{
path: '/bar',
component: Bar
}
]
})
Here foo:
<template id="foo">
<form name="basic-form" v-on:submit="submit">
<fieldset>
<legend>User</legend>
<ul>
<li>
<label>First Name</label>
<input v-model="firstName" name="first-name" />
</li>
</ul>
</fieldset>
<button type="submit">Submit</button>
</form>
</template>
<script>
export default {
name: 'foo',
data: function () {
return {
firstName
}
},
methods: {
submit: function (event) {
event.preventDefault()
console.log(this.$data.firstName)
}
}
}
</script>
Here is bar:
<template id="createDb">
<button v-on:click="createDb">Create Database</button>
</template>
<script>
export default {
name: 'CreateDb'
}
</script>
It's worth noting that each of these components work on their own if I set the path to the root /. Essentially, no matter what is typed in - it navigates to the root and thus will grab and use any component that is associated with the root. I'm lost as to why this is.
I friend of mine gave me the answer on twitter, adding the mode: 'history' to the router makes it work as expected, here it is updated:
export default new Router({
mode: 'history',
routes: [
{
path: '/foo',
component: Foo
},
{
path: '/bar',
component: Bar
}
]
})
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
}