Skip to content
Advertisement

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.

Advertisement

Answer

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.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement