Vue3でファイルアップロード&モーダルプレビュー
Vue3の<scrpt setup lang="ts">
の練習用にファイルアップロード機能を実装。バリデーションは未実装です。ref
と reactive
でどちらを使うべきか迷いましたが、ref
を使った方がよさそうだったのでこちらを選択しました。
Github公開してあります。
vue3_fileupload_demo
https://github.com/neru-ne/vue3_fileupload_demo
ref()について
最初こんがらがってわからなかったのですが、<script setup lang="ts"></script
>の中では、refで定義したものに関しては、.value
でアクセスする必要があります。<template></template>
の中では、.value
なしでアクセスできました。
<script setup lang="ts">
const showModal = ref(false);
//アクセスしたい場合 .valueが必要
showModal.value = true
</script>
<template>
//templateの中では、.valueなしでアクセスできる。
<Modal v-if="showModal" @close="showModal = false">
</Modal>
</template>
イベントの型
clickなどのイベントの型はe:Event
でいけるようです。e.target
の場合はif (e.target instanceof HTMLInputElement)
で囲むと大丈夫でした。
// 画像データ等取得
const onImageUploaded = (e: Event) => {
if (e.target instanceof HTMLInputElement) {
if (e.target.files) {
const image = e.target.files[0];
const _thisId = e.target.id;
let _thisIndex = _thisId.split("__");
let _thisIndexNumber = Number(_thisIndex[1]);
createImage(image, _thisIndexNumber);
}
}
};
e.currentTargetを取得してgetAttributeを取得しようとしたら怒られた
e.currentTarget
の時、プロパティ 'getAttribute' は型 'EventTarget' に存在しません。
と怒られてしまいました。
その時はas HTMLElement
を追記すると良いようです。
const _this = e.currentTarget as HTMLElement;
const dataFileId = _this.getAttribute("data-fileId");//怒られない
アップロードされたファイル情報を取得
//画像入れ込み
const createImage = (image: File, _thisIndexNumber: number) => {
//FileReader.readAsDataURL()を使用してプレビュー用の画像データを取得。
const reader = new FileReader();
reader.readAsDataURL(image);
reader.onload = () => {
file.value.selectedFile[_thisIndexNumber].name = image.name;
file.value.selectedFile[_thisIndexNumber].image = reader.result;
};
};
// 画像データ等取得
const onImageUploaded = (e: Event) => {
if (e.target instanceof HTMLInputElement) {
if (e.target.files) {
const image = e.target.files[0];
const _thisId = e.target.id;
let _thisIndex = _thisId.split("__");
let _thisIndexNumber = Number(_thisIndex[1]);
createImage(image, _thisIndexNumber);
}
}
};
//中略
<div class="file-item-add-button">
<input
type="file"
:id="n.fileId"
:name="n.fileId"
class="file_input"
@change="onImageUploaded"
/>
<Button :data-fileId="n.fileId" :disabled="false">
ファイルを選択
</Button>
</div>
プレビューボタンを押して画像プレビューを出す
//画像のプレビュー
const previewImage = (e: Event) => {
e.preventDefault();
const _this = e.currentTarget as HTMLElement;
const dataFileId = _this.getAttribute("data-fileId");
if (dataFileId) {
let _thisIndex = dataFileId.split("__");
let _thisIndexNumber = Number(_thisIndex[1]);
file.value.previewImage = file.value.selectedFile[_thisIndexNumber].image;
}
//画像が存在していたらモーダルあげる
if (file.value.previewImage) {
showModal.value = true;
}
};
//中略
<Button
:data-fileId="n.fileId"
@click="previewImage"
:disabled="!n.image"
>
プレビュー
</Button>
//中略
<!-- モーダル -->
<transition name="modal">
<Modal v-if="showModal" @close="showModal = false">
<template v-slot:content>
<div v-if="file.previewImage">
<img :src="typeof file.previewImage === 'string' ? file.previewImage : ''" alt="" />
</div>
</template>
</Modal>
</transition>
<!-- モーダル -->
モーダルのアニメーション
フワッと現れるモーダルアニメーションはvueの<transition>
で実装しました。
下記サイトの方法を使わせていただきました。🙇
【Vue.js】モーダルの外側をクリックした時に閉じる方法 | ゆうやの雑記ブログ
https://yuyauver98.me/vue-modal-selfclose/
添付ファイルのセルを1つ追加する
file.value.selectedFile
に新しい配列を追加します。file.value.selectedFile
の配列が増えると、v-for
でループしている<div class="file-item">
も増えていきます。
//添付ファイルのセルを1つ追加
const addFileIttem = (e: Event) => {
e.preventDefault();
let fileValue = file.value;
let selectedFile = fileValue.selectedFile;
const selectedFileLength = selectedFile.length;
//selectedFileにpush
const fileArray = {
id: selectedFileLength,
fileId: "file_input__" + selectedFileLength,
name: fileValue.defaultName,
image: null,
};
selectedFile.push(fileArray);
file.value.selectedFile = selectedFile;
};
//中略
<div class="file">
<div class="file-item" v-for="n in file.selectedFile" :key="n.id">
// file.selectedFileの分だけループ
</div>
</div>
//中略
//5つまで増やすことができ、セルが5つになるとボタンは非活性になる
<Button
@click="addFileIttem"
:disabled="5 <= file.selectedFile.length"
>
ファイルの追加
</Button>
参考サイト
【Vue.js 3.2】<script setup>
構文がすごくすごい
https://zenn.dev/azukiazusa/articles/676d88675e4e74
【Vue.js】ref と reactive どっちを使う?
https://zenn.dev/azukiazusa/articles/ref-vs-article
【Vue.js】モーダルの外側をクリックした時に閉じる方法 | ゆうやの雑記ブログ
https://yuyauver98.me/vue-modal-selfclose/
FileReader.readAsDataURL() – Web API | MDN
https://developer.mozilla.org/ja/docs/Web/API/FileReader/readAsDataURL