id passed through params get lost on page refresh - vue.js

I have a component called Subscribers that receives props via params from another component called Details. I use the props received to make an API call to get the list of subscribers. This works well.
But an issue arises when the page is refreshed. The id that is passed via params is lost - becomes null. Cause of that subscribers which is defined as an empty array in data becomes undefined as seen in this error:
TypeError: Cannot read property 'length' of undefined
This is how I am passing the params from Details component
<router-link class="unsubscribe" :to="{ name: 'fund-subscribers', params: {fundProp: fund, id: fund.id } }">View all subscribers to this fund</router-link>
Then I have this:
props: {
fundProp: {
type: Object,
default() {
return {
id: null,
title: '',
description: ''
};
}
},
},
data() {
return {
fund: this.fundProp,
subscribers: [],
}
},
Here is the route configuration code for fund-subscribers
{
path: 'funds/:id/subscribers',
name: 'fund-subscribers',
component: Subscribers,
meta: {
title: 'Admin'
},
props: true
},
What could I be doing wrong?

So I found a way to resolve it. First, there was no need to pass fundProp, I could pass only the id via params to router-link
<router-link class="unsubscribe" :to="{ name: 'fund-subscribers', params: { id: fund.id } }">
View all subscribers to this fund
</router-link>
With that done I can now make use of this.$route.params.id. Which I can set my fund to in my component's data, and then use it to make the API call.
data() {
return {
fund: this.$route.params.id
}
},
The function that makes the request to server can use this path:
/funds/${fund}/users, instead of /funds/${fund.id}/users
I hope this helps someone later (and me too in the future).
To understand it better make use of the links below:
https://forum.vuejs.org/t/route-params-not-showing-after-full-page-refresh/30364
https://router.vuejs.org/en/advanced/data-fetching.html

Related

How can I return an empty instance from a getter in Vuex?

I have a store that by default returns undefined for a particular attribute:
// store.js
export const state = {
locationLoading: false,
locationsLoading: false,
locations: [],
location: undefined, <--
};
In my component, I am using a getter within the computed attribute:
// component.vue
...
computed: {
location() {
return this.$store.getters['location/location']; // returns undefined or a location
},
},
...
Within my data for a location I have an array:
// location.json
...
"name": "my location",
"messaging": [
{
"email": {
"fromName": "No Reply <noreply#example.com>",
"fromAddress": "noreply#example.com"
}
}
],
If I am visiting a url such as /locations/123 everything works great. There are no errors; the page renders correctly including validation.
If visit /locations for example, get a list of all locations.
The issue I am having is using vuelidate for my validation. Because in my /locations route, there isn't a location to load, my getter returns undefined (as expected).
The error I am getting is
Cannot read properties of undefined (reading 'email')"
Which makes sense since my location is undefined.
Here is what I am using to validate messaging:
// component.vue
<input type="text" v-model.trim=" $v.location.messaging.$each.$iter[0].email.fromAddress.$model"
/>
I could pass a location in as a prop that contained every attribute. For example:
// component.vue
props: {
location: {
type: Object,
required:false,
default:() => ({
name: '',
messaging: [{
email: {
fromName: '',
fromAddress: '',
},
}],
...
Doing this clears all errors, but I feel that is pretty fragile. If I decide to add a new attribute to a location, I need to remember to add it here too.
How can I return an empty object that satisfies validation?
You can try with optional chaining :
<input type="text"
v-model.trim="
$v.location?.messaging?.$each.$iter[0].email.fromAddress.$model"
/>

Something will trigger a warning in the console? Why is it not recommended and how would you fix it?

it's about good practice and knowledge on Javascript and VueJs.
I already commented the Type Api to see if other warnings persist
<script>
export default {
name: "printer",
props: {
model: {
type: String,
required: true
},
ip: {
type: String,
required: true
},
jobs: {
type: Object,
validator: (value) => ["inProgress", "done", "error"].every((status) => typeof value[status] === "number")
},
progress: {
type: Number,
required: true
},
api: {
type: Api,
required: true
}
},
data: () => ({
timer: null
}),
methods: {
async refreshProgress() {
this.jobs = await this.api.getJobsNumbers({ model: this.model });
}
},
created() {
this.timer = setInterval(() => this.refreshProgress(), 30000);
},
destroyed() {
clearInterval(this.timer);
}
};
</script>
So I'm looking for advices as I'm a beginner in VueJS and need to describe and purpose changements
Question is missing exact warning text so I'm just guessing the warning you talk about is probably something like this:
[Vue warn]: Avoid mutating a prop directly since the value will be overwritten whenever the parent component re-renders. Instead, use a data or computed property based on the prop's value. Prop being mutated: "jobs"
In other words you are mutating parent's state in child component which is not a good idea and that's why Vue warns you.
There are several ways how to fix it:
make a copy of props and store it in components data. Update internal state only. Changes will not be visible to parent
Instead of mutating jobs prop, fire a custom event passing new state (returned from API call) as an argument. Parent needs to handle the event and update its own state with new value (update will flow through prop back to child component)
If you are absolutely sure your printer component will be the only place where jobs object will be mutated, you can mutate object's properties instead of replacing whole object without any warnings. This is possible but not recommended...
For more see Vue documentation or some very related SO questions

Vue prop validation is not called

I create a component with input properties:
export default {
data() {
:
:
},
props: {
rowsContent: {
type: Object,
default: null,
validator: function(value) {
console.log("In validator");
}
},
rowsPerPage: {
type: Number,
default: 10,
},
}
I tried to pass different type of parameters, and got no error message.
Moreover, no "In validator" message is printed to console.
Any idea?
I don't know the reason, but it is working if I use component tag like <tag></tag>. If I use like <tag/>, it does not work. See example here. https://codesandbox.io/s/z6rlzl998p
EDIT: Vue does not support self-closing tags as components: https://github.com/vuejs/vue/issues/8664 (as mentioned in comment)

What is the best way to pass data between components in vue.js?

Suppose I have an App.vue. Query.vue and Result.vue are its children.
And the router looks like this:
import Query from '#/components/Query'
import Result from '#/components/Result'
export default new Router({
routes: [
{
path: '/',
name: 'query',
component: Query
}, {
path: '/result',
name: 'result',
component: Result
}
]
})
There is a button in Query.vue, when clicked, it will send a POST request to API:
this.$http.post(url, {
arg1: arg1,
arg2: arg2
}).then(function (response) {
data = response.data
// I want to do something like:
this.$router.push({path: 'result', payload: data})
}, function (response) {})
And then in Query.vue, I can handle the payload.
But it seems not possible. Should I use store to make it?
this.$http.post(url, {
data: "some data"
}).then(function (response) {
store.store(response.data)
this.$router.push({path: 'result'})
}, function (response) {})
If you need just to pass some data one directional from parent to child - USE PROPS.
If you need to pass data from child to parent - USE EVENTS
If you need to keep some data synced across many components (not parent/child) - USE VUEX

Params field is empty in $router.push

Consider this:
this.$root.$router.push({
path: '/dashboard',
params: { errors: 'error' },
query: { test: 'test' }
})
I use this in my component to redirect to another URL, and some error has occured. The problem is that when I want to access params field in dashboard component, it's empty. The query field works well. I'm trying to access it by this.$route.params.errors.
You can use params only with named paths (i think).
Example:
//route (in your router file should have "name")
{ path: '/errors', name: 'EXAMPLE', component: ... }
//navigating
this.$router.push({
name: 'EXAMPLE',
params: { errors: '123' }
});
Now it will have correct value in this.$route.params.
If you don't want to use named routes you can try this:
ES6
this.$root.$router.push({
path: `/dashboard/${error}`,
query: { test }
})
ES5
this.$root.$router.push({
path: '/dashboard/' + error,
query: { test: 'test' }
})
I faced the similar issue where in one of my views (component). I was trying to navigate (programmatically) from /foo/bar to /foo/bar/123, but the route param was not available later in the component. My relevant navigation code looked like below:
methods: {
save_obj() {
let vm = this;
// Make AJAX call to save vm.my_obj, and on success do:
let v = `${vm.$route.path}/${vm.my_obj.id}`;
console.log("Loading view at path: "+v);
vm.$router.push({ path: v });
},
...
}
It would print the expected log (e.g., Loading view at path: /foo/bar/112), however, the loading of data in the created() hook would not receive the value of route param. My failing created() code looked like below:
created: function() {
console.log("Loading object details.");
let vm = this;
let cid = vm.$route.params.id; // <---- This was the problem
vm.$http.get('api/'+cid)
.then(function (res) {
if (res.data.status == "OK") {
vm.my_obj = res.data.body;
} else {
vm.setStatusMessage(res.data.body);
}
})
.catch(function (error) {
console.log(error);
vm.setStatusMessage("Error: "+error);
});
}
The solution was indicated in the third note here quoted below :
Note: If the destination is the same as the current route and only
params are changing (e.g. going from one profile to another /users/1
-> /users/2), you will have to use beforeRouteUpdate to react to changes (e.g. fetching the user information).
I had to do the following in my component:
Change the line let cid = vm.$route.params.id; in created() to let cid = vm.course.id
and, add the following to the component:
beforeRouteUpdate(to, from, next) {
if (to.params.id) {
this.my_obj.id = to.params.id;
}
// Some other code specific to my app
next();
}
I hope this helps someone stuck with the similar issue.
If you want to send a parameter with a query parameter you can use that syntax like that
this.$router.push({
path: this.localePath(`/bookings/${requestReservation?.attributes?.booking_id}`),
query: { requestReservation: requestReservation }
})
You can access it on the next page like that
this.$route.query.requestReservation
If you want send it fro nuxt-link than its syntax like that
<nuxt-link
:to="{ path: '/bookings/'+ requestReservation.attributes.booking_id,
query: { requestReservation } }">
Booking
</nuxt-link>
You can access it on the next page same like previous
this.$route.query.requestReservation