v-file-inputでファイル選択後にもう一度選択画面を開き、「キャンセル」をクリックするとmodelが空になってしまう問題に諸般の事情で対処してみた。
<template>
<v-file-input
label="ファイル選択"
@change="loadFile"
:clearable = false
v-model="fileName"
></v-file-input>
</template>
<script>
data () {
return {
fileName: null,
}
},
methods: {
loadFile(e) {
if(e != null && e != undefined){
const file = e
let reader = new FileReader()
reader.readAsText(file)
reader.onload = () => {
this.loadedFunction(reader.result)
}
}
},
loadedFunction () {
//ロード後の処理
}
}
</script>
だいたい上記のような実装になると思う。
これにキャンセル対策を施してみる。
<template>\
<v-file-input
label="ファイル選択"
@change="loadFile"
:clearable = false
v-model="fileData"
></v-file-input>
</template>
<script>
data () {
return {
fileData: null,
//退避させる変数を用意
prevFileData: null,
}
},
methods: {
loadFile(e) {
if(e != null && e != undefined){
const file = e
let reader = new FileReader()
reader.readAsText(file)
reader.onload = () => {
// 正常に読み込まれた場合、そのデータを保存しておく
this.prevFileData = e
this.loadedFunction(reader.result)
}
} else {
//キャンセルされたときの処理
let reader = new FileReader()
//保存しておいたFileオブジェクトからBlobに変換、Fileオブジェクトにすることで参照渡しではなくコピーする
reader.readAsDataURL(this.prevFileData)
reader.onload = () => {
let blob = this.dataURItoBlob(reader.result)
let file = new File([blob], this.prevFileData.name, { type:this.prevFileData.type, lastModified:this.prevFileData.lastModified })
//v-modelへ反映する
this.fileData= file
}
}
},
loadedFunction () {
//ロード後の処理
}
//dataURIをBlobに変換する関数
dataURItoBlob(dataURI) {
let byteString = atob(dataURI.split(',')[1])
let mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
let ab = new ArrayBuffer(byteString.length);
let ia = new Uint8Array(ab);
for (let i = 0; i < byteString.length; i++) {
ia[i] = byteString.charCodeAt(i);
}
return new Blob([ab], {type: mimeString});
},
}
</script>
要はv-model以外に退避させておいてキャンセルだったらv-modelに退避させておいたデータを反映させるというやり方。
なんだかリソース食うなーという感じ。
これが正しいのかどうかわかりませんけど一応。