Skip to content

Instantly share code, notes, and snippets.

@psychonetic
Last active April 4, 2020 07:02
Show Gist options
  • Save psychonetic/9b01d2b1eb04b9654d8c2627a2b1bcb5 to your computer and use it in GitHub Desktop.
Save psychonetic/9b01d2b1eb04b9654d8c2627a2b1bcb5 to your computer and use it in GitHub Desktop.
Vue-Dropzone and Vee-Validate
// Custom Validator to add errors into vuex store and also handle laravel form errors.
import { mapGetters } from 'vuex';
import { CLEAR_FORM_ERRORS, ERROR } from '../vuex/types';
import Vue from 'vue';
export default {
data() {
return {
errors: [],
keepServerErrors: ['unique', 'boolean', 'exists', 'regular_chars', 'identifier', 'date'],
};
},
mounted() {
Object.keys(this.fields).map((field) => {
Vue.set(this.errors, field, undefined);
});
},
computed: {
...mapGetters([
'formErrors',
]),
},
methods: {
hasServerFormErrors() {
if (this.formErrors.length === 0) {
return false;
}
return true;
},
resetFormErrors() {
this.$store.dispatch(CLEAR_FORM_ERRORS).then(() => {
this.errors = [];
this.$validator.errors.clear();
});
},
addServerErrors() {
if (this.formErrors.hasOwnProperty(0)) {
this.formErrors[0].map((failed) => {
const field = failed.field;
if (this.fields.hasOwnProperty(field)) {
if (this.keepServerErrors.indexOf(failed.rule) != -1) {
this.errors[field] = failed.msg;
this.fields[field].valid = false;
this.fields[field].invalid = true;
}
}
});
}
},
collectErr(key) {
if (this.errors[key] !== undefined) {
return this.errors[key];
}
return undefined;
},
hasAnyError(arr) {
for (const index in arr) {
if (this.errors[arr[index]] !== undefined) {
return true;
}
}
return false;
},
errorCount() {
return Object.keys(this.errors).length;
},
hasError(key) {
if (this.errors[key] !== undefined) {
return true;
}
return false;
},
delNullValues(object, keys) {
for (const property in object) {
if (object.hasOwnProperty(property) && keys.indexOf(property) !== -1) {
if (object[property] === null || object[property] === '') {
object[property] = undefined;
}
}
}
return object;
},
},
watch: {
veeErrors: {
handler(val, oldVal) {
this.errors = [];
val.items.map((err) => {
if (this.fields.hasOwnProperty(err.field) !== -1) {
this.errors[err.field] = err.msg;
}
});
this.addServerErrors();
},
deep: true,
},
formErrors: {
handler() {
this.errors = [];
this.addServerErrors();
},
},
},
};
// Laravel Custom FormRequest.
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class CoreFormRequest extends FormRequest {
protected $failed = [];
/**
* Add a field with detailed information about failed fields.
* @param \Illuminate\Contracts\Validation\Validator
*/
public function withValidator($validator)
{
$validator->after(function ($validator) {
$errors = $validator->errors()->getMessages();
$obj = $validator->failed();
foreach($obj as $input => $rules) {
$i = 0;
$fail = [];
foreach($rules as $rule => $ruleInfo){
$key = $rule;
$fieldParts = explode(".", $input);
$clientInput = $fieldParts[0];
if(count($fieldParts) > 1) {
$clientInput = $fieldParts[0].'['.$fieldParts[1].']';
}
$fail = [
'rule' => strtolower($rule),
'field' => $clientInput,
'msg' => $errors[$input][$i]
];
$i++;
}
$this->failed[] = $fail;
}
if(count($this->failed) > 0) {
$validator->errors()->add('details', $this->failed);
}
});
}
}
<v-uploader
:url="uploadUrl"
:mimes="uploader.mimes"
:remove-url="removeUrl"
v-model="form.mandat"
v-validate="'required'"
data-vv-name="mandat"
data-vv-as="SEPA-Mandat"
:has-error="hasError('mandat')"
:error="collectErr('mandat')"
label="SEPA-Mandat">
</v-uploader>
<template>
<div class="input--group">
<label class="input--group--label">{{label}}</label>
<dropzone id="dropzone"
ref="dropzone"
:options="options"
:name="name"
@vdropzone-success="uploadDone"
@vdropzone-removed-file='remove'
:destroyDropzone="true"/>
<p class="input--group--error" v-if="hasError">{{ error }}</p>
</div>
</template>
<script>
import Dropzone from 'nuxt-dropzone'
import 'nuxt-dropzone/dropzone.css'
import axios from 'axios';
import input from "../../mixins/input";
export default {
props: {
label: {
type: String,
required: true,
},
url: {
type: String,
required: true,
},
removeUrl: {
type: String,
required: true,
},
maxFiles: {
default: 1,
},
mimes: {
default: null,
type: String,
},
},
name: 'VUploader',
mixins: [input],
components: {
Dropzone,
},
created() {
this.options.url = this.url;
this.options.maxFiles = this.maxFiles;
this.options.acceptedFiles = this.mimes;
},
data() {
return {
options: {
url: null,
maxFiles: 1,
maxFilesize: 5,
acceptedFiles: null,
addRemoveLinks: true,
headers: {
'Access-Control-Allow-Origin': '*',
},
dictDefaultMessage: this.$t('dropzone.defaultMessage'),
dictFallbackMessage: this.$t('dropzone.fallbackMessage'),
dictFallbackText: this.$t('dropzone.fallbackText'),
dictFileTooBig: this.$t('dropzone.fileTooBig', {
filesize: '{filesize}',
maxFilesize: '{maxFilesize}'
}),
dictInvalidFileType: this.$t('dropzone.invalidFileType'),
dictResponseError: this.$t('dropzone.responseError', {statusCode: '{statusCode}'}),
dictCancelUpload: this.$t('dropzone.cancelUpload'),
dictCancelUploadConfirmation: this.$t('dropzone.cancelUploadConfirmation'),
dictRemoveFile: this.$t('dropzone.removeFile'),
dictMaxFilesExceeded: this.$t('dropzone.maxFilesExceeded'),
},
uploaded: [],
};
},
methods: {
remove(file) {
this.uploaded.forEach((item) => {
if(item.file === file.upload.uuid) {
this.$emit('input', null)
axios.delete(`${this.removeUrl}?items=${item.id}`).then(() => {
console.debug('Deleted file.')
}).catch(() => {
console.error('Could not delete file.')
})
}
});
},
uploadDone(file, response) {
this.uploaded.push(
{
file: file.upload.uuid,
id: response.file.id
}
)
this.$emit('input', this.uploaded);
}
}
};
</script>
<style lang="css" scoped>
.vue-dropzone {
width: 50%;
}
</style>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment