Open apotheosis91 opened 3 years ago
Having the same one. @apotheosis91 did you find some solution here?
@stefanaz, No, I had to create a custom formulate input, with dropzone.js under the hood.
@stefanaz, No, I had to create a custom formulate input, with dropzone.js under the hood.
Mind to share? I will be implementind upload in the next few days and will probably face that same issue
@vicenterusso,
Sure, here's what I did:
1) Create FormulateDropzone.vue
component with the following content:
(I also changed the default dropzone styles and markup, but I omit that part to make the code clearer)
<template>
<div ref="dz" :id="context.attributes.id" class="dropzone">
<div class="dz-message">
<div v-if="!hasFiles">
{{ dropzoneLabel }}
</div>
</div>
</div>
</template>
<script>
import Dropzone from 'dropzone';
import 'dropzone/dist/min/dropzone.min.css';
Dropzone.autoDiscover = false;
export default {
name: "FormulateDropzone",
props: {
context: {
type: Object,
required: true
},
initialFiles: {
type: Array,
default: () => []
},
maxFiles: {
type: Number,
default: null
},
maxFilesize: {
type: Number,
default: null
},
acceptedFiles: {
type: String,
default: null
},
dropzoneOptions: {
type: Object,
default: () => ({})
},
dropzoneLabel: {
type: String,
default: 'Drop files here or click to upload'
}
},
mounted() {
this.context.model = this.context.model || [];
this.dropzone = new Dropzone(this.$refs.dz, this.options);
this.dropzone
.on('addedfile', this.onFileAdded)
.on('removedfile', this.onFileRemoved)
.on('success', this.onFileUploaded);
// Display initial files
// @see https://github.com/dropzone/dropzone/wiki/faq#how-to-show-files-already-stored-on-server
this.initialFiles.forEach((item) => {
const mockFile = {
name: item.filename,
size: item.size,
mediaId: item.id,
isMock: true
};
this.dropzone.displayExistingFile(mockFile, item.url, null, null, false);
});
// Dropzone's maxFiles option doesn't take into account initial mock files, so we need to reduce it (dropzone.js hack)
if (this.options.maxFiles !== null) {
this.dropzone.options.maxFiles -= this.initialFiles.length;
}
},
beforeDestroy() {
if (this.dropzone) {
this.dropzone.destroy();
}
},
computed: {
options() {
const defaults = {
url: '/api/admin/uploads',
method: 'post',
paramName: 'file',
headers: {
// 'X-XSRF-TOKEN': Cookies.get('XSRF-TOKEN')
},
addRemoveLinks: true,
maxFiles: this.maxFiles,
maxFilesize: this.maxFilesize, // in MB
acceptedFiles: this.acceptedFiles, // e.g. 'image/jpeg,image/png'
error: this.onError, // replace error handler here, to customize server error response handling
dictMaxFilesExceeded: `Maximum allowed number of files: ${this.maxFiles}` // for displaying correct maxFiles count (dropzone.js hack)
};
return {...defaults, ...this.dropzoneOptions};
},
hasFiles() {
return this.filesCount > 0;
}
},
data() {
return {
filesCount: 0
}
},
methods: {
onFileAdded(file) {
this.filesCount++;
},
onFileUploaded(file, response) {
// Handle the case when the file is successfully uploaded to the server here.
// In my case, the server returns id of the upload in the database.
// I store it in the mediaId property and then write the required data to the VueFormulate context.model
file.mediaId = response.id;
this.context.model.push({id: response.id, action: 'add'});
},
onFileRemoved(file) {
// Handle the case when the file is removed from the dropzone here.
// In my case, I write the required data to the VueFormulate context.model
// and then handle deletions on server side, after submitting the form with dropzone.
if (file.mediaId) {
this.context.model.push({id: file.mediaId, action: 'delete'});
}
this.filesCount--;
// When removing initial file, we need to correct maxFiles option (dropzone.js hack)
if (file.isMock && this.dropzone.options.maxFiles !== null) {
this.dropzone.options.maxFiles++;
}
},
onError(file, message, xhr) {
// Handle Errors here.
// I've modified the standard dropzone's error handler:
// https://github.com/dropzone/dropzone/blob/9dfbd74fd245736dc4051f34a43e9ac7126f764e/src/options.js#L677
// And when an error occurs, I remove the file preview from the dropzone
// and show error notifications using euvl/vue-notification
if (file.previewElement) {
file.previewElement.classList.add("dz-error");
// If the error was due to the XMLHttpRequest the xhr parameter will be present.
if (typeof message === 'string') {
message = [message];
} else if (xhr) {
message = this.parseUploadErrors(xhr);
} else {
message = ['Error :('];
}
for (let node of file.previewElement.querySelectorAll("[data-dz-errormessage]")) {
node.textContent = message.join('; ');
}
if (this.$notify && typeof this.$notify === 'function') {
this.dropzone.removeFile(file);
message.forEach((value) => {
this.$notify({
group: 'app',
type: 'error',
title: 'Error',
text: value
});
});
}
}
},
parseUploadErrors(error) {
// Parse server error response here and return array with error messages
return ['Error. Something wrong happened.'];
}
}
}
</script>
2) Register component in VueFormulate. In the file where you instantiate Vue add the following:
import FormulateDropzone from "./components/FormulateDropzone";
Vue.component('FormulateDropzone', FormulateDropzone);
Vue.use(VueFormulate, {
library: {
dropzone: {
classification: 'custom',
component: 'FormulateDropzone',
slotProps: {
component: ['initialFiles', 'maxFiles', 'maxFilesize', 'acceptedFiles', 'dropzoneOptions', 'dropzoneLabel']
}
}
},
});
3) Use it like any other FormulateInput components:
<formulate-form
v-model="formValues"
@submit="submitHandler"
>
<formulate-input
type="dropzone"
name="images"
label="Images"
help="Add some images"
dropzone-label="Click to upload files or drop them here"
:max-files="5"
:max-filesize="10"
accepted-files="image/jpeg,image/png"
:initial-files="[{id: 1, filename: 'file.jpg', size: '12345', url: 'path/to/file.jpg'}]"
></formulate-input>
<!-- Other inputs ... -->
</formulate-form>
@vicenterusso thanks! That is very nice one.
Thank you @apotheosis91 !
Describe the bug Using a File Input with "multiple" attribute and custom uploader, as shown in the documentation.
If the server responds with an error when you select a file, then when you try to upload another file, it appears in the list, but the actual upload is not performed.
To Reproduce Steps to reproduce the behavior:
+ Add File
button and select another file.Reproduction https://codepen.io/apotheosis91/pen/xxgGQKb
Expected behavior Newly added files after the server responds with an error should be uploaded.