Search code examples
javascriptvue.jsdata-bindingvuejs22-way-object-databinding

Clear input VUE component data from parent


I'm a newbie of Vue, and I'm trying to simply clear the data of input component once I've submitted, but it seems I'm missing something, because since it's parent data is cleared, I still see the filled value of the input component.

Here is a living example.

I've set to the input child component v-model="title" from it's parent wrapper. Once I submit the data to the parent, I call addItem and in the end, I supposed to clear the data model just by clear it this.title = '', but I probably do something wrong on how to bind data from parent to child.

And above the code, starting from the parent component:

<template>
  <form @submit="addItem" class="todo-insert">
    <input-item
      icon="create"
      name="title"
      placeholder="Add a ToVue item..."
      v-model="title"
    />
    <button-item tone="confirm" class="todo-insert__action">
      Aggiungi
    </button-item>
  </form>
</template>

<script>
import ButtonItem from '@vue/Form/ButtonItem/ButtonItem.vue'
import InputItem from '@vue/Form/InputItem/InputItem.vue'
import uuid from 'uuid'

export default {
  name: 'TodoInsert',
  components: {
    ButtonItem,
    InputItem
  },
  data () {
    return {
      title: ''
    }
  },
  methods: {
    addItem (e) {
      e.preventDefault()
      const todo = {
        id: uuid.v4(),
        isComplete: false,
        title: this.title
      }
      this.$emit('add-todo', todo)
      this.title = ''
    }
  }
}
</script>
<style lang="scss" scoped src="./TodoList.scss"></style>

This is the child input component:

<template lang="html">
  <label class="input">
    <div v-if="label" class="input__label text-sans text-sans--label">
      {{ label }}
    </div>
    <div class="input__row">
      <input
        :autocomplete="autocomplete"
        :class="[hasPlaceholderLabel, isDirty]"
        :name="name"
        :placeholder="placeholder"
        class="input__field"
        type="text"
        v-on:input="updateValue($event.target.value)"
        v-on:blur="updateValue($event.target.value)"
      >
      <div v-if="placeholderLabel" class="input__placeholder text-sans text-sans--placeholder">
        {{ placeholderLabel }}
      </div>
      <div v-if="icon" class="input__icon-area">
        <icon-item
          :name="icon"
        />
      </div>
      </div>
  </label>
</template>

<script>
import IconItem from '../../IconItem/IconItem.vue'

export default {
  name: 'InputItem',
  props: {
    autocomplete: {
      type: String,
      default: 'off'
    },
    icon: String,
    label: String,
    name: {
      type: String,
      default: 'input-text'
    },
    placeholder: String,
    placeholderLabel: String
  },
  computed: {
    hasPlaceholderLabel () {
      return this.placeholderLabel ? 'input__field--placeholder-label' : ''
    },
    isDirty () {
      // return this.$attrs.value ? 'input__field--dirty' : ''
      return 'input__field--dirty'
    }
  },
  methods: {
    updateValue: function (value) {
      this.$emit('input', value)
    }
  },
  components: {
    IconItem
  }
}
</script>
<style lang="scss" src="./InputItem.scss"></style>

What am I missing?


Solution

  • Your child component is bound unidirectionally. It means that it can change the value, but does not receive any update from the parent component. To receive updates, you need to receive the property value in your child:

    props: {
      value: String
    }
    

    Then, you need to pass the value received to the input :

    <input
        :value="value"
        :autocomplete="autocomplete"
        :class="[hasPlaceholderLabel, isDirty]"
        :name="name"
        :placeholder="placeholder"
        class="input__field"
        type="text"
        v-on:input="updateValue($event.target.value)"
        v-on:blur="updateValue($event.target.value)"
      >
    

    Now the input should update when the parent component changes the value