I'm trying to render my nested routes in Vue 3 using Vue Router 4.
routes/index.ts
import { createRouter, createWebHistory } from "vue-router";
import {NavbarLayout} from "#/layouts/NavbarLayout";
import AuthRoutes from "#/router/children/AuthRoutes";
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: "/",
component: NavbarLayout,
// children: AuthRoutes,
},
],
});
export default router;
App.ts
import { defineComponent, h } from "vue";
import { VApp, VMain } from "vuetify/components";
export const App = defineComponent({
name: "App",
setup() {
return function render() {
// "Hello" is printed to the screen
return h(VApp, h(VMain, () => ["Hello", h("router-view")]));
};
},
});
NavbarLayout.ts
import { defineComponent, h } from "vue";
import { VContainer } from "vuetify/components";
import { useRoute } from "vue-router";
export const NavbarLayout = defineComponent({
name: "NavbarLayout",
setup() {
const route = useRoute();
// The alert pop up is not shown as well
alert("it should trigger this alert");
return function render() {
// "sub router" is not printed to the screen
return h(VContainer, () => ["sub router", h("router-view", { key: route.path })]);
};
},
});
The HTML being rendered
I have tried several combinations but so far nothing seems to work. Anyone has a clue on why the nested route is not being rendered?
Related
I created a project with Vite, Pinia and Vue-router. Everything works perfectly in development, but when I access the build, only the main path works. All other redirects return 404:
"Failed to load resource: the server responded with a status of 404 ()"
"crbug/1173575, non-JS module files deprecated.
(anonymous) # VM10:6789"
Any idea what could be happening?
*** Main.js ***
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
// Font Awesome
import { library } from '#fortawesome/fontawesome-svg-core'
import { fas } from '#fortawesome/free-solid-svg-icons'
import { far } from '#fortawesome/free-regular-svg-icons'
import { fab } from '#fortawesome/free-brands-svg-icons'
import { FontAwesomeIcon } from '#fortawesome/vue-fontawesome'
//Router
import router from './router'
//Pinia
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
library.add(fas, far, fab);
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
createApp(App)
.use(pinia)
.use(router)
.component('fa', FontAwesomeIcon)
.mount('#app')
*** App.vue ***
<script setup>
import { RouterView } from "vue-router";
</script>
<template>
<RouterView />
</template>
*** router/index.js ***
import { createRouter, createWebHistory } from 'vue-router'
import { useAuthStore } from '../stores/AuthStore';
const router = createRouter({
history: createWebHistory(),
routes: [
{
path: "/katriumweb/login",
name: "login",
component: () => import("#/views/Login.vue")
},
{
path: "/katriumweb/playground",
name: "playground",
component: () => import("#/views/Playground.vue")
},
{
path: "/katriumweb/",
name: "home",
component: () => import("#/views/Home.vue"),
meta: {
authRequired: true
}
},
{
path: "/katriumweb/vehicleupdate",
name: "vehicleupdate",
component: () => import("#/views//workflows/VehicleUpdate.vue"),
meta: {
authRequired: true
}
}
],
});
router.beforeEach(async (to, from, next) => {
const authStore = useAuthStore();
let token = authStore.user? authStore.user.TOKEN : false;
const checkToken = await fetch("*******", {
method: "GET",
headers: {
"Token": `${token}`
}
})
if (to.meta.authRequired) {
if (!checkToken.ok || !token) {
localStorage.clear();
next("/katriumweb/login");
} else {
next();
}
} else {
next();
}
})
export default router;
*** vite.config.js ***
import { fileURLToPath, URL } from "node:url";
import { defineConfig } from "vite";
import vue from "#vitejs/plugin-vue";
// https://vitejs.dev/config/
export default defineConfig({
base: "/katriumweb/",
plugins: [vue()],
resolve: {
alias: {
"#": fileURLToPath(new URL("./src", import.meta.url)),
},
},
});
Since the app itself only has an index.html file and everything else is done via javascript, when you navigate to /mypage it tries to grab another html file.
The Vue Router createWebHistory works this way. A simple fix is to use createWebHashHistory, which uses a hash in order to create the routing.
Otherwise, more solutions are available on the documentation (eg. Netlify supports a redirect property to handle this).
The docs: https://router.vuejs.org/guide/essentials/history-mode.html
I have a UserStore which contains some information about the current user. This store also is responsible for loggin in and out.
In order to make the getters available I map the getters to my computed attribute within my Vue component.
Unfortunately I get an error saying that it cannot access useUserStore before initilization.
This is my component:
<template>
//...
</template>
<script>
import {mapState} from "pinia"
import {useUserStore} from "../../stores/UserStore.js";
import LoginForm from "../../components/forms/LoginForm.vue";
export default {
name: "Login",
components: {LoginForm},
computed: {
...mapState(useUserStore, ["user", "isAuthenticated"]) //commenting this out makes it work
}
}
</script>
This is my store:
import { defineStore } from 'pinia'
import {gameApi} from "../plugins/gameApi.js"
import {router} from "../router.js";
export const useUserStore = defineStore("UserStore", {
persist: true,
state: () => ({
authenticated: false,
_user: null
}),
getters: {
user: (state) => state._user,
isAuthenticated: (state) => state.authenticated
},
actions: {
async checkLoginState() {
// ...
},
async loginUser(fields) {
// ...
},
async logutUser() {
// ...
}
}
})
And my main.js
import {createApp} from 'vue'
import App from './App.vue'
import gameApi from './plugins/gameApi'
import {router} from './router.js'
import store from "./stores/index.js";
createApp(App)
.use(store)
.use(router)
.use(gameApi)
.mount('#app')
And finally my store configuration:
import {createPinia} from "pinia"
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import {useUserStore} from "./UserStore.js";
const piniaStore = createPinia()
piniaStore.use(piniaPluginPersistedstate)
export default {
install: (app, options) => {
app.use(piniaStore)
const userStore = useUserStore()
const gameStore = useGameStore()
}
}
I wasn't able to initialize the store using the "old" way comparable with Vuex. Instead I had to make use of the setup() function Pinia Docu:
<script>
import {useUserStore} from "../../stores/UserStore.js";
export default {
name: "RegisterForm",
setup() {
// initialize the store
const userStore = useUserStore()
return {userStore}
},
data() {
return {
// ...
}
},
methods: {
checkLoginState() {
this.userStore.checkLoginState()
}
}
}
</script>
I am using Vue 3 including the Composition API and additionally Pinia as State Management.
In the options API there is a method beforeRouteEnter, which is built into the component itself. Unfortunately this method does not exist in the composition API. Here the code, which would have been in the beforeRouteEnter method, is written directly into the setup method. However, this means that the component is loaded and displayed first, then the code is executed and, if the check fails, the component is redirected to an error page, for example.
My idea was to make my check directly in the route configuration in the beforeEnter method of a route. However, I don't have access to the Pinia Store, which doesn't seem to be initialized yet, although it is called before in the main.js.
Console Log
Uncaught Error: [🍍]: getActivePinia was called with no active Pinia. Did you forget to install pinia?
const pinia = createPinia()
app.use(pinia)
This will fail in production.
Router.js
import { useProcessStore } from "#/store/process";
const routes: Array<RouteRecordRaw> = [
{
path: "/processes/:id",
name: "ProcessView",
component: loadView("ProcessView", "processes/"),
beforeEnter: () => {
const processStore = useProcessStore();
console.log(processStore);
},
children: [
{
path: "steer",
name: "ProcessSteer",
component: loadView("ProcessSteer", "processes/")
},
{
path: "approve/:code",
name: "ProcessApprove",
component: loadView("ProcessApprove", "processes/")
}
]
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
});
export default router;
main.js
import { createApp } from "vue";
import "#/assets/bundle-bootstrap.css";
import App from "#/App.vue";
import { createPinia } from "pinia";
import router from "#/router";
import SvgIcon from "#/components/SvgIcon.vue";
const pinia = createPinia();
const app = createApp(App);
app.use(pinia);
app.use(router);
app.component("SvgIcon", SvgIcon);
router.isReady().then(() => {
app.mount("#app");
});
However, I don't have access to the Pinia Store, which doesn't seem to be initialized yet, although it is called before in the main.js
Before what? Pinia instance is created with const pinia = createPinia(); after the router module is imported - while it is imported, all side-effects including the call to createRouter() are executed. Once the router is created it begins it's initial navigation (on client - on server you need to trigger it with router.push()) - if you happen to be at URL matching the route with guard that is using Pinia store, the useProcessStore() happens before Pinia is created...
Using a store outside of a component
You have two options:
either you make sure that any useXXXStore() call happens after Pinia is created (createPinia()) and installed (app.use(pinia))
or you pass the Pinia instance into any useXXXStore() outside of component...
// store.js
import { createPinia } from "pinia";
const pinia = createPinia();
export default pinia;
// router.js
import pinia from "#/store.js";
import { useProcessStore } from "#/store/process";
const routes: Array<RouteRecordRaw> = [
{
path: "/processes/:id",
name: "ProcessView",
component: loadView("ProcessView", "processes/"),
beforeEnter: () => {
const processStore = useProcessStore(pinia ); // <-- passing Pinia instance directly
console.log(processStore);
},
},
];
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes
});
export default router;
// main.js
import { createApp } from "vue";
import App from "#/App.vue";
import store from "#/store.js";
import router from "#/router";
const app = createApp(App);
app.use(store);
app.use(router);
router.isReady().then(() => {
app.mount("#app");
});
Hope this would be helpful.
Vue provide support for some functions in which we need store(outside of the components).
To fix this problem I just called the useStore() function inside the function provided by Vue(beforeEach) and it worked.
Reference : https://pinia.vuejs.org/core-concepts/outside-component-usage.html
Example :
import { useAuthStore } from "#/stores/auth";
.
.
.
.
const router = createRouter({
history: createWebHistory(process.env.BASE_URL),
routes,
});
router.beforeEach(async (to, from) => {
const authStore = useAuthStore();
// use authStore Here
});
I have same problem to access the store in "beforeEach" method for managing authorization.
I use this method in main.js, not in router.js. in router.js store is not accessible.
create pinia instance in piniCreate.js
//piniaCreate.js
import { createPinia } from "pinia";
const pinia = createPinia();
export default pinia;
after that create my store in mainStore.js
import { defineStore } from 'pinia'
export const mainStore = defineStore('counter', {
state: () => {
return {
user: {
isAuthenticated: isAuthen,
}
}
},
actions: {
login(result) {
//...
this.user.isAuthenticated = true;
} ,
logOff() {
this.user.isAuthenticated = false;
}
}
});
Then I used beforeEach method in the main.js
//main.js
import { createApp } from 'vue'
import App from './App.vue'
import pinia from "#/stores/piniaCreate";
import { mainStore } from '#/stores/mainStore';
import router from './router'
const app = createApp(App)
.use(pinia)
.use(router)
const store1 = mainStore();
router.beforeEach((from) => {
if (from.meta.requiresAuth && !store1.user.isAuthenticated) {
router.push({ name: 'login', query: { redirect: from.path } });
}
})
app.mount('#app');
You can pass the method in the second parameter of definestore:
store.js
export const useAppStore = defineStore('app', () => {
const state = reactive({
appName: 'App',
appLogo: ''
})
return {
...toRefs(state)
}
})
router.js
router.beforeEach((to, from, next) => {
const apppStore = useAppStore()
next()
})
I have resolved this by adding lazy loading
const routes = [
{
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')
}
]
http://localhost:3000/apartamentai?filter[city]=Vilnius
import { ref, onMounted } from 'vue';
import { useRouter, useRoute } from 'vue-router';
export default {
setup() {
const router = useRouter();
const route = useRoute();
console.log(router);
console.log(route);
onMounted(() => {
console.log(router);
console.log(route);
});
}
}
I get 4 undefined. What's wrong?
https://next.router.vuejs.org/guide/advanced/composition-api.html
You have to create and register the router in your app:
import { createApp } from 'vue';
import { createRouter, createWebHistory } from 'vue-router';
const router = createRouter({
mode: 'history',
history: createWebHistory(),
routes: [],
});
createApp({})
.use(router)
.mount('#app');
as taken from here:
https://next.router.vuejs.org/guide/#router-view
I use vue.js and I try to set a parameter id in axios.get request and I can't understand how exactly to do it
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import Movie from './views/Movie.vue'
Vue.use(Router)
export default new Router({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
name: 'home',
component: Home
},
{
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')
},
{
path: '/movie/:m_id',
name: 'movie',
component: Movie
}
]
})
import Navbar from '../components/Navbar'
import axios from "axios"
export default {
components:{
Navbar
},
data () {
return {
movi: null,
}
},
mounted () {
axios
.get(`https://api.themoviedb.org/3/movie/${m_id}?api_key=7bc75e1ed95b84e912176b719cdeeff9&language=en-US`)
.then(response => (this.movi= response.data))
}
}
I am trying to pass to axios this id of the page to get information about that specific movie and I got stuck.
Any help?
You can try this to use your params from the URL:
// Retrieve the `m_id` param from the `this.$route.params` object:
this.$route.params.m_id
For more info see https://router.vuejs.org/api/#route-object-properties
#How can I do the same thing but in Vuex
import Vue from 'vue'
import Vuex from 'vuex'
import Axios from 'axios';
import router from './router'
Vue.use(Vuex)
Vue.use(Axios)
Vue.use(router)
export default new Vuex.Store({
// data() {
// return {
// m_id:this.$route.params.m_id
// }
// },
// m_id : this.$route.params.m_id,
state: {
video_key: [],
},
mutations: {
updateInfo (state , video_key){
state.video_key = video_key
}
},
getters:{
m_id : this.route.params.m_id
},
actions: {
fetchData({commit,getters}){
axios.get(`https://api.themoviedb.org/3/movie/${this.m_id}/videos?api_key=7bc75e1ed95b84e912176b719cdeeff9&language=en-US`)
.then(response =>{
commit('updateInfo',response.data.results[0].key)
})
}
}
})