Element UI with VueJS Autocomplete : Avoid Mutation A Props (Vue Warn) - vue.js

I have a big problem with VueJS and Element UI https://element.eleme.io/#/en-US/component/input#autocomplete-attributes
I use the AutoComplete Component, and i want that I click on the input (onFocus Event), my suggestions re appear
<el-autocomplete
id="inputID"
name="inputName"
class="inputClass"
v-model="inputModel"
:fetch-suggestions="querySearchInput"
placeholder= "Select an Input"
ref="inputReference"
value-key="id"
v-loading="inputLoader"
:value="inputValue"
#select="onChangeInput"
#focus="onFocusInput"
#clear="onClearInput"
clearable
>
<!-- Slot : Override Component Suggestions -->
<template slot-scope="{item}" v-if="item.id">
{{ item.id }} - {{ item.name }}
</template>
</el-autocomplete>
on my onFocus or onClear i used :
this.inputModel = "";
this.inputValue = ""
But it does not reset my suggestions.. :/
The only way for me is to use :
this.$refs.inputReference.value = "";
But it's not a best pratice I have a message : " vue.esm.js?efeb:591 [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: "value" "
I use a :value and a v-model on my component because i stock an ID but i display a label with i18n, isn't important
VERY THANKS YOU
I think it's because the AutoComplete Component includes a InputComponent, and i'm not a pro with the cascading prop on VueJS..
Thanks.

Just do like it says. Do not mutate props. If you need to change some date from props, save it to local component data and modify it
props: {
foo: {
type: number,
requred: true
}
},
...
data () {
return {
localFoo: this.foo
}
}

Related

Data from props not showing in v-model (VueJS)

I'm trying to use a props from the parent component to use it as a data in my child component.
parent component :
<ChangeCommentModal :comment="this.modalInfo.comment" />
And child component (ChangeCommentModal) :
props: ['comment'],
data() {
return {
localComment: this.comment,
};
}
The localComment variable get the value but I can't use it in a v-model in this child component :
<textarea id="message" rows="2" v-model="localComment"></textarea>
The textarea is empty when the component is displayed.
Any idea ? Thanks !
Seems your code is only assign this.comment to localComment once when the child component is init. Instead, you can use watcher to watch the change of prop comment and assign it to the localComment everytime you update from the parent component. Let's try to see if resolve your problem
watch() {
comment(value) {
this.localComment = value
}
}

Vue - conditionally pass down props

I'd like to know if there is a good way to conditionally pass down one or more props to a child component.
If there is sometimes an id set, then I want to pass it down. I can't set id to null because a prop must have a value. I have solved it before by using a "v-if" like this:
<survey-wrapper v-if="id" :inputJson="inputJson" :id="id"></survey-wrapper>
<survey-wrapper v-else :inputJson="inputJson"></survey-wrapper> // <-- no id
But it's a workaround which looks bad and it becomes a lot of code if the component has many props. And what if you have two props that may be set or not?
You can use v-bind and pass it and object containing all your props. and conditionally add your id prop like this.
<survey-wrapper v-bind="{ inputJson, ...(id ? { id } : {}) }"></survey-wrapper>
You can do it using v-bind and dynamically creating the list of props with a method.
Here is an example:
<template>
<div id="app">
<Component v-bind="propsToPass()"/>
</div>
</template>
<script>
export default {
name: "App",
components: {
Component
},
data() {
return {
passId: true
}
},
methods: {
propsToPass() {
const result = {};
if (this.passId) {
result.id = 1;
}
return result
}
}
};
</script>
In the above example, the prop id will only be passed if passId is true.
I think the best way to do so is by using v-bind with a logical operator && :
<survey-wrapper :inputJson="inputJson" v-bind="id && {id}" />
note that it will only pass the prop if (id) is available in such case id should not be required as a prop by the component.
Thanks

Checkbox renders false but the data and DOM attribute is true

I want to create a checkbox group component. Currently there is no native solution so I tried to create one on my own
<template>
<v-container fluid>
<v-checkbox
v-for="(groupItem, index) in groupItems"
:key="index"
:label="groupItem.display"
:value="groupItem.value"
#change="onCheckboxUpdated(index)"
></v-checkbox>
</v-container>
</template>
<script>
export default {
props: {
groupItems: {
type: Array,
required: true
}
},
methods: {
onCheckboxUpdated: function(index) {
this.groupItems[index].value = !this.groupItems[index].value;
this.$emit("checkboxGroupUpdated", this.groupItems);
}
}
};
</script>
This component should render a specific amount of checkboxes and fire an event with all the updated values.
When I pass in these values
values: [
{
display: "Read permissions",
value: true
},
{
display: "Write permissions",
value: false
},
{
display: "Delete permissions",
value: false
}
]
the first generated checkbox renders a false state although the DOM element is set to true
When toggling the checkbox it will work fine the next time.
I created an example to reproduce the problem
https://codesandbox.io/s/checkboxgroup-l8gcg
If you want to expand the v-model of a v-checkbox you'll need to use input-value as the prop rather than value.
:input-value="groupItem.value"
See:
https://github.com/vuetifyjs/vuetify/blob/27d5fdd32dc7c8a9af38f823d1574d92b211d405/packages/vuetify/src/mixins/selectable.js#L14
That's a mixin that's included in v-checkbox.
While input-value is documented, https://vuetifyjs.com/en/components/selection-controls#api, you do have to dig a bit to find it.
Unrelated to your problem, your current implementation of a checkbox group violates one-way data flow. You are mutating an object that has been passed via a prop.
https://v2.vuejs.org/v2/guide/components-props.html#One-Way-Data-Flow
I'll leave it to you to decide whether you care.
It is because Vue.js ignores the value of inputs. You need to use v-model instead.
<v-checkbox
v-for="(groupItem, index) in groupItems"
:key="index"
:label="groupItem.display"
v-model="groupItem.value"
#change="onCheckboxUpdated(index)"
></v-checkbox>
From the documentation https://v2.vuejs.org/v2/guide/forms.html
v-model will ignore the initial value, checked or selected attributes found on any form elements. It will always treat the Vue instance data as the source of truth. You should declare the initial value on the JavaScript side, inside the data option of your component.

vue 2 components, one for display, one for changing?

I would like to have two components, one for displaying a value, and one for changing it with a text field. I can't get this to work? Is there another way of doing this?
I get this error message:
"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: "forpris""
Vue.component('prislapp-forpris', {
props: ['forpris'],
template: '<div class="prislappForpris">[[ forpris ]],-</div>',
delimiters: ['[[',']]']
});
Vue.component('input-forpris', {
props: ['forpris'],
template: '<input type="text" v-model="forpris" />'
});
var app = new Vue({
el: '.previewPage',
data: {
lapp: {
id: 1,
forpris: 30595
}
}
});
It's all about v-model directly mutating the forpris prop. As the warning states, you should avoid to mutate a prop from a component.
 Rationale behind the warning
The reason is that allowing child component to modify props that belong to their parents make programs more error prone and difficult to reason about.
Instead, the idea behind Vue and other component oriented architectures and frameworks is that child components emit events to their parents, and then the parents change their own state, which in turn modify the child component via events from their children.
This ensures that the component passing down the props have full control of the state and may, or may not, allow the desired state changes that come via props.
How to fix your code to avoid the warning
v-model is syntax sugar over a :value and an #input on the input element. A really good read to understand how v-model innerly works is this article.
What you should do, is to drop v-model on the input for this:
template: '<input type="text" :value="forpris" #input="$emit('input', $event)" />'
This will set forpris as the value of the input (as v-model was already doing), but, instead of automatically modifying it, now the component will emit an input event when the user writes in the input.
So you now need to listen for this event in the parent and react accordingly. Now from your code is not absolutely clear who is rendering the two component, I guess the rendering comes from the .previewPage element in the Vue template, so the Vue instance is the parent component here.
You don't show the html of that template, but I guess it is something like the following:
<div class="previewPage">
<prislapp-forpris :forpriss="lapp.forpris" />
<input-forpris :forpriss="lapp.forpris" />
</div>
You now should listen to the #input event in the input-forpriss component:
<div class="previewPage">
<prislapp-forpris :forpriss="lapp.forpris" />
<input-forpris :forpriss="lapp.forpris" #input="handleInput" />
</div>
So, whenever we receive an #input event, we call the handleInput method. We also need to add such method to the Vue instance:
var app = new Vue({
el: '.previewPage',
data: {
lapp: {
id: 1,
forpris: 30595
}
},
methods: {
handleInput(value){
console.log(value); // now I'm not 100% sure if this
// is the value or a DOM event, better check
this.lapp.forpriss = value;
},
}
});

public properties in vue.js showing warning

Component Code
<template lang="html">
<div class="chat-users">
<ul class="list-unstyled">
<li v-for="chatuser in chatusers" class="left clearfix">
{{ chatuser.UserName }}
</li>
</ul>
</div>
</template>
<script>
export default {
props: ["chatusers"],
created() {
axios.post("some url").then(response => {
this.chatusers = response.data.Data;
});
}
}
</script>
<style>
</style>
Everything works perfectly, but I am getting below warning.
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:
"chatusers"
There is an explanation why prop should not be mutate in the vue documentation. If you need to mutate the state, perhaps you need a data instead.
Like:
export default {
data () { return { chatusers: null } },
created() {
axios.post("some url").then(response => {
this.chatusers = response.data.Data;
});
}
}
It is not the best way to mutate the props, since the parent is in control of this data and any changes it will overwrite child data, from the docs:
In addition, every time the parent component is updated, all props in
the child component 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.
According to Vue.js data flow, the props received from parent should not be modified by child component. See the official documentation and this Stack Overflow answer.
As suggested by your warning: "use a data or computed property based on the prop's value", that in your case is the response received from axios call.