Custom wrapper component with props and v-model - javascript

I am using the vue-select library: https://github.com/desislavsd/vue-select in my app.
Since I am using a lot of them I just want to make a separate wrapper component, but now my selects don't work. Vue somehow doesn't recognize the props. How can I make my v-select a separate reusable component that can accept it's normal props and worK?
This is my Select component:
<template>
<div>
<v-select/>
</div>
</template>
<script>
export default {
name: "Select",
}
</script>
And this is how I am using it:
<Select as="role" placeholder="Assesor" v-model="value1" :from="roles" :key="roles.role" />
export default {
name: "Admin",
components: {
Header,
Select
},
data() {
return {
value1: [],
selected: {
role: ''
},
roles: [
{ role: "Assesors" },
{ role: "Finance" },
{ role: "Sales" }
]
};
}
};

This is more complex than it seems. You expect the props passed to the custom component to be applied to the inner v-select but Vue doesn't know that. Someone else might expect them to go on the <div>.
v-bind="$attrs"
Props are not automatically passed to a child element. To do that, you need:
<v-select v-bind="$attrs" />
v-model
Now the props are applied to the element you choose. But since v-model is a special prop from the parent with hidden functionality, it's not passed properly without some extra preparation. You'll have to code a v-model directly on the child:
<v-select v-bind="$attrs" v-model="model" />
Computed setter
The parent's v-model passes down a value prop. Create a computed setter in the custom element (I'm calling it model) to use with the child's v-model:
computed: {
model: {
get() { return this.$attrs.value },
set(val) { this.$emit('input', val) }
}
}
Here is the updated demo with a custom wrapper component using selected and as="role::role"

Related

What is better to use to communicate with a child component - $emit or $ref?

Tell me, please.
There is a page for changing user data. This page consists of fields for entering information about the user and a Submit button.
I need to create a popup component that performs the same functions.
The form itself will now be in a child component. The submit button is now moved to the parent component.
Therefore, I need to pass the entered data to the parent component. Everything is complicated by the fact that initially data comes from the server with previously entered information about the user. Therefore, initially you need to transfer data to the child component through props, and then, when changing them from the child, transfer it to the parent.
But what if there are a lot of variables?
For example: I need to create 15 props and then pass this data through $emit. The data itself is passed either through the #input event of the <input> tag or the #change event of the <select> tag.
I thought of three options, but I don't know which one is better. Or maybe some should not be used at all.
Using $emit
Parent component
<template>
<Child
:first-props="user.first"
:second-props="user.second"
....
:fifteenth-props="user.fifteenth"
/>
<button #click='submit'>Submit</button>
</template>
<script>
import Child from '#/components/Child.vue'
import { mapGetters } from 'vuex'
export default {
data: () => ({
first: '',
second: '',
...
fifteenth: ''
}),
components: {
Child
},
computed: {
...mapGetters({
user: 'user/getUser'
})
},
methods: {
submit() {
//Sending data
}
},
mounted: {
this.$store.dispatch('user/getUserData')
}
}
</script>
Child component
<template>
<div>
<input type="text" value="first" #input="username" />
<input type="text" value="second" #input="name" />
<input type="text" value="fifteenth" #input="surname" />
</div>
</template>
<script>
export default {
props: {
first: {
type: String,
required: true
},
first: {
type: String,
required: true
},
...
fifteenth: {
type: String,
required: true
}
},
methods: {
username() {
this.$emit('changeUsername', first)
},
name() {
this.$emit('changeName', second)
},
surname() {
this.$emit('changeSurname', fifteenth)
}
}
}
</script>
In this variant, I am worried about the number of props. I do not know if this can somehow affect the speed and quality of the component.
Using $ref
Parent component
<template>
<Child
ref='childComponent'
/>
<button click="submit">Submit</button>
</template>
<script>
import Child from '#/components/Child.vue'
export default {
data: () => ({
first: '',
second: '',
...
fifteenth: ''
}),
components: {
Child
},
method: {
submit() {
this.$refs.childComponent.submit()
}
}
}
</script>
Child component
<template>
<div>
<input type="text" v-model="first" #input="username" />
<input type="text" v-model="second" #input="name" />
<input type="text" v-model="fifteenth" #input="surname" />
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
data: () => ({
first: '',
second: '',
...
fifteenth: ''
}),
computed: {
...mapGetters({
user: 'user/getUser'
})
},
methods: {
submit() {
//Sending data
}
},
mounted: {
this.$store.dispatch('user/getUserData')
}
}
</script>
In this variant, there is not much interaction between the parent and child component. And the parent component fires only the submit method. Plus, you don't need to change the existing code too much, as you just need to move the button. But here I am concerned: is it not a bad decision to apply ref in this way ? And the second problem is the availability of data when the button is clicked. It is more likely that the child component has not yet received the necessary data or simply has not loaded yet, and the user has already pressed the button.
For example, the parent component already has a main title and a submit button. But the child component is still loading.
Although the solution here can be to make the button available for pressing only after loading the child component using :disabled="isDisabled".
Using vuex
This option immediately seems wrong to me, since the data will be used only inside this component. And vuex seems to me suitable only for global cases.
I have tried all three options and they all work. But I wanted the code not only to work, but also to be correct.
$ref and $emit don't serve the same purpose, there's generally no choice between them.
Option 1 can use a single data prop in a child:
<input
type="text"
:value="formData.first"
#input="$emit('updateFormData', { key: 'first', value: $event.target.value }))"
/>
And merge the changes in a parent:
<Child
:formData="formData"
#updateFormData="formData[$event.key] = $event.value"
/>
Option 2 is possible in its current state. Alternatively, a child can be made self-contained and contain submit button, then there is no use for $ref. It can emit submit event in case there's business logic associated with form submit that shouldn't be moved to a child.
Option 3 is possible but the use of global store for local purposes is not justified, local stores can be achieved with Vuex dynamic modules.

Vuejs passing props to from parents component to child component not working

I am newbie and in charge a project using Vuejs.
Here is a page that use a component called DashboardCard and each DardboardCard is passed 2 props is val and icon from the page a.k.a the parent component
I declared props in DashboardCard component a.k.a child component but the value of all props can not be passed and assigned
I now I got this error
UPDATE
Here is the Vuejs devtool log, you can see that props was passed
The reason, is that before even the props hold the value, you are trying to access it. So there are two ways I would suggest
From parent component (Dashboard.vue):
Try passing it like :icon="'#/assets/sale.png'" and :val="'$700'"
and in child Component (DashboardCard.vue)
Method-1:
Declare two data variables and set them with the prop values using watch handler
export default {
components: {DatePicker},
props: {
icon: {
type: String,
default: ''
},
val: {
type: String,
default: ''
}
},
data() {
return {
iconPath: '',
amtVal: ''
};
},
watch: {
icon(newVal, oldVal) {
this.iconPath = newVal;
},
val(newVal, oldVal) {
this.amtVal = newVal;
}
}
and in template use your local data variables like
<span>{{amtVal}}</span>
and
<img :src="require(iconPath)" alt="icon" />
Method-2:
Using a computed property
export default {
components: {DatePicker},
props: {
icon: {
type: String,
default: ''
},
val: {
type: String,
default: ''
}
},
computed: {
getIcon() {
return this.icon;
},
getVal() {
return this.val;
}
}
and in template
<span>{{getVal}}</span>
and
<img :src="require(getIcon)" alt="icon" />
You are missing the script ending tag </script> in the DashboardCard component so Vue can't spot out the component properties. At first, I thought there was more code down there until I try the sandbox.
Please note that in the Vue devtools extension, a prop is supposed to sit under the props tab, which is not your case.
I suggest you using some kind of snippets to avoid this annoying typos. For me I use Vetur. It also helps with syntax highlighting, pretty handy extension.
https://marketplace.visualstudio.com/items?itemName=octref.vetur

Vuejs moving data between multiple components

I have a parent component with two children components that I need data to move between. I can get emitted data from one child (multiselect.vue) to the parent, but cannot get it to then move into the other child component (render.vue). I suspect it has something to with v-model. Below is a simplifed version.
Parent component
<template>
<div>
<multiSelect v-model="content" />
<renderSplashPage :content="render" />
</div>
</template>
<script>
import multiSelect from '#/utils/multiSelect'
import render from '#/components/Render'
export default {
components: {
multiSelect,
render,
},
name: 'somename',
data(){
return{
content: {},
render: {}
}
},
watch: {
'content': function(val) {
this.render = val;
},
},
}
</script>
How can I get the emitted data to be 'watched' and 'picked up' by the 'render' component?
render is a reserved name (i.e., the internal render function cannot be overwritten), and for some reason, Vue silently ignores attempts to modify it.
Rename your property (e.g., to "myRender"), and you should see reactivity. If myRender is always equal to content, you could actually just replace it with content in your binding:
<multiselect v-model="content" :options="options" />
<render :content="content" />
demo
I used a variation of this here to solve my issue:
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })
Taken from here:
https://v2.vuejs.org/v2/guide/reactivity.html

How can i access the errors computed property on custom input component validated using vee-validate?

I am trying to use VeeValidate in a custom input component.
I tried using $emit on #input and #blur but the validation occurs on next tick and i end up failing to catch the validation on event.
onEvent (event) {
console.log('error length', this.errors.items.length)
if (this.errors.items.length) {
this.hasError = true
this.$emit('has-error',this.errors.items)
} else {
this.hasError = false
this.$emit('on-input', event)
}
}
I also tried injecting the validator from the parent so as to be able to access the errors computed property directly but there might be 1-2-3 levels of nesting between the parent page and the custom input component itself. I would have to inject the validator through all of them and these component are meant to be reusable.
export default {
//in custom input component
inject: ['$validator'],
}
The best idea i got is to watch the errors computed property and emit an event when a change occurs whit the errors in that instance of the component.
watch: {
errors: function (errorsNew) {
console.log('errors watch',errorsNew)
}
},
The problem is that i can't seem to watch the errors computed property introduced by vee-validate.
Some simplified version of code:
parent
<template>
<div id="app">
<CustomInput
:label="'Lable1'"
:value="'value from store'"
:validations="'required|max:10|min:5'"
:name="'lable1'"
/>
<button>Save</button>
</div>
</template>
<script>
import CustomInput from "./components/CustomInput";
export default {
name: "App",
components: {
CustomInput
}
};
</script>
CustomInput
<template>
<div>
<label >{{ label }}</label>
<input :value="value" :name="name" v-validate="validations">
<span v-if="this.errors.items.length">{{this.errors.items[0].msg}}</span>
</div>
</template>
<script>
export default {
name: "HelloWorld",
props: {
label: {
type: String,
required: true
},
value: {
type: String,
default: ""
},
validations: {
type: String,
default: ""
},
name: {
type: String,
required: true
}
},
watch: {
errors: function(errorsNew) {
console.log("errors watch", errorsNew);
this.$emit('has-error')
}
}
};
</script>
Can someone please help me access the validation errors from the custom input?
Update
I created a simple fiddle if anyone finds it easier to test it https://codesandbox.io/s/mqj9y72xx
I think the best way to handle this is to inject $validator into the child component. That seems to be how the plugin recommends it be done: https://baianat.github.io/vee-validate/advanced/#injecting-parent-validator.
So, in your CustomInput, you'd add inject: ['$validator'],.
Then, in the App.vue, you can have this in the template:
<div>
These are the errors for "lable1" in App.vue:
<span v-if="errors.has('lable1')">{{errors.first('lable1')}}</span>
</div>
I think that's really it.
Working example based off your example: https://codesandbox.io/s/pw2334xl17
I realize that you've already considered this, but the inject method searches up the component tree until it finds a $validator instance, so perhaps you should disable automatic injection in your app at the root level, thus every component searching for a validator to inject will all get that one. You could do that using:
Vue.use(VeeValidate, { inject: false });

Vue 2 - Mutating props vue-warn

I started https://laracasts.com/series/learning-vue-step-by-step series. I stopped on the lesson Vue, Laravel, and AJAX with this error:
vue.js:2574 [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: "list" (found in component )
I have this code in main.js
Vue.component('task', {
template: '#task-template',
props: ['list'],
created() {
this.list = JSON.parse(this.list);
}
});
new Vue({
el: '.container'
})
I know that the problem is in created() when I overwrite the list prop, but I am a newbie in Vue, so I totally don't know how to fix it. Does anyone know how (and please explain why) to fix it?
This has to do with the fact that mutating a prop locally is considered an anti-pattern in Vue 2
What you should do now, in case you want to mutate a prop locally, is to declare a field in your data that uses the props value as its initial value and then mutate the copy:
Vue.component('task', {
template: '#task-template',
props: ['list'],
data: function () {
return {
mutableList: JSON.parse(this.list);
}
}
});
You can read more about this on Vue.js official guide
Note 1: Please note that you should not use the same name for your prop and data, i.e.:
data: function () { return { list: JSON.parse(this.list) } } // WRONG!!
Note 2: Since I feel there is some confusion regarding props and reactivity, I suggest you to have a look on this thread
The Vue pattern is props down and events up. It sounds simple, but is easy to forget when writing a custom component.
As of Vue 2.2.0 you can use v-model (with computed properties). I have found this combination creates a simple, clean, and consistent interface between components:
Any props passed to your component remains reactive (i.e., it's not cloned nor does it require a watch function to update a local copy when changes are detected).
Changes are automatically emitted to the parent.
Can be used with multiple levels of components.
A computed property permits the setter and getter to be separately defined. This allows the Task component to be rewritten as follows:
Vue.component('Task', {
template: '#task-template',
props: ['list'],
model: {
prop: 'list',
event: 'listchange'
},
computed: {
listLocal: {
get: function() {
return this.list
},
set: function(value) {
this.$emit('listchange', value)
}
}
}
})
The model property defines which prop is associated with v-model, and which event will be emitted on changes. You can then call this component from the parent as follows:
<Task v-model="parentList"></Task>
The listLocal computed property provides a simple getter and setter interface within the component (think of it like being a private variable). Within #task-template you can render listLocal and it will remain reactive (i.e., if parentList changes it will update the Task component). You can also mutate listLocal by calling the setter (e.g., this.listLocal = newList) and it will emit the change to the parent.
What's great about this pattern is that you can pass listLocal to a child component of Task (using v-model), and changes from the child component will propagate to the top level component.
For example, say we have a separate EditTask component for doing some type of modification to the task data. By using the same v-model and computed properties pattern we can pass listLocal to the component (using v-model):
<script type="text/x-template" id="task-template">
<div>
<EditTask v-model="listLocal"></EditTask>
</div>
</script>
If EditTask emits a change it will appropriately call set() on listLocal and thereby propagate the event to the top level. Similarly, the EditTask component could also call other child components (such as form elements) using v-model.
Vue just warns you: you change the prop in the component, but when parent component re-renders, "list" will be overwritten and you lose all your changes. So it is dangerous to do so.
Use computed property instead like this:
Vue.component('task', {
template: '#task-template',
props: ['list'],
computed: {
listJson: function(){
return JSON.parse(this.list);
}
}
});
If you're using Lodash, you can clone the prop before returning it. This pattern is helpful if you modify that prop on both the parent and child.
Let's say we have prop list on component grid.
In Parent Component
<grid :list.sync="list"></grid>
In Child Component
props: ['list'],
methods:{
doSomethingOnClick(entry){
let modifiedList = _.clone(this.list)
modifiedList = _.uniq(modifiedList) // Removes duplicates
this.$emit('update:list', modifiedList)
}
}
Props down, events up. That's Vue's Pattern. The point is that if you try to mutate props passing from a parent. It won't work and it just gets overwritten repeatedly by the parent component. Child component can only emit an event to notify parent component to do sth. If you don't like these restrict, you can use VUEX(actually this pattern will suck in complex components structure, you should use VUEX!)
You should not change the props's value in child component.
If you really need to change it you can use .sync.
Just like this
<your-component :list.sync="list"></your-component>
Vue.component('task', {
template: '#task-template',
props: ['list'],
created() {
this.$emit('update:list', JSON.parse(this.list))
}
});
new Vue({
el: '.container'
})
According to the VueJs 2.0, you should not mutate a prop inside the component. They are only mutated by their parents. Therefore, you should define variables in data with different names and keep them updated by watching actual props.
In case the list prop is changed by a parent, you can parse it and assign it to mutableList. Here is a complete solution.
Vue.component('task', {
template: ´<ul>
<li v-for="item in mutableList">
{{item.name}}
</li>
</ul>´,
props: ['list'],
data: function () {
return {
mutableList = JSON.parse(this.list);
}
},
watch:{
list: function(){
this.mutableList = JSON.parse(this.list);
}
}
});
It uses mutableList to render your template, thus you keep your list prop safe in the component.
The answer is simple, you should break the direct prop mutation by assigning the value to some local component variables(could be data property, computed with getters, setters, or watchers).
Here's a simple solution using the watcher.
<template>
<input
v-model="input"
#input="updateInput"
#change="updateInput"
/>
</template>
<script>
export default {
props: {
value: {
type: String,
default: '',
},
},
data() {
return {
input: '',
};
},
watch: {
value: {
handler(after) {
this.input = after;
},
immediate: true,
},
},
methods: {
updateInput() {
this.$emit('input', this.input);
},
},
};
</script>
It's what I use to create any data input components and it works just fine. Any new data sent(v-model(ed)) from parent will be watched by the value watcher and is assigned to the input variable and once the input is received, we can catch that action and emit input to parent suggesting that data is input from the form element.
do not change the props directly in components.if you need change it set a new property like this:
data() {
return {
listClone: this.list
}
}
And change the value of listClone.
I faced this issue as well. The warning gone after i use $on and $emit.
It's something like use $on and $emit recommended to sent data from child component to parent component.
one-way Data Flow,
according to https://v2.vuejs.org/v2/guide/components.html, the component follow one-Way
Data Flow,
All props form a one-way-down binding between the child property and the parent one, when the parent property updates, it will flow down to the child but not the other way around, this prevents child components from accidentally mutating the parent's, which can make your app's data flow harder to understand.
In addition, every time the parent component is updates all props
in the child components will be refreshed with the latest value. This means you should not attempt to mutate a prop inside a child component. If you do .vue will warn you in the
console.
There are usually two cases where it’s tempting to mutate a prop:
The prop is used to pass in an initial value; the child component wants to use it as a local data property afterwards.
The prop is passed in as a raw value that needs to be transformed.
The proper answer to these use cases are:
Define a local data property that uses the prop’s initial value as its initial value:
props: ['initialCounter'],
data: function () {
return { counter: this.initialCounter }
}
Define a computed property that is computed from the prop’s value:
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
If you want to mutate props - use object.
<component :model="global.price"></component>
component:
props: ['model'],
methods: {
changeValue: function() {
this.model.value = "new value";
}
}
I want to give this answer which helps avoid using a lot of code, watchers and computed properties. In some cases this can be a good solution:
Props are designed to provide one-way communication.
When you have a modal show/hide button with a prop the best solution to me is to emit an event:
<button #click="$emit('close')">Close Modal</button>
Then add listener to modal element:
<modal :show="show" #close="show = false"></modal>
(In this case the prop show is probably unnecessary because you can use an easy v-if="show" directly on the base-modal)
You need to add computed method like this
component.vue
props: ['list'],
computed: {
listJson: function(){
return JSON.parse(this.list);
}
}
Vue.component('task', {
template: '#task-template',
props: ['list'],
computed: {
middleData() {
return this.list
}
},
watch: {
list(newVal, oldVal) {
console.log(newVal)
this.newList = newVal
}
},
data() {
return {
newList: {}
}
}
});
new Vue({
el: '.container'
})
Maybe this will meet your needs.
Vue3 has a really good solution. Spent hours to reach there. But it worked really good.
On parent template
<user-name
v-model:first-name="firstName"
v-model:last-name="lastName"
></user-name>
The child component
app.component('user-name', {
props: {
firstName: String,
lastName: String
},
template: `
<input
type="text"
:value="firstName"
#input="$emit('update:firstName',
$event.target.value)">
<input
type="text"
:value="lastName"
#input="$emit('update:lastName',
$event.target.value)">
`
})
This was the only solution which did two way binding. I like that first two answers were addressing in good way to use SYNC and Emitting update events, and compute property getter setter, but that was heck of a Job to do and I did not like to work so hard.
Vue.js props are not to be mutated as this is considered an Anti-Pattern in Vue.
The approach you will need to take is creating a data property on your component that references the original prop property of list
props: ['list'],
data: () {
return {
parsedList: JSON.parse(this.list)
}
}
Now your list structure that is passed to the component is referenced and mutated via the data property of your component :-)
If you wish to do more than just parse your list property then make use of the Vue component' computed property.
This allow you to make more in depth mutations to your props.
props: ['list'],
computed: {
filteredJSONList: () => {
let parsedList = JSON.parse(this.list)
let filteredList = parsedList.filter(listItem => listItem.active)
console.log(filteredList)
return filteredList
}
}
The example above parses your list prop and filters it down to only active list-tems, logs it out for schnitts and giggles and returns it.
note: both data & computed properties are referenced in the template the same e.g
<pre>{{parsedList}}</pre>
<pre>{{filteredJSONList}}</pre>
It can be easy to think that a computed property (being a method) needs to be called... it doesn't
For when TypeScript is your preferred lang. of development
<template>
<span class="someClassName">
{{feesInLocale}}
</span>
</template>
#Prop({default: 0}) fees: any;
// computed are declared with get before a function
get feesInLocale() {
return this.fees;
}
and not
<template>
<span class="someClassName">
{{feesInLocale}}
</span>
</template>
#Prop() fees: any = 0;
get feesInLocale() {
return this.fees;
}
Assign the props to new variable.
data () {
return {
listClone: this.list
}
}
Adding to the best answer,
Vue.component('task', {
template: '#task-template',
props: ['list'],
data: function () {
return {
mutableList: JSON.parse(this.list);
}
}
});
Setting props by an array is meant for dev/prototyping, in production make sure to set prop types(https://v2.vuejs.org/v2/guide/components-props.html) and set a default value in case the prop has not been populated by the parent, as so.
Vue.component('task', {
template: '#task-template',
props: {
list: {
type: String,
default() {
return '{}'
}
}
},
data: function () {
return {
mutableList: JSON.parse(this.list);
}
}
});
This way you atleast get an empty object in mutableList instead of a JSON.parse error if it is undefined.
YES!, mutating attributes in vue2 is an anti-pattern. BUT...
Just break the rules by using other rules, and go forward!
What you need is to add .sync modifier to your component attribute in the parent scope.
<your-awesome-components :custom-attribute-as-prob.sync="value" />
Below is a snack bar component, when I give the snackbar variable directly into v-model like this if will work but in the console, it will give an error as
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.
<template>
<v-snackbar v-model="snackbar">
{{ text }}
</v-snackbar>
</template>
<script>
export default {
name: "loader",
props: {
snackbar: {type: Boolean, required: true},
text: {type: String, required: false, default: ""},
},
}
</script>
Correct Way to get rid of this mutation error is use watcher
<template>
<v-snackbar v-model="snackbarData">
{{ text }}
</v-snackbar>
</template>
<script>
/* eslint-disable */
export default {
name: "loader",
data: () => ({
snackbarData:false,
}),
props: {
snackbar: {type: Boolean, required: true},
text: {type: String, required: false, default: ""},
},
watch: {
snackbar: function(newVal, oldVal) {
this.snackbarData=!this.snackbarDatanewVal;
}
}
}
</script>
So in the main component where you will load this snack bar you can just do this code
<loader :snackbar="snackbarFlag" :text="snackText"></loader>
This Worked for me
Vue.js considers this an anti-pattern. For example, declaring and setting some props like
this.propsVal = 'new Props Value'
So to solve this issue you have to take in a value from the props to the data or the computed property of a Vue instance, like this:
props: ['propsVal'],
data: function() {
return {
propVal: this.propsVal
};
},
methods: {
...
}
This will definitely work.
In addition to the above, for others having the following issue:
"If the props value is not required and thus not always returned, the passed data would return undefined (instead of empty)". Which could mess <select> default value, I solved it by checking if the value is set in beforeMount() (and set it if not) as follows:
JS:
export default {
name: 'user_register',
data: () => ({
oldDobMonthMutated: this.oldDobMonth,
}),
props: [
'oldDobMonth',
'dobMonths', //Used for the select loop
],
beforeMount() {
if (!this.oldDobMonth) {
this.oldDobMonthMutated = '';
} else {
this.oldDobMonthMutated = this.oldDobMonth
}
}
}
Html:
<select v-model="oldDobMonthMutated" id="dob_months" name="dob_month">
<option selected="selected" disabled="disabled" hidden="hidden" value="">
Select Month
</option>
<option v-for="dobMonth in dobMonths"
:key="dobMonth.dob_month_slug"
:value="dobMonth.dob_month_slug">
{{ dobMonth.dob_month_name }}
</option>
</select>
I personally always suggest if you are in need to mutate the props, first pass them to computed property and return from there, thereafter one can mutate the props easily, even at that you can track the prop mutation , if those are being mutated from another component too or we can you watch also .
Because Vue props is one way data flow, This prevents child components from accidentally mutating the parent’s state.
From the official Vue document, we will find 2 ways to solve this problems
if child component want use props as local data, it is best to define a local data property.
props: ['list'],
data: function() {
return {
localList: JSON.parse(this.list);
}
}
The prop is passed in as a raw value that needs to be transformed. In this case, it’s best to define a computed property using the prop’s value:
props: ['list'],
computed: {
localList: function() {
return JSON.parse(this.list);
},
//eg: if you want to filter this list
validList: function() {
return this.list.filter(product => product.isValid === true)
}
//...whatever to transform the list
}
You should always avoid mutating props in vue, or any other framework. The approach you could take is copy it into another variable.
for example.
// instead of replacing the value of this.list use a different variable
this.new_data_variable = JSON.parse(this.list)
A potential solution to this is using global variables.
import { Vue } from "nuxt-property-decorator";
export const globalStore = new Vue({
data: {
list: [],
},
}
export function setupGlobalsStore() {
Vue.prototype.$globals = globalStore;
}
Then you would use:
$globals.list
Anywhere you need to mutate it or present it.

Categories