How to get current name of route in Vue? - vue.js

I want to get the name of the current route of vue-router, i have a component menu with navigation to another componentes, so i want to dispaly the name of the current route.
I have this:
created(){
this.currentRoute;
//this.nombreRuta = this.$route.name;
},
computed:{
currentRoute:{
get(){
this.nombreRuta = this.$route.name;
}
}
}
But the label of the name of the route does not change, the label only show the name of the first loaded route.
Thank You
EDIT:
Image to show what i want

You are using computed incorrectly. You should return the property in the function. See the docs for more information.
Here is your adapted example:
computed: {
currentRouteName() {
return this.$route.name;
}
}
You can then use it like this:
<div>{{ currentRouteName }}</div>
You can also use it directly in the template without using a computed property, like this:
<div>{{ $route.name }}</div>

Vue 3 + Vue Router 4
Update 5/03/2021
If you are using Vue 3 and Vue Router 4, here is two simplest ways to get current name of route in setup hook:
Solution 1: Use useRoute
import { useRoute } from 'vue-router';
export default {
setup () {
const route = useRoute()
const currentRouteName = computed(() => route.name)
return { currentRouteName }
}
}
Solution 2: Use useRouter
import { useRouter } from 'vue-router';
export default {
setup () {
const router = useRouter()
const currentRouteName = computed(() => router.currentRoute.value.name;)
return { currentRouteName }
}
}

I use this...
this.$router.history.current.path

In Composition API, this works
import { useRouter } from 'vue-router'
const router = useRouter()
let currentPathObject = router.currentRoute.value;
console.log("Route Object", currentPathObject)
// Pick the values you need from the object

I used something like this:
import { useRoute } from 'vue-router';
then declared
const route = useRoute();
Finally if you log route object - you will get all properties I used path for my goal.

This is how you can access AND watch current route's name using #vue/composition-api package with Vue 2 in TypeScript.
<script lang="ts">
import { defineComponent, watch } from '#vue/composition-api';
export default defineComponent({
name: 'MyCoolComponent',
setup(_, { root }) {
console.debug('current route name', root.$route.name);
watch(() => root.$route.name, () => {
console.debug(`MyCoolComponent- watch root.$route.name changed to ${root.$route.name}`);
});
},
});
</script>
I will update this answer once Vue 3.0 and Router 4.0 gets released!

I use this...
this.$route.name

In my Laravel app I created a router.js file and I can access the router object in any vue component like this.$route
I usually get the route like this.$route.path

Using composition API,
<template>
<h1>{{Route.name}}</h1>
</template>
<script setup>
import {useRoute} from 'vue-router';
const Route = useRoute();
</script>

Using Vue 3 and Vue Router 4 with Composition API and computed:
<script setup>
import { computed } from 'vue'
import { useRouter } from 'vue-router'
const router = useRouter()
// computed
const currentRoute = computed(() => {
return router.currentRoute.value.name
})
</script>
<template>
<div>{{ currentRoute }}</div>
</template>
⚠ If you don't set a name in your router like so, no name will be displayed:
const routes = [
{ path: '/step1', name: 'Step1', component: Step1 },
{ path: '/step2', name: 'Step2', component: Step2 },
];

In Vue 3.2 using Composition API
<script lang="ts" setup>
import { useRoute } from "vue-router";
const route = useRoute();
const currentRouteName = computed(() => {
return route.name;
});
</script>
<template>
<div>
Using computed:{{currentRouteName}}
or without using computed: {{route.name}}
</div>
</template>

This is how you can get id (name) of current page in composition api (vue3):
import { useRoute } from 'vue-router';
export function useFetchPost() {
const currentId = useRoute().params.id;
const postTitle = ref('');
const fetchPost = async () => {
try {
const response = await axios.get(
`https://jsonplaceholder.typicode.com/posts/${currentId}`
);
postTitle.value = response.data.title;
} catch (error) {
console.log(error);
} finally {
}
};
onMounted(fetchPost);
return {
postTitle,
};
}

I'm using this method on vue 3 & vue-router 4
It works great!
<script>
import { useRoute } from 'vue-router'
export default {
name: 'Home',
setup() {
const route = useRoute();
const routeName = route.path.slice(1); //route.path will return /name
return {
routeName
}
}
};
</script>
<p>This is <span>{{ routeName }}</span></p>

I've Tried and it Worked:
Use Following in Your Elements;
{{ this.$route.path.slice(1) }}

this.$router.currentRoute.value.name;
Works just like this.$route.name.

Vue 3 + Vue Router 4 + Pinia store (or any other place outside of vue components)
#KitKit up there gave an example how to get route if you are using Vue 3 and Vue Router 4 in setup hook. However, what about state management in Pinia store ?
In vue#2 and vue-router#3.5.1: We could have used router.currentRoute.query.returnUrl like so (example in vuex state management):
import router from "#/router";
const state = initialState;
const getters = {};
const actions = { // your actions };
const mutations = {
loginSuccess(state, user) {
let returnUrl = "";
if(router.currentRoute.query.returnUrl != undefined)
returnUrl = router.currentRoute.query.returnUrl;
},
};
export default {
state,
getters,
actions,
mutations,
};
export const authentication = {
actions: {},
mutations: {},
};
In vue#3 and vue-router#4: We have to append value to currentRoute like so:
import router from '#/router';
export const authenticationStore = defineStore('authUser', {
state: (): State => ({
// your state
}),
getters: {
// your getters
},
actions: {
loginSuccess(user: object) {
let returnUrl = '';
if (router.currentRoute.value.query.returnUrl != undefined)
returnUrl = router.currentRoute.value.query.returnUrl;
},
},
});

Related

Easier way to use plugins with the composition api in vue 3

When adding vuex or vue-router as plugin in vue and using the options api you could access these plugins with the this keyword.
main.js
import { createApp } from 'vue';
import i18n from '#/i18n';
import router from './router';
import store from './store';
app.use(i18n);
app.use(store);
app.use(router);
RandomComponent.vue
<script>
export default {
mounted() {
this.$store.dispatch('roles/fetchPermissions');
},
}
</script>
The this keyword is no longer available with the composition api which leads to a lot of repetitive code. To use the store, vue-router or the i18n library I have to import and define the following:
RandomComponent.vue with composition api
<script setup>
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
import { useI18n } from 'vue-i18n';
const router = useRouter();
const store = useStore();
const { t } = useI18n();
const { handleSubmit, isSubmitting, errors } = useForm('/roles/create', role, CreateRoleValidationSchema, () => {
store.dispatch('notifications/addNotification', {
type: 'success',
title: t('create_success', { field: t('role', 1) }),
});
router.push({ name: 'roles' });
});
</script>
Is there a way to avoid these imports and definitions and have a way to easily use these plugins like I could do with the options api?
There is no built-in way of doing that in Composition API and script setup.
What you could do is:
Create a plugins.js file that exports common bindings you want to import. For example:
export * from 'vuex'
export * from 'vue-router'
export * from 'vue-i18n'
And then you have only 1 import to do:
<script setup>
import { useStore, useRouter, useI18n } from './plugins'
const router = useRouter();
const store = useStore();
const { t } = useI18n();
const { handleSubmit, isSubmitting, errors } = useForm('/roles/create', role, CreateRoleValidationSchema, () => {
store.dispatch('notifications/addNotification', {
type: 'success',
title: t('create_success', { field: t('role', 1) }),
});
router.push({ name: 'roles' });
});
</script>
You can go even further by initiating the plugins like:
import { useStore, useRouter, useI18n } from './plugins'
export function initiateCommonPlugins() {
const router = useRouter();
const store = useStore();
const { t } = useI18n();
return { router, store, t }
}
And your code then will look like this:
<script setup>
import { router, store, t } from './initiate-plugins'
const { handleSubmit, isSubmitting, errors } = useForm('/roles/create', role, CreateRoleValidationSchema, () => {
store.dispatch('notifications/addNotification', {
type: 'success',
title: t('create_success', { field: t('role', 1) }),
});
router.push({ name: 'roles' });
});
</script>
Use unplugin-auto-import plugin
This plugin can eliminate all imports you want and is highly customizable. Haven't tried it yet but I have seen people recommend it.
Stick to Options API
Using Vue 3 doesn't mean that you have to use Composition API for creating components. You can use Options API along with script setup for composables instead of Mixins.
So Options API for components, Composition API for reusing code or advanced use-cases.

VUE 3 JS : can't acces to my props in mounted

I have a problem in a component.
I receive an id (name : theIdPost) from a parent file of this component but when I would like to use it in the mounted(){} part , it tells me :
TS2339: Property 'theIdPost' does not exist on type '{...
I can print the id in template, no worries but to use it in the SCRIPT part it doesn't work.
the component file:
<template lang="fr">
// All my html
</template>
<script lang="ts">
import { computed } from 'vue';
import { store } from '../store/index';
export default{
name: 'comment',
props: {
theIdPost: Number,
theTxtPost: String,
theLike: Number,
},
setup() {
const myStore: any = store
const commentList = computed(() => myStore.state.commentList);
console.log("CommentList > " +commentList.value);
return { commentList };
},
mounted() {
const myStore: any = store;
myStore.dispatch("getComments",
{'id': this.theIdPost}
);
}
}
</script>
<style lang="scss">
#import "../scss/variables.scss";
// ..... the style part
</style>
Can you explain me why it doesn't work ?
Thanks
If you are using the composition API with the setup, you have to add the lifecycle hooks differently:
https://v3.vuejs.org/guide/composition-api-lifecycle-hooks.html
setup(props) {
const myStore: any = store
const commentList = computed(() => myStore.state.commentList);
console.log("CommentList > " +commentList.value);
onMounted(() => {
myStore.dispatch("getComments",
{'id': props.theIdPost}
);
})
return { commentList };
},
For Solution there is 2 points :
because I use vue 3 and setup in composition API , the lifecycle Hook is different and mounted => onMounted
setup(props) {
const myStore: any = store
const commentList = computed(() => myStore.state.commentList);
onMounted(() => {
myStore.dispatch("getComments",
{'id': props.theIdPost}
);
})
return { commentList };
},
when we use onMounted, is like when we use ref(), we have to import before. So at the beginning of the SCRIPT part, we have to write :
import { onMounted } from 'vue';
So my final script is :
<script lang="ts">
import { computed, onMounted } from 'vue';
import { store } from '../store/index';
export default {
name: 'comment',
props: {
theIdPost: Number,
theTxtPost: String,
theLike: Number,
},
setup(props) {
const myStore: any = store;
const commentList = computed(() => myStore.state.commentList);
onMounted(() => {
myStore.dispatch("getComments",
{ 'id': props.theIdPost }
);
})
return { commentList };
},
}
</script>
Thanks to Thomas for the beginning of the answer :)
it worked for me too. i was setting up the setup and not pass props in to the setup. now okay

Vue3 route.query empty

Trying to pass route query to axios request, but it is empty..
route.query returns empty in mounted. route.queryreturns {"filter[city]": "Vilnius" } in axios then
nextTick doesn't solve issue. Any tips?
import { ref, onMounted, nextTick } from 'vue';
import axios from 'axios';
import { useRouter, useRoute } from 'vue-router';
export default {
setup() {
const router = useRouter();
const route = useRoute();
onMounted(() => {
console.log(route.query); // log is {}
fetchApartments();
});
function fetchApartments() {
console.log(route.query); // log is {}
axios.get('/api/apartments').then(response => {
console.log(route.query); // log is { "filter[city]": "Vilnius" }
});
}
}
}
Route navigation is asynchronous. You need to wait for router.isReady for queries to be available
import {useRouter, useRoute} from 'vue-router';
export default {
setup() {
const router = useRouter();
const route = useRoute();
onMounted(async () => {
await router.isReady();
console.log(route.query);
});
}
}
Update your code like this:
...
import { computed } from 'vue'
...
and inside setup()
const route = useRoute();
const query = computed(() => route.query)
The missing part here is computed property.

Dynamic layout in Vue 3 with Vite

New to Vue and Vite but trying to get dynamic layouts working properly here. I believe I have what is needed but the issue it the meta seems to always come up as an empty object or undefined.
AppLayout.vue
<script setup lang="ts">
import AppLayoutDefault from './stub/AppLayoutDefault.vue'
import { markRaw, watch } from 'vue'
import { useRoute } from 'vue-router'
const layout = markRaw(AppLayoutDefault)
const route = useRoute()
console.log('Current path: ', route.path)
console.log('Route meta:', route.meta)
watch(
() => route.meta,
async (meta) => {
try {
const component = await import(`./stub/${meta.layout}.vue`)
layout.value = component?.default || AppLayoutDefault
} catch (e) {
layout.value = AppLayoutDefault
}
},
{ immediate: true }
)
</script>
<template>
<component :is="layout"> <router-view /> </component>
</template>
App.vue
<script setup lang="ts">
import AppLayout from '#/layouts/AppLayout.vue'
</script>
<template>
<AppLayout>
<router-view />
</AppLayout>
</template>
Each and every route has the appropriate meta set with a property called layout.
I just can't seem to get he layout applied correctly on the first load or any click of a link in the navbar(which are just router-link) for that matter.
The main problem is layout is initialized as markRaw(), which is not a reactive ref:
const layout = markRaw(AppLayoutDefault) // ❌ not reactive
Solution
Initialize layout as a ref.
Instead of watching the full route object, watch route.meta?.layout because that's the only relevant field for the handler.
Wrap layout's new values with markRaw() to avoid reactivity on the component definition.
const layout = ref() 1️⃣
watch(
() => route.meta?.layout as string | undefined, 2️⃣
async (metaLayout) => {
try {
const component = metaLayout && await import(/* #vite-ignore */ `./${metaLayout}.vue`)
layout.value = markRaw(component?.default || AppLayoutDefault) 3️⃣
} catch (e) {
layout.value = markRaw(AppLayoutDefault) 3️⃣
}
},
{ immediate: true }
)
demo
The solution from Tony is great. But you need to add computed inside the watch because it will prevent code execution for the very first "layout" variable initialized which is still undefined and it will cause unnecessary rendering. So, please add the computed function inside the watch function. Also you need to watch "route.path" not the "route.meta?.layout".
import DefaultLayout from '#/layouts/Default.vue'
import { markRaw, ref } from '#vue/reactivity'
import { computed, watch } from '#vue/runtime-core'
import { useRoute } from 'vue-router'
const layout = ref()
const route = useRoute()
watch(
computed(() => route.path), async () => {
let metaLayout = route.meta?.layout
try {
const metaLayoutComponent = metaLayout && await import(`./layouts/${metaLayout}.vue`)
layout.value = markRaw(metaLayoutComponent?.default || DefaultLayout)
} catch (error) {
layout.value = markRaw(DefaultLayout)
}
}
);

How to use Vuex mapGetters with Vue 3 SFC Script Setup syntax?

I'm refactoring component from regular Vue 3 Composition API to Script Setup syntax. Starting point:
<script lang="ts">
import { defineComponent, computed } from 'vue';
import { mapGetters } from 'vuex';
export default defineComponent({
name: 'MyCoolBareComponent',
computed: {
...mapGetters('auth', ['isAdmin']),
},
});
</script>
Current Vue v3 migration documentation, SFC Composition API Syntax Sugar (< script setup >), links to this RFC page: https://github.com/vuejs/rfcs/pull/182
There is only one example for using computed reactive property:
export const computedMsg = computed(() => props.msg + '!!!')
As there is no current Vuex 4 documentation available that is mentioning <scrip setup>, it remains unclear to me how I should be using mapGetters when using this syntax? Or what is the correct way of going about this with Vuex 4?
tldr: scroll down to final result
There is now better documentation and the simple answer is: You don't need mapGetters but you can implement it yourself.
https://next.vuex.vuejs.org/guide/composition-api.html#accessing-state-and-getters
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
const store = useStore()
const count = computed(() => store.getters.count)
</script>
If you have many getters you want to turn into a "computed property" you could use something as "intuitive" as this:
const { countIsOdd, countIsEven } = Object.fromEntries(Object.keys(store.getters).map(getter => [getter, computed(() => store.getters[getter])]))
Put that into a function and it even looks nice.
const mapGetters = (getters) => {
return Object.fromEntries(Object.keys(getters).map(getter => [getter, computed(() => getters[getter])]))
}
const { countIsOdd, countIsEven } = mapGetters(store.getters)
Put that function into a file and export it as a module...
// lib.js
import { computed } from 'vue'
import { useStore } from 'vuex'
const mapGetters = () => {
const store = useStore()
return Object.fromEntries(Object.keys(store.getters).map(getter => [getter, computed(() => store.getters[getter])]))
}
export { mapGetters }
...and you can easily use it in all your components.
// components/MyComponent.vue
<script setup>
import { mapGetters } from '../lib'
const { countIsOdd, countIsEven } = mapGetters()
</script>
Final result:
Here's the final lib.js I came up with:
import { computed } from 'vue'
import { useStore } from 'vuex'
const mapState = () => {
const store = useStore()
return Object.fromEntries(
Object.keys(store.state).map(
key => [key, computed(() => store.state[key])]
)
)
}
const mapGetters = () => {
const store = useStore()
return Object.fromEntries(
Object.keys(store.getters).map(
getter => [getter, computed(() => store.getters[getter])]
)
)
}
const mapMutations = () => {
const store = useStore()
return Object.fromEntries(
Object.keys(store._mutations).map(
mutation => [mutation, value => store.commit(mutation, value)]
)
)
}
const mapActions = () => {
const store = useStore()
return Object.fromEntries(
Object.keys(store._actions).map(
action => [action, value => store.dispatch(action, value)]
)
)
}
export { mapState, mapGetters, mapMutations, mapActions }
Using this in the component looks like this:
<template>
Count: {{ count }}
Odd: {{ counterIsOdd }}
Even: {{ counterIsEven }}
<button #click="countUp">count up</button>
<button #click="countDown">count down</button>
<button #click="getRemoteCount('https://api.countapi.xyz')">
get remote count
</button>
</template>
<script setup>
import { mapState, mapGetters, mapMutations, mapActions } from '../lib'
// computed properties
const { count } = mapState()
const { countIsOdd, countIsEvent } = mapGetters()
// commit/dispatch functions
const { countUp, countDown } = mapMutations()
const { getRemoteCount } = mapActions()
</script>
Any feedback on this would be very appreciated.
So far this syntax seems to be working. However, I'm hoping that Vuex would develop a cleaner way for exposing computed getters for template.
If you know a better way, we'd love to hear!
<script setup lang="ts">
import { mapGetters } from 'vuex';
export const name = 'MyCoolBareComponent';
export default {
computed: {
...mapGetters('user', ['profile', 'roles']),
},
};
</script>
import {useStore} from "vuex";
import {computed} from "vue";
const {getEvents, getSelectedTag} = useStore().getters;
const events = computed(() => getEvents)
const selectedTag = computed(() => getSelectedTag)
i do this and for me is working
You don't need to export anything, an SFC will register all variables and components for you and make them available in template.
An SFC automatically infers the component's name from its filename.
Here are a few examples that may be useful:
<script setup>
import { computed } from 'vue'
import { useStore } from 'vuex'
import MyComponent from './components/MyComponent'
const store = useStore()
const data = 'Random string as a data'
// without module/data
const myAction = () => store.dispatch('myAction')
// with data
const mySecondAction = () => store.dispatch('mySecondAction', data)
// with module
const myMutation = () => store.commit('moduleName/myMutation')
// with module/data
const myNewMutation = () => store.commit('moduleName/myNewMutation', data)
const myStateVariable = computed(() => store.state.myStateVariable)
// with module
const myGetter = computed(() => store.getters.moduleName.myGetter)
// replace using of mapState/mapGetters
const state = computed(() => store.state)
// and then
console.log(state.myStateVariable)
console.log(state.mySecondStateVariable)
....
</script>
You can do something like this
import { mapGetters } from "vuex"
setup() {
return {
...mapGetters("myModule", ["doSomething"])
}
}
Follow this:
https://vuex.vuejs.org/guide/typescript-support.html#typing-usestore-composition-function
Here is an example:
store.ts
import { InjectionKey } from 'vue'
import { createStore, Store } from 'vuex'
// define your typings for the store state
export interface State {
token: string|null
}
// define injection key
export const key: InjectionKey<Store<State>> = Symbol()
export const store = createStore<State>({
state: {
token: localStorage.getItem('token') ? localStorage.getItem('token'):'',
}
})
main.js
import { store, key } from './store'
import { createApp } from 'vue'
import App from './App.vue'
const app = createApp(App)
// pass the injection key
app
.use(store, key)
.mount('#app')
In a vue component
<script setup>
import { onMounted } from 'vue'
import { useStore } from 'vuex'
import { key } from './store'
const token = useStore(key)
onMounted(() => {
console.log(store.state.token)
})
</script>