inuinu blog(開発用)

BOT @wagagun の開発ノウハウや、IT向け?の雑記ブログです。

【Ruby on Rails7】cocoonを導入(その9:最終行からコピーしたい)

この記事では、Ruby on Rails7のgemパッケージcocoonについて、導入・カスタマイズ行います。
今回は行追加時に、最終行から値をコピーする手法を考えます。

前回の続き。

inuinu-tech.hatenablog.jp

今回は、失敗をもとに、ビューで細工するのをやめ、JQueryでコードを実装していきます。

HTMLの中身を調査し、仕様を決める

INPUTタグののidを覗いてみる

INPUTタグを覗いてみたところ、name属性には[]が入っており、取り扱うには厳しい気がします。
idはキャメルケースのようですので、こちらを活用したほうが良さそうです。

idを見てみると、ビューで描画された行は0から始まりますが、行追加ボタンで追加された行は、13桁程度の数字がセットされていますね。
なので、配列等にセットする際、添字としては使用できないでしょうね。

どの行からコピーするか?

cocoonが決めたJQueryのエントリーポイントに実装することになるのですが、ビューのみの場合と比較して、多少の自由度は増します。

今回は、行追加リンクを押下する直前時点での最終行の値を、新たに追加する行にコピーするようにします。

別ボタンを用意するか?

行追加ボタンの他に、行コピー(追加&複写)といったボタンを別途用意した場合、JQueryのロジックを2つ用意する必要がありそうですね。ちょっと冗長だと思います。

ですので、ボタンの横に「□行コピー」といったチェックボックスを用意し、チェックされたらコピーの処理を追加します。

コピーロジックを実装する

明細行計算のロジックを改良する

inuinu-tech.hatenablog.jp

で、紹介した行計算ロジックを改良あるいはコピーします。
(どちらでも構いません。)

一例をあげます。
cocoon:after-insertにペーストするか、適当なファンクションにして、cocoon:after-insertから呼び出します。

let obj = document.querySelectorAll('#codedetails .nested-fields');
let flds_id = []; // 多次元配列用
obj.forEach(function(field, idx) {
  if (field.style.display !== 'none') {
      let work_id = [];
      let input_obj = field.querySelectorAll("input");
      input_obj.forEach(function(field2, idx2) {
        if (field2.id.indexOf('_destroy') == -1) {
         work_id.push(field2.id);
        }
      })
      flds_id.push(work_id);
  }
})
return flds_id;

flds_idは2次元配列(行、列)になります。
(Rubyと同様)Javascriptは初っ端で2次元配列を定義できないので、work_idを別途用意し、列の配列をflds_idにプッシュしています(flds_id[idx]に直接プッシュしていっても構いません)。
行削除ボタン自体もINPUTタグですので、_destroyが含まれるINPUTは除外します。

上記例はINPUTですが、INPUTの中でもvalueで値がコピーできないものもあります。そういった項目が行に含まれる場合は、type属性で除外することになります。
また、TEXTAREAやSELECTタグもvalueで行けそうですので、こちらに含めてもいいかもしれません。
要は、valueで複写できる、できないで、配列を別にし、それぞれのコピーロジックを作成を別途作成していきます。

コピーしたくない項目があった場合

例えば、ユニークキーとなるような項目をコピーした場合、重複チェックで引っかかるので、そこは空欄にしたい…のであれば、

if (field2.id.indexOf('_destroy') == -1) {

の箇所を工夫すれば良いと思います。

コピーロジックを実装する

次にコピーロジックを実装するのですが、先程の配列ごと(valueで複写できるもの、できないもの)にロジックを作っていきます。

その前に、配列の何番目から、どこにコピーするかを整理します。
もうすでにコピー先の行は作成されているので…

  • コピー元の添字:配列の長さ − 2
  • コピー先の添字:配列の長さ − 1

となるはずです。
以下はコピーロジック例です。cocoon:after-insertに挿入するか、適当なファンクションにして、cocoon:after-insertから呼び出します。
(前提条件:INPUTタグ等でvalueコピーが可能なもの、かつ、行コピーにチェックが入っていて、行が2行以上存在していること。)

let copy_moto = flds_id.length - 2;
let copy_saki = flds_id.length - 1;

// 各行のinputタグ(削除するボタンを除く)でループ
for (let idx=0; idx<flds_id[0].length; idx++) {
  document.getElementById(flds_id[copy_saki][idx]).value = document.getElementById(flds_id[copy_moto][idx]).value;
}

こちらで、最終行(実行段階では1行前の行)からのコピーが可能になります。

次回

次回は、最終行ではなく、任意の行をコピーしたい場合を考えてみます。

inuinu-tech.hatenablog.jp