日記
会社で有料セミナーにいってもいいと言われると、お高い普段なかなかいけないような勉強会を探したりしています。
これいいな!と思ったらちょっと高かったりして敬遠したことが何度かあったのに、いざ探すとなるとなかなか見つからないものですね。
本題
Vue.jsでファイルアップロード処理をつくった際にいろいろと勉強になったので、作成の過程を残しておくよ!
正直なところ、バッドプラクティスみたいなものもいっぱいあるかもしれないし、未だにいろいろ悩んでるんだ。
なので指摘をいだけるととすごくうれしいです!
今回つくりたいもの
ファイルとテキスト項目をボタンを押下することで、サーバに送る処理をVueコンポーネントを使ってつくるよ!
環境
Vue.jsはwebpack(laravel-mix)を使ってコンパイルしているよ!
まずはHTML
Vue.jsではコンポートとしてHTMLをがりがり書けるだけに、どこまでをVue.jsで書くのか、というのは悩みどころですね。
今回、大まかなレイアウトはHTMLファイルとして、直接書くことにします。
「タイトル」のテキスト入力欄があるだけのシンプルなフォームになります。
シンプルフォーム「create.html」
<form class="simple-form">
<h2>ファイルアップロード</h2>
<label for="title">タイトル</label>
<input type="text" name="title">
<button>投稿する</button>
</form>
上記のHTMLにはコードを見やすくするため、スタイルのためのDOM要素だったり、classは一切書いていません。
ここに、ファイルをドラッグ&ドロップでファイル選択を行うことのできる部品を追加する予定です。
未来のcreate.html
<form class="simple-form">
<h2>ファイルアップロード</h2>
<label for="title">タイトル</label>
<input type="text" name="title">
<drop></drop>
<button>投稿する</button>
</form>
ドラッグ&ドロップをしたタイミングでアップロードする機能はサンプルが結構あったりするのですが、今回は「タイトル」と「ファイル」を投稿するボタンを押したタイミングで合わせてサーバーに送信するところが厄介な部分になります。
最初のmain.js
さて、早速最初のプログラムmain.js
を書いていきます。
main.js
には、ドラッグ&ドロップのコードは書かずに、ファイルをサーバーにAjaxで送る処理を書くことにします。
まずは、いつも通りにVueインスタンスを生成します。
生成する際には、simple-form
クラスを要素に指定します。
main.js
new Vue({
el:'.form',
})
これだけですが、大きな一歩になります。
さきほどのcreate.html
からmain.js
を読み込めるようにして置くのを忘れないようにします。
サーバーにデータを送信する処理を書く
では、サーバーにデータを送信する処理を先に書いていきます。
まずは、button
タグに@click
を追記してボタンをクリックしたときの処理を書くことにします。
button
タグのtype属性を「button」にすることを忘れて、たまにあれーとなるので気をつけてください。
Ajaxではなく普通のpostを行ったりします。
create.html
<form class="simple-form">
<h2>ファイルアップロード</h2>
<label for="title">タイトル</label>
<input type="text" name="title">
<button type="button" @click="onSubmit">投稿する</button>
</form>
続いてmain.js
です。
とりあえずclickイベントが発生できているか確認するため、console.log
を行うだけの処理onSubmit
を書くことにします。
main.js
new Vue({
el:'.form',
methods:{
onSubmit:function(){
console.log('test');
},
},
問題がなければ、サーバー送信処理を書くことにします。
this.title
とthis.files
はこの後に追加するよ!
main.js
new Vue({
el:'.form',
methods:{
onSubmit:function(){
console.log('test');
let data = new FormData;
data.append('title', this.title);
for(let i = 0; i < this.files.length; i++){
data.append('files[]', this.files[i]);
}
axios.post('/file',data)
.then((response) => {
console.log(response.data);
})
.catch((error) => {
console.log(error);
})
},
},
サーバー送信処理は、FormDataとaxiosを使っておこなっています。
ファイルを送る際にContent-Type=multipart/form-data
を指定しよう!とか気にしなきゃいけないのですが、FormDataを使うとそのへんを勝手にやってくれるみたいです。
axiosの詳細な説明はあまりできないので、ググってほしいんだ!
dataを追加する
さきほど、サーバーに送信する処理を書きましたが、this.title
とthis.files
を何も定義してないので、値が入ってくることがありません。
なので、データバインディングを使って、フォームの項目とひも付けてあげます。
main.js
に、こんな感じでdataを追加します。
main.js
new Vue({
el:'.form',
data:{
title:'',
files:[]
},
methods:{
onSubmit:function(){
},
},
タイトルをv-model
でひも付けてあげます。
create.html
<form class="simple-form">
<h2>ファイルアップロード</h2>
<label for="title">タイトル</label>
<input type="text" name="title" v-model="title">
<button type="button" @click="onSubmit">投稿する</button>
</form
これで、タイトルに何かを入力すれば、this.title
に値が反映され、投稿ボタン
を押したときにサーバーにタイトルを送るようになります。
filesを送る
肝心のファイルですが、ドラッグ&ドロップの機能はコンポートに記載するので、現段階でひも付ける対象がなかったりします。
後ほどコンポーネントで作成していくのですが、作成する前にファイルを取得するにあたって、特殊なことがあるのでここに記載します。
以下は、コンポーネントではなくcreate.html
に、直接ファイル選択を書いた例になります。
create.html
<form class="simple-form">
<h2>ファイルアップロード</h2>
<label for="title">タイトル</label>
<input type="text" name="title" v-model="title">
<input type="file" mulitple="multiple">
<button type="button" @click="onSubmit">投稿する</button>
</form
このように書くと、title
と同様に、v-modelを使ってデータバインディングじゃ!となるに違いありません。
create.html
<input type="file" mulitple="multiple" v-model="file">
しかし、input type="file"
に対しては、v-modelでデータバインディングすることはできないです。
理由はわからないけれども、公式でいってるんだよ!
なので、inpuy type="file"
の値が何かしらかわったときに発生するchange
イベントを使う必要があります。
create.html
<input type="file" mulitple="multiple" @change="onDrop" >
そして、Vueには以下のようにonDrop
メソッドを追加することでデータが取得できるようになります。
main.js
new Vue({
el:'.form',
data:{
title:'',
files:[]
},
methods:{
onSubmit:function(){
},
onDrop:function(event){
let file = event.target.files
}
},
`
ようやく本題 コンポートを作成する
さてさて、ようやく本題です。
前述の通り、create.html
にinput
タグを直接書いていくのももちろんいいんだけれどもコンポートにしていくよ。
コンポートにすることで、 いろんなページで使い回せたりするんだよ!
drop.vueをつくる
Vue.jsのコンポートですが、生成方法だったり書き方がいろいろあったりします。
※以前、こちらで生成方法を書いたりしました。
www.tohuandkonsome.site
ここでは、レイアウトもDOM要素も、Vueも部品ごとにひとつのファイルにかける方法で書いてみるよ!
drop.vue
<template>
<div>
<input type="file" @change="onDrop">
</div>
</template>
<script>
export default {
methods:{
onDrop:function(event){
let fileList = event.target.files
}
},
}
</script>
<style lang="scss">
</style>
まずは、シンプルにファイル選択だけができる処理を書きました。
main.js
側にコンポーネントとしてdrop.vue
を登録してあげます。
main.js
const Drop = require('./components/drop');
new Vue({
el:'.form',
data:{
title:'',
files:[]
},
methods:{
onSubmit:function(){
},
},
components:{
'drop':Drop
}
こうすることで、create.html
でdropタグが使えるようになります。
create.html
<form class="simple-form">
<h2>ファイルアップロード</h2>
<label for="title">タイトル</label>
<input type="text" name="title" v-model="title">
<button type="button" @click="onSubmit">投稿する</button>
<drop></drop>
</form
これでブラウザでみてみると、以下のように表示されることが確認できます。
いつも肝心なところまでたどり着かないので不甲斐ないのですが、次回に続きます。
www.tohuandkonsome.site