Mav
Mav

Reputation: 1190

Prop value lost after running a method Vuejs

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>&nbsp;{{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.

Upvotes: 2

Views: 3402

Answers (2)

Bert
Bert

Reputation: 82459

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>&nbsp;{{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.

Upvotes: 3

Gabriel Robert
Gabriel Robert

Reputation: 3080

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>&nbsp;{{placeholder}}</p>
</span>
</div>

So you should put your attention on:

@input="updateValue"

And

updateValue(event) {
    this.value = event.target.value
}

Upvotes: 0

Related Questions