Vee-validate: handler for "input": "TypeError: files.every is not a function"

Created on 9 Oct 2017  ยท  4Comments  ยท  Source: logaretm/vee-validate

Versions:

  • VueJs: 2.4.4
  • Vee-Validate: 2.0.0.rc.18

Description:

As the documentation said:

  • Must emit an input event whenever the value changes.
  • Should have a data-vv-name or a name attribute defined.
  • Should have a data-vv-value-path attribute which denotes how to access the value from within that component (Needed for validateAll calls).

Parent Component template:

<div class="c-field">
  <file-input v-validate="'required|image'" data-vv-value-path="files" data-vv-name="attachment"></file-input>
</div>

Custom file component template:

<div
  @click="clickInputHandler"
  :class="{'has-file': showRemovedBtn}"
  class="c-file-input js-file-input">
  <div class="c-file-input__indicator">
    <span class="c-file-input__indicator__icon c-icon c-icon--attach"></span>
  </div>
  <div class="c-file-input__label js-file-input__label">{{ theFileName }}</div>
  <input
    type="file" @change="fileChangeHandler"
    name="attachment" class="c-file-input__field js-file-input__field">
  <div @click.stop="clearFileHandler" class="c-file-input__remove js-file-input__remove">
    <span class="c-file-input__remove__icon c-icon c-icon--remove-circle"></span>
  </div>
</div>

Custom file component script:

name: 'file-input',
  props: {
    labelPlaceholder: {
      type: String,
      default: 'No file choosen',
    },
    mustBeImage: {
      type: Boolean,
      default: false,
    },
  },
  data() {
    return {
      value: '',
      files: [],
      showRemovedBtn: false,
    };
  },
  computed: {
    theFileName() {
      if ((this.files.length > 0) && 'name' in this.files[0]) {
        return this.files[0].name;
      }
      return this.labelPlaceholder;
    },
  },
  methods: {
    clickInputHandler() {
      const theInput = this.$el.querySelector('.js-file-input__field');

      if (typeof theInput.click === 'function') {
        this.$el.querySelector('.js-file-input__field').click();
      } else if (typeof theInput.onclick === 'function') {
        this.$el.querySelector('.js-file-input__field').onclick();
      }
    },
    fileChangeHandler(e) {
      if (e.target.files.length > 0) {
        this.files = e.target.files;
        this.showRemovedBtn = true;
        this.$emit('input');
      } else {
        this.files = [];
        this.showRemovedBtn = false;
      }
    },
    wrapInputAndReset(el, wrapper) {
      // wrap input with form tag
      el.parentNode.insertBefore(wrapper, el);
      wrapper.appendChild(el);

      // reset input with form.reset()
      wrapper.reset();

      // place childNodes in document fragment
      const docFrag = document.createDocumentFragment();
      while (wrapper.firstChild) {
        const child = wrapper.removeChild(wrapper.firstChild);
        docFrag.appendChild(child);
      }

      // replace wrapper with document fragment
      wrapper.parentNode.replaceChild(docFrag, wrapper);

      this.files = [];
      this.showRemovedBtn = false;
    },
    clearFileHandler() {
      const el = this.$el.querySelector('.js-file-input__field');
      const wrapper = document.createElement('form');
      this.wrapInputAndReset(el, wrapper);
    },
  },

Steps To Reproduce:

Type error will show when ever the input change, fileChangeHandler will emit input.

Please let me know if there is something that wrong in my implementation, i've google it but no luck.

thank you very much

โ” question

Most helpful comment

There are multiple things that can go wrong here, first you should emit the input event with the current value of the component.

You would also need to ensure the component value is updated before you emit the event if you are going to reference it from this.

Additionally you need be careful when using input.files property, because it does not return an Array as you might think, so you need to cast it from FileList to an Array using Array.from.

// cast the FileList to an Array
this.files = Array.from(e.target.files);

// Emit the updated input event with the value.
this.$emit('input', this.files);

There might be some additional issues, having a live example would make it easier for me to fix it right away for you.

All 4 comments

There are multiple things that can go wrong here, first you should emit the input event with the current value of the component.

You would also need to ensure the component value is updated before you emit the event if you are going to reference it from this.

Additionally you need be careful when using input.files property, because it does not return an Array as you might think, so you need to cast it from FileList to an Array using Array.from.

// cast the FileList to an Array
this.files = Array.from(e.target.files);

// Emit the updated input event with the value.
this.$emit('input', this.files);

There might be some additional issues, having a live example would make it easier for me to fix it right away for you.

// cast the FileList to an Array
this.files = Array.from(e.target.files);

// Emit the updated input event with the value.
this.$emit('input', this.files);

@logaretm the error is gone, but the file is not validated

I've create live example here: https://codepen.io/ahmadmilzam/pen/rGdvwW

Thank you

Nvm, it is working now, thank you very much @logaretm !

But error message not deleted when i clear the file, only if i chose new file with the right extention and required is not showing when you focus out the custom field when the field is empty.

How can i achieve that behavior?

When you clear the files, you need to tell the validator that the value has changed, simply by emitting another input event.

this.files = [];
this.$emit('input', this.files);

which will display the required error afterwards.

https://codepen.io/anon/pen/veRoJr

Was this page helpful?
0 / 5 - 0 ratings

Related issues

Shchepotin picture Shchepotin  ยท  3Comments

MeltedFreddo picture MeltedFreddo  ยท  3Comments

yyyuuu777 picture yyyuuu777  ยท  3Comments

schel4ok picture schel4ok  ยท  3Comments

parweb picture parweb  ยท  3Comments