フォームを追加していくjs
※注意。ハムルだけどインデントおかしいのでそこは注意。多分はてなブログ自体がhtml編集できるからスペースが無視されるんだろう。エスケープするか自分で作ったアプリでも見とけ。
先にaccepts_nested_attributes_for_テーブル名でメモ書いといたけどその続きで。
一つのfom_for内で複数モデルに登録をする方法が前回の部分だった訳だけど、画像が一枚ならあのままでも大丈だが、複数枚投稿したいなら画像を追加する毎にフォームも増える必要があるので、jsが必要になってくる。fields_forを使ってfile_fieldのフォームを作った場合はinputの
typeがfileなのは当然として、
nameが”親モデル[子テーブル_attributes][0][子カラム]”
idが”親モデル_子テーブル_attributes_0_子カラム”
となっているので二つ目のフォームからは[0]の部分の数字を増やしていくことになる。
例としてこんな感じのhamlがあったとする。なんかインデントおかしいけどスペース入れてもこうなるからファイルフィールドのインデントはbrと同じだと思って。
この時ちょっと注意なのはi.file_fieldの方に先ほどのnameやidが書いてあるのだが、hamlの方では直接は見えてないこと。.js-file_groupの方は普通のdivタグにカスタムデータ属性でindexの番号を入れてあげてるような感じ.
jsはこんな感じになる
説明加えてく
const buildFilefield = (index)=> {} のとこは引数にもらったindexを使ってhtml組み立てて返してるだけ。
const buildFilefield = function(index){}って書いても挙動変わんなかったから要は無名関数ってことだと思う。自分は下の方が分かり易かったのでアプリでは下を使ってるが、キモい書き方(アロー関数?)もあるってことで上も記録に残しとく。
その後index振るための配列をfileIndexに定義して
もしimage-boxの中のフォームjs-fileの要素がchangeしたらさっき定義した配列の一番最初[0]の、つまりここでは1の数字をindexに入る引数といてbuildFilefieldにhtml作ってもらってappendしてる。この辺りのhtml組み立ての流れは大丈夫だと思うんだが、
$('hoge').on('change', 'hogehoge' function(e){こん中に変化があった場合の処理})
っていうchangeの後になんか指定するっていう使い方をパッと思いつけるように慣れたらいいなと思った。onの使い方はこの記事が分かり易いと思う。
https://www.sejuku.net/blog/38774
shifメソッドは配列の先頭を削除するのでなんか追加したらとりあえず1を削除するようになってる。その後pushで配列を一つ増やして配列の総数は変わんないようにしてる。ここで注意するのはfileIndex[]のインデックス番号は0から始まるが、fileIndex.lengthは単純に配列に要素が何個あるかなので、.lengthから1引いた数に1を足している。1引いて1足してるから結局.lengthのままでいいような気もするが、意味合い的には1足す表現があった方が分かり易いかもしれんので一応アプリでもこれ使ってっる。(でも本心では.lengthでよくねって思ってる)
.parentに関しては下の記事が分かり易い。まぁここでは単純に親要素だろうが。
子モデルの削除やも親モデルのコントローラーでやりたい場合はストロングパラメーターで、例えば
などとする。destroyを許可してやらないといけない感じ。ちょと抜粋すると
accepts_nested_attributes_for
は、paramsの○○s_attritbutes:
というキーの中で特定の値を送ることで、親モデルに紐づいた子モデルの削除、更新を行います。
らしい。
登録した画像の削除は編集、editページで行うだろうから、editページのフォームの話にうつる。
削除する対象は当然データベースに保存済みのものであるので.persisted?を使う。これはDBに保存済みならtrue、そうでなければfalseを返すメソッドである。(これ使ってるのでnewの方でも同じフォーム使いまわせる。)
例えば
等とあったとするとnew側の方は今までと同じで、edit側はすでにDBに保存されているものだけ、つまり削除や更新の対象になるものだけが表示される。一つ目の.persisted?は登録されてる画像それぞれにチェックボックスを出現させている。新規作成時には削除のチェックボックスは必要ないのでedit側だけで表示している。
二つ目の.persisted?はedit側でも新規登録用のフォームが要るだろうけど、editページでビルドされるフォームは既存の画像枚数分だけなので新しくフォームを生成している。
editページでは画像に添え字が添えられているので新しく画像を投稿するために辻褄を合わせる必要がある。なので、ページが読み込まれたらfileIndexから数字を取り除く必要がある。
jsを次のように編集
まず.js_file_group(カスタムデータが書いてあるdivタグ。ファイルフィールドから削除ボタンか削除のチェックボックスまで)の一番最後のindexが何なのか取得。
参考記事
http://js.studio-kingdom.com/jquery/selector/last_selector
次にsliceで配列の最初(0)から最後(lastIndex)までの数字削除することで既に使われている数字を削除している。
最後の行は削除ボックスが全部の画像の下に表示されてたら見た目が悪いので全部隠している。この後jsでクリックされたらチェックボックスにがチェックされるようなjsを書く。
$('#image-box').on('click', '.js-remove', function() {
const targetIndex = $(this).parent().data('index')
// 該当indexを振られているチェックボックスを取得する
const hiddenCheck = $(`input[data-index="${targetIndex}"].hidden-destroy`);
// もしチェックボックスが存在すればチェックを入れる
if (hiddenCheck) hiddenCheck.prop('checked', true);
(省略)
});
|
こんな感じ。
コメントで大体わかると思うが、補足入れると'.js-remove'は「削除」の文言を囲ってるspanタグのクラス。.hidden-destroyはチェックボックスのクラス名。
.prop()はproperty関連のメソッドで、引数1個なら指定したプロパティの値を取得できるし、2個なら引数に指定したプロパティに値を代入できる。詳しくはこの参考記事見て
https://www.sejuku.net/blog/36294
今回はcheckedプロパティをtrueにしてるって感じだろう。
ちなみにこの記事によると似たようなのにattr()があるけど、チェックボックスに使うならprop()を使うべきらしい。時間がある時にその辺りも読もうと思う。
なんかまどろこしく感じるかもしれんが、実際にデータに入ってるものを削除したかったらjsでbox削除するだけじゃいかんから見えてなくてもチェックは必要だし、一回チェックしたらboxごとチェックボックスも消えるから都合いいし、DBにない物にdestroyリクエストしたらエラー出るだろうから.persistedも必要だったろうし、で何だかんだ全部必要な記述って感じ。
次にプレビューの表示の話に移る。
image-boxの先頭辺りにこんな感じでidがpreviewsのdivタグの中に画像を並べる。editの方の予め登録されてる画像は一応これで出る感じか。
ただプレビューには投稿をする前に予め見たい方のプレビューもあるのでそちらの話に移る。
まずは後でイベント発生時に書く処理で使う、いつもの、htmlを組み立てる関数をbuildImgに代入しとく。
そしてjs-fileにつまり画像のファイルフィールドの中身に変更があると動く関数を書く。
説明に入る。
まずjs-file_groupのdata-indexを取得(targetIndex)。
配列表してると思われる[0]って必要なのかちょっとわからんけども、fileならtargetしてる部分だし、imgの方はindexを指定してるから一つしかないので矛盾はしない・・・かな。この辺の自分の理解にあんま自信ないな。とりあえずこの解釈であってると仮定して、
ファイル名を取得(file)。
引数にさっきのfile渡して、ブラウザ上でのurlを作成してblobUrlに入れとく。
もしif(targetIndexで取得したindexがカスタムデータに入った物があればimgに代入して){そのimgのsrc属性をさっき作ったblobUrlのパスにする}そうじゃなかったら{このファイルフィールドに割当てられているindexと組み立てたパスblobUrlを使ってイメージタグをbuildImgで組み立ててpreviewのけつにくっつける}。
要するにif前半部分は画像があった場合なので前の画像から新しい画像に更新、なかった場合は新規なので新しく作ってくっつけるってことをしてる。
それ以降は元々あった記述(プレビュー関連ではなく新規フォーム作成関連の物)なので説明は割愛、というか最初の方に書いた。
最後に後一つだけ削除ボタン押した時の処理
に
の行を追記して変更のあったファイルのindexと同じindexの画像が削除が押された時に消えるようにして終わり。
とりあえずこんな流れで作ったのでメモ。