子コンポーネントにrefで値を設定してやれば
this.\$refsから子コンポーネントの状態を取ってくることができます。
今回の場合はthis.\$refs["vali-files"].\$vからvuelidateの機能が利用できる。
利点は子に各vuelidateのルールを切り分けるので、フォームが増えても1ファイル1ルールで肥大化が避けられます。
親コンポーネント
<template>
<form>
<Files ref="vali-files" />
<v-btn class="mr-4" @click="mySubmit">出力</v-btn>
<v-btn @click="myClear">リセット</v-btn>
</form>
</template>
<script>
import Files from "~/components/files.vue";
import { mapState, mapMutations, mapGetters, mapActions } from "vuex";
export default {
components: { Files },
data({}) {
return {
valis: {},
comps: {},
};
},
computed: {
...mapState("state-cms", ["$postData"]),
},
methods: {
mySubmit() {
let errorLen = 0;
for (let key in this.valis) {
this.valis[key].$touch();
if (this.valis[key].$invalid) errorLen++;
}
//失敗… returnで中断
if (errorLen) return;
//成功!以下サーバー処理
//this.exportFiles();
},
myClear() {
for (let key in this.valis) {
this.valis[key].$reset();
}
this.comps["vali-files"].files = [];
},
},
created() {},
mounted() {
for (let key in this.$refs) {
if (key.match(/^(vali-)/) !== null) {
this.comps[key] = this.$refs[key];
this.valis[key] = this.$refs[key].$v;
}
}
},
};
</script>
子コンポーネント
<template>
<v-container>
<v-row
class="text-center"
@dragover.prevent
@dragenter="onDragEnter"
@dragleave="onDragLeave"
@drop="onDrop"
>
<v-file-input
v-model="files"
color="deep-purple accent-4"
counter
label="参照するJSON:例… ~archive/000xxxx.json ※ドラッグ&ドロップ可"
multiple
required
placeholder="あなたはファイルを選ぶ"
prepend-icon="mdi-paperclip"
outlined
:show-size="1000"
:background-color="isDragging ? 'blue' : 'null'"
:error-messages="filesErrors($v.files)"
@change="$v.files.$touch()"
@blur="$v.files.$touch()"
>
<template v-slot:selection="{ index, text }">
<v-chip
v-if="index < 2"
color="deep-purple accent-4"
dark
label
small
>{{ text }}</v-chip
>
<span
v-else-if="index === 2"
class="overline grey--text text--darken-3 mx-2"
>+{{ files.length - 2 }} File(s)</span
>
</template>
</v-file-input>
</v-row>
</v-container>
</template>
<script>
import { mapState, mapMutations, mapGetters, mapActions } from "vuex";
import { validationMixin } from "vuelidate";
import { required, maxLength, email } from "vuelidate/lib/validators";
export default {
mixins: [validationMixin],
validations: {
files: {
required,
//jsonかどうか正しいかどうかを判別する
async chkJson(val) {
if (!val.length) return false;
const jsonFlg = val[0].type.match(/json/);
if (jsonFlg === null) {
return false;
} else {
const reader = new FileReader();
await this.files[0].text().then((data) => {
this.$updatePostData({ key: "srcJson", val: data });
this.$updatePostData({ key: "srcJsonName", val: val[0].name });
});
return true;
}
},
},
},
props: [],
data({}) {
return {
isDragging: false,
dragCount: 0,
files: [],
};
},
computed: {},
methods: {
...mapMutations("state-cms", ["$updatePostData"]),
onDrop(e) {
e.preventDefault();
e.stopPropagation();
this.isDragging = false;
const _files = e.dataTransfer.files;
for (const file in _files) {
if (!isNaN(file)) {
//filesはファイル以外のデータが入っており、ファイルの場合のみキー名が数字になるため
this.files.push(_files[file]);
}
}
this.files =
this.files.length > 1
? this.files.splice(1, this.files.length - 1)
: this.files;
},
onDragEnter(e) {
e.preventDefault();
this.isDragging = true;
this.dragCount++;
},
onDragLeave(e) {
e.preventDefault();
this.dragCount--;
if (this.dragCount <= 0) {
this.isDragging = false;
}
},
filesErrors(_files) {
const errors = [];
if (!_files.$dirty) return errors;
if (!_files.required) {
errors.push("Form is required.");
return errors;
}
!_files.chkJson && errors.push("JSONじゃない!!");
return errors;
},
},
};
</script>