I have this vue component:
<template>
<div id="OrderTypeSelect" class="order-type-select">
<b-form-select v-model="curDocType" :options="options" class="mb-3">
</b-form-select>
</div>
</template>
the value of the select input is bound to the Vuex store like this:
computed: {
curDocType: {
get () {
return this.$store.state.curDocType
},
set (value) {
this.$store.commit('setcurDocType', value)
}
}
}
What I can't figure out is how to conditionally prevent the select value from changing. I've tried this:
computed: {
curDocType: {
get () {
return this.$store.state.curDocType
},
set (value) {
if (this.$store.state.curOrder == ""){
this.$store.commit('setcurDocType', value)
}
else{
console.log("Debe descartar");
this.curDocType.get() //This throws error!
}
}
}
}
Even if I don't commit the value to the store, the value in the input field is changed.
I need to call get() again (or something else) to make this binding persistent when my condition is triggered:
if (this.$store.state.curOrder != "") {
//Do not modify store and return the input selection to it's previous value
}
In your case i recommend to use a data object item called curDocType and watch it instead of using computed property :
<b-form-select v-model="curDocType" :options="options" class="mb-3">
data object :
data(){
return{
curDocType:this.$store.state.curDocType
};
}
watch property :
watch:{
curDocType(newval,oldval){
if (this.$store.state.curOrder == ""){
this.$store.commit('setcurDocType', newval)
}else{
this.$nextTick(() => {this.curDocType=this.$store.state.curDocType})
}
}
}
Try <b-form-select v-model="curValue" #input="setValue" :options="options" class="mb-3">
Where curValue is a variable in data and setValue is a method:
methods: {
setValue(value) {
if (this.$store.state.curOrder == "") {
this.$store.commit('setcurDocType', value)
this.curValue = value
} else {
console.log("Debe descartar");
this.$nextTick(() => {this.curValue = this.$store.state.curDocType})
}
}
}
Related
Hi all and thank for helping :)
Context :
In my child i want pass a boolean to the parent for hidden a component in the parent if user clicked
in my child component ( name : connexionDesktop ) :
<button v-on:click="$emit(childMessage)"> Start </button>
data () {
return {
childMessage: true
}
}
in the parent i try :
<connexionDesktop v-if="fromChild == false " v-on:childToParent="childMessage"> </connexionDesktop>
data () {
fromChild:false
}
methods: {
childMessage (value) {
alert('from child' + value );
this.fromChild = value
}
}
but that's not working ... i think i am a noob :D i can't send a message from child to parent ^^""
can you help me ? thank a lot
The first argument for the $emit method should be the event name. So your code should look better like this:
// child component
<template>
<button v-on:click="handleClick"> Start </button>
</template>
<script>
export default {
data () {
return {
childMessage: true
}
},
methods: {
handleClick() {
this.$emit('update:parent', this.childMessage)
}
}
}
</script>
Then in the parent component, you listen for the event and pass in the value the child emitted to a local value of the parent like so:
<template>
<connexionDesktop v-if="fromChild == false" #update:parent="fromChild = $event">
</connexionDesktop>
</template>
<script>
export default {
data () {
return {
fromChild: false
}
},
}
</script>
As it's stated in the docs on $emit here, the first parameter is the custom event name.
You can do something like this:
<button v-on:click="$parent.$emit('childToParent', childMessage)"> Start </button>
data () {
return {
childMessage: true
}
}
and in the parent:
<connexionDesktop v-if="fromChild == false " v-on:child-to-parent="childMessage"> </connexionDesktop>
...
data () {
fromChild:false
}
methods: {
childMessage (value) {
alert('from child' + value );
this.fromChild = value
}
}
...
New to vuex. Simple pokemon SPA that should display pokemon by generation and type. I am trying to write a method-style getter that will pass a component's parameter. EDIT: moved currentType to state:
//Vuex.Store
state: {
pokemon: [],
currentType: 'all'
},
getters:{
availablePokemon: (state) => (currentType) => {
if(!currentType || currentType === 'all'){
return state.pokemon
}else{
return state.pokemon.filter(pokemon => {
//some api objects have two types
if(pokemon.types.length === 2){
if(pokemon.types[0] == currentType || pokemon.types[1] == currentType){
return true
}
}else if(pokemon.types[0] == currentType){
return true
}
})
}
},
mutations:{
changeType(state, type){
state.currentType = type
}
},
actions:{
updateType(context, type){
context.commit('changeType', type)
}}
}
EDIT:
//Pokemon.vue
<select name="type" id="type" #change="updateType($event.target.value)">
<option v-for="(type, index) in types"
:key="index"
:value="type">{{ type }}</option>
</select>
<li v-for="(pokemon, index) in allPokemon"
:key="index">
{{ pokemon.name }}
</li>
export default {
data(){
return{
types: ['all', 'bug', 'dragon'],
}
},
computed: {
allPokemon(){
//this fails, says currentType is undefined
return this.$store.getters.availablePokemon(currentType)
}
},
methods: {
updateType(type){
this.$store.dispatch('updateType', type)
}
}
The component method-style getter doesn't recognize the parameter. I've tried moving the currentType to my store (and updating it with an action => mutation => etc), but that didn't work either. Any ideas what I'm missing?
Architecturally, I'd store the currentType in the
Vuex state object. Then, you can reference the currentType in the getter function - and - also reference the currentType app wide.
JSFiddle here for your reference (see javascript tab and console output): https://jsfiddle.net/qb47x2z1/38/
When you click on the "add" button, you should check whether some text is entered into the input and, if so, then some object should be added with the text entered into the input and then save in the localstorage. When you restart the program, this object should return to the page. It is also possible to delete an object by clicking on the delete button For the component, this code is working.
Here is how it works.
Now I need to transfer everything to vuex. But I can't do this right. When I enter the text in the input in the console, I get the error "Error in v-on handler:" TypeError: e.target is undefined ". And as soon as I remove the focus from the input, the text entered there will disappear. Also, I cannot use v-model as it is not supported by framework 7
How it works now
My code in component
<f7-block strong>
<f7-block-title>Some items</f7-block-title>
<f7-block v-for="(cat, n) in compCats">
<span>{{ cat }}</span>
<f7-button fill color="red" #click="removeCat(n)">Delete Cat</f7-button>
</f7-block>
<f7-list form>
<f7-list-input
:value="compNewCats"
#input="newCatOnInput"
type="text"
placeholder="Заметка"
></f7-list-input>
<f7-button fill color="blue" #click="addCat">Add some item</f7-button>
</f7-list>
</f7-block>
<script>
export default {
computed:{
compCats(){
return this.$store.state.cats;
},
compNewCats(){
return this.$store.state.newCat;
}
},
mounted() {
if (localStorage.getItem('cats')) {
try {
this.cats = JSON.parse(localStorage.getItem('cats'));
} catch(e) {
localStorage.removeItem('cats');
}
}
},
methods: {
addCat(e) {
this.$store.commit('addNewCat');
},
newCatOnInput(e){
this.$store.commit('newCatInput', e.target.value);
},
removeCat(n){
this.$store.comit('removeSomeCat');
}
}
}
</script>
My code in VUEX
export default new Vuex.Store({
state: {
cats:[],
newCat: null
},
mutations: {
addNewCat(state) {
if (!this.newCat) {
return;
}
this.state.cats.push(this.state.newCat);
this.state.newCat = '';
this.saveCats();
},
removeSomeCat(x) {
this.state.cats.splice(x, 1);
this.saveCats();
},
saveCats(state) {
const parsed = JSON.stringify(this.state.cats);
localStorage.setItem('cats', parsed);
},
newCatInput(payload) {
this.newCat = payload;
},
}
}
});
I have a custom input element my-input in MyInput.vue:
<input :value="value" #input="inputOccurred($event.target.value)">
In my inputOccurred method, I am emitting oninputoccurred custom event and also passing the value:
inputOccurred: function(value){
this.$emit('oninputoccurred', value);
}
But how can I receive the passed value from the parent component? And where does the second argument (value) go from this.$emit()?
<my-input #oninputoccurred="printValue(<!-- How do I get the value here -->)"></my-input>
Here is a basic example of sending a value to a component :in then internally setting the components model this.value, then upon #input send the current value back to the parent via emitting it.
Vue.component('myInput', {
template: '#input-template',
props: ['in'],
data () {
return {
value: this.in
}
},
methods: {
inputOccurred (e) {
// set the model
this.value = e.target.value
this.$emit('on-out', this.value.split("").reverse().join(""))
}
}
});
//
var vm = new Vue({
el: '#app',
data () {
return {
value: 'Sent from parent, reverse by typing a value'
}
},
methods: {
setValue (value) {
this.value = value
}
}
});
<script src="https://cdn.jsdelivr.net/vue/1.0.16/vue.js"></script>
<div id="app">
<my-input :in="value" #on-out="setValue"></my-input>
{{ value }}
</div>
<template id="input-template">
<input :value="value" #input="inputOccurred">
</template>
I made a material design input field as a Vue component. I listen for the focus event and run a function to check the value every time the user focuses out. Here's the code:
<template>
<span class="h-input-container">
<input :type="type" :name="name" v-validate="validation"
#focusout="classHandle" :id="id" :value="value">
<p :class="focusClass"><i :class="icon"></i> {{placeholder}}</p>
</span>
</template>
<script>
export default {
mounted: function(){
if (this.value != '') {
this.focusClass = 'focused'
}
},
props: {
placeholder: {
default: ''
},
name: {
default: 'no-name'
},
type: {
default: 'text'
},
validation: {
default: ''
},
icon: {
default: ''
},
id: {
default: ''
},
value: {
default: ''
}
},
data: function(){
return {
focusClass: '',
}
},
methods: {
classHandle: function(event){
if (event.target.value != '') {
this.focusClass = 'focused'
} else {
this.focusClass = ''
}
}
}
};
</script>
I pass the value as a prop called value and I've used that value for the input field using :value="value". The thing is, every time the method classHandle runs, the input field's value disappears. I can't figure out why.
To clarify the accepted answer, Vue does not "refresh" the value when the focusout handler fires. The property, value, never changed, the input element's value changed.
Changing the focusClass forces Vue to re-render the component to the DOM. Because you've told Vue to use the property, value, as the value of the input via :value="value", it uses the current state of the property, which has never changed as stated above, to render the component and whatever you typed in the input disappears.
The accepted answer goes on to state that you should fix this by updating this.value. In a component, Vue will allow you to do that but it will throw a warning in the console.
[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"
Properties of components in Vue are just like function arguments in javascript. Inside the component, you can change them, but that change is limited to the scope of the component. If the parent component ever has to re-render, then the property "value" of your input will be redrawn using the parent's version of the value, and you will lose your changes.
Component communication in Vue "props down, events up". In order to change the value outside your component, you have to $emit it. Here is a version of your component that works, without throwing any warnings, and properly emits your changes.
{
props: {
placeholder: {
default: ''
},
name: {
default: 'no-name'
},
type: {
default: 'text'
},
validation: {
default: ''
},
icon: {
default: ''
},
id: {
default: ''
},
value: {
default: ''
}
},
template:`
<div class="h-input-container" style="background-color: lightgray">
<input :type="type" :name="name"
#focusout="classHandle" :id="id" :value="value" #input="$emit('input', $event.target.value)" />
<p :class="focusClass"><i :class="icon"></i> {{placeholder}}</p>
</div>
`,
data: function() {
return {
focusClass: '',
}
},
methods: {
classHandle: function(event) {
if (event.target.value != '') {
this.focusClass = 'focused'
} else {
this.focusClass = ''
}
}
}
}
I've put together an example to demonstrate the differences in the two approaches.
Typically, I would not use :value="value" #input="$emit('input', $event.target.value)" and use v-model instead, but you are using :type="type" as well, and Vue will throw a warning about using v-model with a dynamic type.
Your current component do not seems to refresh this.value when you make change in the input. Vue cause a component refresh when you focusOut, and because your value is not updated, it shown empy. To resolve your problem, you need to update this.value on event input
new Vue(
{
el: '#app',
props: {
placeholder: {
default: ''
},
name: {
default: 'no-name'
},
type: {
default: 'text'
},
validation: {
default: ''
},
icon: {
default: ''
},
id: {
default: ''
},
value: {
default: ''
}
},
data: function() {
return {
focusClass: '',
}
},
methods: {
updateValue(event) {
this.value = event.target.value
},
classHandle: function(event) {
if (event.target.value != '') {
this.focusClass = 'focused'
} else {
this.focusClass = ''
}
}
}
});
<script src="https://vuejs.org/js/vue.min.js"></script>
<div id="app">
<span class="h-input-container">
<input :type="type" :name="name" v-validate="validation"
#focusout="classHandle" :id="id" :value="value" #input="updateValue" />
<p :class="focusClass"><i :class="icon"></i> {{placeholder}}</p>
</span>
</div>
So you should put your attention on:
#input="updateValue"
And
updateValue(event) {
this.value = event.target.value
}