参考:https://blog.capilano-fw.com/?p=1189
##やりたい事
ユーザーのプロフィール編集機能を作成するにあたって
・プロフィール画像を画像を選択したらそれを即時に反映させる
・デフォルトのラジオボタンはデザインがダサいのでカスタマイズする
・アクセスした時点ではでは既に設定されているアイコンを表示する
###全体コード
edit.html.erb(一部)
<div class="field">
<%= f.label :image, "プロフィール画像" %><br />
<div id="img_field" @change="onFileChange($event)">
<img :src="imageData" v-if="imageData" class="edit-preview-image">
<img src=<%= current_user.image %> v-else class="edit-preview-image">
<label for="file-input" class="input-file-button">
ファイルを選択
</label>
<%= f.file_field :image, id: "file-input", accept: "image/*" %>
</div>
</div>
image_upload_vue.js
import Vue from 'vue/dist/vue.esm'
import App from '../app.vue'
document.addEventListener('DOMContentLoaded', () => {
const app = new Vue({
el: '#img_field',
data: {
imageData: ''
},
methods: {
onFileChange(e) {
const files = e.target.files;
if(files.length > 0) {
const file = files[0];
const reader = new FileReader();
reader.onload = (e) => {
this.imageData = e.target.result;
};
reader.readAsDataURL(file);
}
}
},
components: { App }
})
})
###デフォルトのラジオボタンをカスタマイズする
edit.html.erb
<label for="file-input" class="input-file-button">
ファイルを選択
</label>
<%= f.file_field :image, id: "file-input", accept: "image/*" %>
application.scss
#img_field > input
{
display: none;
}
.input-file-button {
cursor: pointer;
background: #636;
color: white;
padding: 5px 10px;
border-radius: 5px;
width: 100%;
text-align: center;
}
デフォルトのラジオボタンはidを付与してdisplay:none;で非表示にし、それに対応するlabelタグにcssを当てる事でオリジナルのボタンを作成できる
###v-ifを使って画像プレビュー機能を作成
edit.html.erb
<div id="img_field" @change="onFileChange($event)">
<img :src="imageData" v-if="imageData" class="edit-preview-image">
<img src=<%= current_user.image %> v-else class="edit-preview-image">
・・・
application.scss
.edit-preview-image {
width: 120px;
height: 120px;
border-radius: 50%;
border: 1px solid #eee;
// 縦横比を変えないままトリミング
object-fit: cover;
display: block;
margin: 10px auto;
}
imageDataに値が挿入されていれば上のimgタグ(新しく選択した画像)が表示され画像を選択していない場合はしたのimgタグ(現在設定しているアイコン)が表示される
思っていたよりもrails✖︎vueの記事が無かったので苦戦しました(SPA作る系のやつならたくさんあったのですが)
でもVue.jsの学習の一環としてこの機能はVueで実装してみたかったので粘った甲斐があったと思います。