As the documentation said:
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);
},
},
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
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.
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.filesproperty, because it does not return an Array as you might think, so you need to cast it from FileList to an Array usingArray.from.There might be some additional issues, having a live example would make it easier for me to fix it right away for you.