Vue prop validation is not called - vue.js

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)

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"
/>

Why does vue emit propagate to parent?

I'm new to Vue. I have a child component like below. I added a validator via the 'emits' property and I assumed that if the validator fails then the parent event handler doesn't get called but it does. I also don't see a way to validate the input in the parent or in the child. I know I could add a method in the child component that I assign to the emit property and check it in the child and then only call $emit' if it returns true but it seems backwards. I'm sure I'm missing the point here, someone please clarify because it seems to me that the 'emits' property validator is only for debugging purposes and doesn't actually modify the app behavior, just throws a console warning. To me that is a bug waiting to happen. Is there some vue config setting that I need to change to enable the behavior I was expecting? What am I missing? (btw, when I run npm show vue version I get 3.2.45 if that would matter.
The console showing that the parent handler was called.
Child Component:
<script>
export default {
data() {
return {
username: "",
age: null,
};
},
emits: {
"new-user": function (obj) {
return obj && typeof obj.username == 'string' && typeof obj.age == 'number';
},
},
methods: {
newUser() {
const output = { username: this.username, age: this.age };
this.$emit("new-user", output);
},
},
};
</script>
The parent component:
<template>
<h2>Hello</h2>
<user-data #new-user="newUser"></user-data>
</template>
<script>
export default {
data() {
return {
};
},
methods: {
newUser(val) {
console.log('emitted newUser',val)
},
},
};
</script>
Passing a function to emits will not affect whether the event is emitted, it is only meant for validation. See docs here
though I see that it's not very clear from the docs:
export default {
emits: {
submit(payload) {
// return `true` or `false` to indicate
// validation pass / fail
}
}
}
To make the emit conditional you could use
export default {
data() {
return {
username: "",
age: null,
};
},
emits: ["new-user"],
methods: {
newUser() {
if (typeof this.username == 'string' && typeof this.age == 'number'){
const output = { username: this.username, age: this.age };
this.$emit("new-user", output);
}
},
},
};
You're spot on.
Emit validation only shows a console warning. It is not a mechanism to not emit something.
Similar to prop validation, its primary use is for development. Very useful for large teams or if you're making a component library.

Props interdependance in VueJS

I want to add some linking dependencies between my props in my VueJS component.
For example in my component on props declaration, i would like to stipulate that if a prop is present, then another one should be required, but not required at all if the previous props is not there.
props: {
url: {
type: String,
required: true,
},
isShared: {
type: Boolean,
default: false,
},
isSharedByOtherMember: {
type: Boolean,
default: false,
},
archivedId: {
type: String,
required: isSharedByOtherMember ? true : false, // This is not working, bit is there a way to do so ?
},
After reading vuejs docs :
Note that props are validated before a component instance is created, so instance properties (e.g. data, computed, etc) will not be available inside default or validator functions.
Is there a way to still do this in props declaration for better readability/understandability after ?
Thanks in advance
You could use the validator property for prop.
Vue docs has this example: (https://v2.vuejs.org/v2/guide/components-props.html#Prop-Validation)
// Custom validator function
propF: {
validator: function (value) {
// The value must match one of these strings
return ['success', 'warning', 'danger'].indexOf(value) !== -1
}
}
You could define a validator method in Vue methods section.
Something like this:
export default {
props: {
isSharedByOtherMember: {
type: Boolean,
default: false
},
archivedId: {
type: String,
default: null,
required: false,
validator: this.validateArchiveId(),
errorMessage: 'Archived ID required when isSharedByOtherMember has value of true.'
}
},
methods: {
validateArchiveId () {
return this.isSharedByOtherMember
}
}
}

How can I fill an array by calling a method in Vue js?

I am trying to fill an array, declared in data property, by calling a function in methods property in Vue js. Here is the code:
<script>
export default {
extends: Bar,
mounted() {
this.renderChart(this.chartData, this.options);
this.fillLabel();
},
data() {
return {
chartData: {
labels:[],
datasets: [
{
label: "Users",
data: [40,20,12,39,10,40]
}
]
},
};
},
methods: {
fillLabel() {
this.chartData.datasets[0].data.map(function (key,value) {
this.chartData.labels.push(key);
})
}
}
};
</script>
But this gives me following error in console :
[Vue warn]: Error in mounted hook: "TypeError: Cannot read property 'chartData' of undefined"
So how can I fill the labels array(inside chatData) by 0 to length of data array(inside datasets).
I am looking for your help. Thanks in advance.
This is due to the fact that your function inside your map function will loose the this context. and so its getting undefined.
to solve this problem use an arrow function inside your map.
this.chartData.datasets[0].data.map((key,value)=> {
this.chartData.labels.push(key);
})
this will solve the problem
Only need to introduce this to your fillLabel method as below:
I tried this solution nd it fixed the problem
fillLabel() {
let th = this;
th.chartData.datasets[0].data.map(function (key,value) {
th.chartData.labels.push(key);
})
alert(th.chartData.labels)
},

id passed through params get lost on page refresh

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