##前提
- ruby on rails 6.0.0 を使用。
- ユーザー機能はdeviseにより導入されているものとする。
- viewファイルは全てhaml形式とする。
- ちなみに使っているのはMacBook Air(Retina, 13-inch, 2020)です。
##はじめに
前回(part2)のあらすじです。carrierwaveのuploaderを使って商品登録の際に画像を投稿できる機能を実装しました。
前置きや手順などは part1 に詳しく記載してあるので気になったら騙されたと思ってクリックしてLGTMを押してみましょう。特に何も起こりません。
前回で画像1枚のみの投稿はできるようになったので今回は最大5枚まで一気に登録できるようにしていきます。手順の3番目にあたる、 「jQueryを導入して複数画像の投稿を実装」ですね。
1つ画像を登録するのに3万年かかったのに5つ登録しようと思ったらそれはもう15万年かかるのでは?(あほ)
そんなことはさておき、
##画像を複数投稿する
前置きが長くなってしまいましたが、尻込みしていても仕方がないのでさっそくやっていきましょう。
現在の状態だと、画像のフォームは一つしか表示されておらず、一つ選択してしまったらそれで終わりとなってしまっています。そこで登場するのがjavascript。(今回はjQueryを使います)
最終的に、一つ画像を入力したら新しいフォームが出現し、最大5回まで画像を登録できるといった状態ができればゴールとします。
###jQueryについて
まずはjQueryの導入からですが、Rails6において、私の知る限りjQueryの導入方法には2つの方向性があります。
- gemとしてjQueryをインストールする方法
- webpackerを通してjQueryを呼び出す方法
今回は先述のgemを通した方法でやりました。混乱する人も多いと思うのでjQueryの導入方法は番外編として後ほど投稿しようと思います。というか僕が大混乱しました。
###時を飛ばそう
というわけで、無事jQueryが導入できたていで進めていきましょう。
= form_with model: @product, local: true do |f|
= f.text_field :name, placeholder: 'name'
#image-box
= f.fields_for :images do |i|
.group{ data: { index: i.index } }
= i.file_field :src, class: 'file'
= f.submit 'SEND'
まずはformのビューファイルにクラスやIDをつけていきます。{ data: ~ } の部分はカスタムデータ属性の指定です。他は特に問題ないと思います。基礎的なことをhamlで書いているだけです。
$(function() {
const buildFileField = (index)=> {
const html = `<div data-index="${index}" class="group">
<input class="file" type="file"
name="product[images_attributes][${index}][src]"
id="product_images_attributes_${index}_src"><br>
<div class="remove">削除</div>
</div>`;
return html;
}
let fileIndex = [1,2,3,4,5];
$('#image-box').on('change', '.file', function(e) {
$('#image-box').append(buildFileField(fileIndex[0]));
fileIndex.shift();
fileIndex.push(fileIndex[fileIndex.length - 1] + 1)
});
$('#image-box').on('click', '.remove', function() {
$(this).parent().remove();
if ($('.file').length == 0) $('#image-box').append(buildFileField(fileIndex[0]));
});
});
こちらがjQueryの記述となります。番外編で詳しく書きますが、Rails6のassetsフォルダにjavascriptはないので、自分で作成していただく必要があります。
それでは順を追って解説していきましょう。
let fileIndex = [1,2,3,4,5];
$('#image-box').on('change', '.file', function(e) {
// ファイルが選択されたときfileIndexの最初の数字をindexとして持ったフォームを新しく作成する。
$('#image-box').append(buildFileField(fileIndex[0]));
// fileIndexの最初の数字を削除して数字をひとつずつ左へずらす。
fileIndex.shift();
// fileIndexの最後の数字に1を足した数字を最後尾に挿入する。
fileIndex.push(fileIndex[fileIndex.length - 1] + 1)
});
まずはこちら、表示されているフォームで画像を選択した際に、新しいフォームが表示される。といった処理ですね。一行ずつの考え方を追記しておきました。これを繰り返すことで、ひとつ一つのフォームに固有のindexを持たせることができます。
const buildFileField = (index)=> {
const html = `<div data-index="${index}" class="group">
<input class="file" type="file"
name="product[images_attributes][${index}][src]"
id="product_images_attributes_${index}_src"><br>
<div class="remove">削除</div>
</div>`;
return html;
}
先ほどの buildFileField の処理を記述したものです。先ほど作った固有のindexを引数として渡しています。``で囲われている部分は、_form.html.haml で記述されていたフォームをhtmlとして記述し直し、あとで参照できるようにidとnameをつけているだけです。
$('#image-box').on('click', '.remove', function() {
// クリックされた.removeの親要素を削除する。
$(this).parent().remove();
// フォームの数が0になった際、新しいフォームを表示させる。
if ($('.file').length == 0) $('#image-box').append(buildFileField(fileIndex[0]));
});
最後にフォームを削除する処理ですね。こちらも一行ずつ書いておきました。
さて、ここまでで登録画面における処理はひとまずできました。jQueryの書き方さえ分かっていれば特に難しいこともなかったかと思います。
そういえばfileやremoveなど、一部メソッドなのかクラスなのかがわかりにくかったかもしれませんが、'.~'という形で記述されているのは全てクラスになります。慣れれば簡単に見分けられます。
##最後に
大袈裟な前置きをしていましたが、実はデータベースへの複数登録自体は前回で終わっているのです。なので今回はフロントにおける作業がメインでしたね。
ようやく形になってきました。今回で画像の複数登録はできたので、次のpartでは複数画像の編集を行っていきたいと思います。
ではまた次のpartでお会いしましょう。