Is it possible to prevent a route change within a Vue Component (not within my router file)?
My situation uses the same component, but the URL changes (/users/1 -> /users/2)
Vue.extend({
data: () => ({
active: true
}),
beforeRouteLeave(to, from, next){
if (this.active) {
// do not redirect
} else {
next();
}
}
})
My understanding is that this doesn't work when navigating the URL but the View/Component stays the same.
I need to use beforeRouteUpdate instead of beforeRouteLeave as stated in the docs
beforeRouteUpdate(to, from, next) {
if (this.active) {
next(false);
} else {
next();
}
},
If I'm using beforeRouteUpdate route url path in the browser not getting update, so I used beforeRouteLeave which is updating url and stop reloading same page.
beforeRouteLeave(to, from, next) {
if (this.active) {
next(false);
} else {
next();
}
}
newbie Nuxt question:
I try to redirect to the same route with a different param, and have no idea how to use $nuxt to push the route so I tried the below vue router.
The url is changed but mounted hook does not get invoked to change the data
methods: {
changeMatch(event) {
this.$router.push({
name: "tran-dau-id-tuong-thuat",
params: { id: event.target.value },
});
},
Update: I follow https://router.vuejs.org/guide/essentials/dynamic-matching.html#reacting-to-params-changes
watch: {
$route(to, from) {
if (to.params.id != from.params.id) {
this.loadFixture();
}
},
},
You have to watch the router param for changes.
watch: {
id(newId) {
// has changed call mounted logic again ...
this.getDataAgainLogic(newId) ...
}
}
Vue Router Documentation: Reacting to Params Changes
I have created a module to grab api data and hold it for a vue component:
import Vue from 'vue';
export default {
namespaced: true,
state() {
return {
all: []
}
},
getters: {
all(state) {
return state.all;
}
},
mutations: {
all(state, data) {
Vue.set(state, 'all', data);
},
clear(state) {
Vue.set(state, 'all', []);
}
},
actions: {
async init({ commit, state }) {
let response = await this.axios.get('/api/v1/teams/', {
params: {
includes: 'users'
}
});
if (response.status === 200) {
let data = response.data.data.filter(function (element) {
return element.users.length > 0;
});
commit('userTeamSelect/all', data, {root: true});
} else {
Vue.toasted.error('Could not fetch team data', {
duration: 10000
});
}
}
}
}
I have a component that is dynamically registering the module when it is created and removing the module from the store before it is deleted:
created() {
if (this.$store.getters['userTeamSelect/all'] === undefined) {
this.$store.registerModule('userTeamSelect', UserTeamSelectModule);
this.$store.dispatch('userTeamSelect/init');
}
},
beforeDestroy() {
if (this.$store.getters['userTeamSelect/all'] !== undefined) {
this.$store.commit('userTeamSelect/clear');
this.$store.unregisterModule('userTeamSelect');
}
},
When I navigate to a page where this component is used, I get the following error message in the console:
[vuex] unknown mutation type: userTeamSelect/all
This mutation is only called within the init action of the dynamic module, so the problem must be there somewhere. I have tried calling it both locally commit('all', data) and globally commit('userTeamSelect/all', data, {root:true}) with no luck.
The module must have been added to the vuex store because I can call the action without issue. It just seems the mutation isn't being registered somehow? I would expect it to work fine, or recieve an error about an unknown action.
Can anyone point me in the right direction please?
I use Vue.js and have a component. I pass a prop "request" to that component:
<adjustments-list
v-if="request"
:request="request"
/>
In the component I'm able to do this:
<text-input
:value="request.id"
/>
It works that is the value of "id" is displayed.
In props section of component:
props: {
request: Object
In mounted hook of component:
async mounted () {
await this.refresh()
},
In refresh function of component:
async refresh () {
console.log('this.request.id =', this.request.id)
if (this.request.id) {
const data = await requestApi.getRequestResultAdjustmentByReqId(this.request.id)
}
},
The this.request.id is undefined.
I'm not sure why.
If the request property is asynchronously available to the component then, you have to use combination of watchers like:
// adjustments-list component
new Vue({
props: {
request: Object
},
data() {
return {
apiData: null
}
},
watch: {
request(newValue, _oldValue) {
this.refresh(newValue);
}
},
mounted: function () {
// Do something here
},
methods: {
refresh (request) {
if (request.id) {
// Using promise instead of async-await
requestApi.getRequestResultAdjustmentByReqId(request.id)
.then(() => this.apiData = data);
}
}
}
});
Also, note that, mounted should be a plain old JS function and not an async function. That's the lifecycle method of the component supposed to behave in particular way.
Is there a way to re-render a component on route change? I'm using Vue Router 2.3.0, and I'm using the same component in multiple routes. It works fine the first time or if I navigate to a route that doesn't use the component and then go to one that does. I'm passing what's different in props like so
{
name: 'MainMap',
path: '/',
props: {
dataFile: 'all_resv.csv',
mapFile: 'contig_us.geo.json',
mapType: 'us'
},
folder: true,
component: Map
},
{
name: 'Arizona',
path: '/arizona',
props: {
dataFile: 'az.csv',
mapFile: 'az.counties.json',
mapType: 'state'
},
folder: true,
component: Map
}
Then I'm using the props to load a new map and new data, but the map stays the same as when it first loaded. I'm not sure what's going on.
The component looks like this:
data() {
return {
loading: true,
load: ''
}
},
props: ['dataFile', 'mapFile', 'mapType'],
watch: {
load: function() {
this.mounted();
}
},
mounted() {
let _this = this;
let svg = d3.select(this.$el);
d3.queue()
.defer(d3.json, `static/data/maps/${this.mapFile}`)
.defer(d3.csv, `static/data/stations/${this.dataFile}`)
.await(function(error, map, stations) {
// Build Map here
});
}
You may want to add a :key attribute to <router-view> like so:
<router-view :key="$route.fullPath"></router-view>
This way, Vue Router will reload the component once the path changes. Without the key, it won’t even notice that something has changed because the same component is being used (in your case, the Map component).
UPDATE --- 3 July, 2019
I found this thing on vue-router documentation, it's called In Component Guards. By the description of it, it really suits your needs (and mine actually). So the codes should be something like this.
export default () {
beforeRouteUpdate (to, from, next) {
// called when the route that renders this component has changed,
// but this component is reused in the new route.
// For example, for a route with dynamic params `/foo/:id`, when we
// navigate between `/foo/1` and `/foo/2`, the same `Foo` component instance
// will be reused, and this hook will be called when that happens.
// has access to `this` component instance.
const id = to.params.id
this.AJAXRequest(id)
next()
},
}
As you can see, I just add a next() function. Hope this helps you! Good luck!
Below is my older answer.
Only saved for the purpose of "progress"
My solution to this problem was to watch the $route property.
Which will ended up you getting two values, that is to and from.
watch: {
'$route'(to, from) {
const id = to.params.id
this.AJAXRequest(id)
}
},
The alternate solution to this question handles this situation in more cases.
First, you shouldn't really call mounted() yourself. Abstract the things you are doing in mounted into a method that you can call from mounted. Second, Vue will try to re-use components when it can, so your main issue is likely that mounted is only ever fired once. Instead, you might try using the updated or beforeUpdate lifecycle event.
const Map = {
data() {
return {
loading: true,
load: ''
}
},
props: ['dataFile', 'mapFile', 'mapType'],
methods:{
drawMap(){
console.log("do a bunch a d3 stuff")
}
},
updated(){
console.log('updated')
this.drawMap()
},
mounted() {
console.log('mounted')
this.drawMap()
}
}
Here's a little example, not drawing the d3 stuff, but showing how mounted and updated are fired when you swap routes. Pop open the console, and you will see mounted is only ever fired once.
you can use just this code:
watch: {
$route(to, from) {
// react to route changes...
}
}
Yes, I had the same problem and solved by following way;
ProductDetails.vue
data() {
return {
...
productId: this.$route.params.productId,
...
};
},
methods: {
...mapActions("products", ["fetchProduct"]),
...
},
created() {
this.fetchProduct(this.productId);
...
}
The fetchProduct function comes from Vuex store. When an another product is clicked, the route param is changed by productId but component is not re-rendered because created life cycle hook executes at initialization stage.
When I added just key on router-view on parent component app.vue file
app.vue
<router-view :key="this.$route.path"></router-view>
Now it works well for me. Hopefully this will help Vue developers!
I was having the same issue, but slightly different. I just added a watch on the prop and then re-initiated the fetch method on the prop change.
import { ref, watch } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import Page from './content/Page.vue';
import Post from './content/Post.vue';
const props = defineProps({ pageSlug: String });
const pageData = ref(false);
const pageBodyClass = ref('');
function getPostContent() {
let postRestEndPoint = '/wp-json/vuepress/v1/post/' + props.pageSlug;
fetch(postRestEndPoint, { method: 'GET', credentials: 'same-origin' })
.then(res => res.json())
.then(res => {
pageData.value = res;
})
.catch(err => console.log(err));
}
getPostContent();
watch(props, (curVal, oldVal) => {
getPostContent();
});
watch(pageData, (newVal, oldVal) => {
if (newVal.hasOwnProperty('data') === true && newVal.data.status === 404) {
pageData.value = false;
window.location.href = "/404";
}
});
router - index.js
{
path: "/:pageSlug",
name: "Page",
component: Page,
props: true,
},
{
path: "/product/:productSlug",
name: "Product",
component: Product,
},
{
path: "/404",
name: "404",
component: Error404,
}