carrierwaveとaccepts_nested_attributes_forとわたし〜♪

今回itemと同時に、items table とは違うimages table に画像を登録。
以下備忘録、本人の頭の整理のために、、、^^;

さて結論として
「この方法で、multiple使用して登録しても画像を読み込みません！すいません！」
解決方法は下の方に書いています。

## アソシエーション
item has_many images
image belongs_to item


## マイグレーション

```ruby:***_create_images.rb
t.references  :item,  foreign_key: true
t.string      :image, null: false
```


## carrierwaveの設定
ImageUploaderは画像を登録するテーブルに対するモデルに以下を記述
※例えばitem table に画像を含めるときは item model に書くことになります。

```ruby:models/image.rb
belongs_to :item
mount_uploaders :image, ImageUploader #後述のmultiple:trueを使用しないなら mount_uploader(sを消す)
```

## item登録時と同時に画像も登録
accepts_nested_attributes_forを使用

```ruby:models/item.rb
has_many :images
accepts_nested_attributes_for :images
```
```ruby:items_controller.rb
  def new
    @item=Item.new
    @item.images.build
  end

  def create
    Item.create(item_params)
  end

  private
  def item_params
    params.require(:item).permit(
      :name,   #itemsテーブルのカラム名
      images_attributes: {image:[]}  #imagesテーブルのカラム名※他にカラムがある場合はいつものようにカンマでつなぐ
                                     #後述のmultipleを使用しない場合は [:image] と記述
    )
    .merge(user_id:current_user.id)
  end
```
```ruby:new.html.haml
  = form_with model:@item,local:true do |f|
    = f.fields_for :images do |img|         #ブロック変数名は何でも良い
      = img.file_field :image,multiple:true #multiple:true(デフォルトはfalse)で複数枚同時選択可能
```

## 上記方法でmultipleを使用して登録した画像は呼び出しができません。
さてさて、newが終わって、ほぼそのままeditで使っちゃえ
というノリでeditを作っていました。

```
- @item.images.each do |img|
  = image_tag img.image.url
```
とかノリノリで書いてました。が、
読み込んでる様子があるが画像が表示されない？？何故？？
理由は、DBに配列として保存されているからみたいです。
images table の image colum は全て
["画像名.jpg"]
これ
画像名.jpg
でないとおかしいみたいです、、、OTL
ぐすっTT

## 修正方法
・配列でも読み込むようにする。
・配列で保存しないようにする。
以上の２つを思いつきネットで色々調べたのですが、
後者で行きます。

#### 修正箇所
- models/image.rb
- items_controller.rb
- new.html.haml

```ruby:models/image.rb
  belongs_to :item
  mount_uploader :image, ImageUploader
  #結果として「mount_uploader」一択です。「s」はつけません。さよならTT
```
```ruby:items_controller.rb
  def new
    @item = Item.new
    @image = @item.images.build
    #上で生成した@itemに紐付いたimagesをbuildして変数@imageに入れます。
    #後で触るためです。
  end

  def create
    @item = Item.new(item_params) #後述のviewを修正するので、先程とはitem_paramsが変わります。
    params[:images]['image'].each do |a|
      @image = @item.images.build(image: a)
    end
    #後述のviewを修正したのでparams[:images]['image']に複数の画像が入ってます。
    #それをeachで回して一個一個先程の@imageにbuildしていきます。
    #よく見る記述では@item.saveの後に@item.image.createをしていますが、既にnewアクションで生成しているのでcreateする意味がないかと考えます。
    #また、先にsaveするため、「画像が無いと保存できない」というバリデーションもできないと思います。
    @item.save
  end

  private
  def item_params
    params.require(:item).permit(
      :name,   #itemsテーブルのカラム名
      :images  #後述のviewを修正するので、これだけになっちゃいます。
    )
    .merge(user_id:current_user.id)
  end
```
```ruby:new.html.haml
  = form_with model:@item,local:true do |f|
    = f.fields_for :images do |img|
      = img.file_field :image,name:"images[image][]",multiple:true 
  #nameを強制的に上記にします。これによりparamsが変化し、controller内の記述が可能となります。
  #これでmultipleを使用しても画像を読み込めるようになります。
```

## JQueryの記述について
よく考えたらこれがないと動かないです。
ポイントと思われる部分をコメントアウトしておきます。
恐らくもっと簡単にかけるかと思います。

```javascript:app/assets/javascripts/items_new_edit.js
  $(".itemNew,.itemEdit").on("change",'[id^="item_images_attributes"][id$="_image"]', //inputがchangeされたら
    function(){
      let datas = this.files; //受け取ったfilesを配列としてdatasにかくのうしています。
      let image_number=$(this).data("index");
      let box_length=$(".item__image--upload>.item__image--uploadBox").length;
      let delete_box_num=0
      let data_max;
      if(datas.length+box_length<12){
        data_max=datas.length;
      }else if(click_place=="item_image--change"){
        data_max=1
      }else{
        data_max=11-box_length;
      }
      for(i=0;i<data_max;i++){
        let parent_label;
        let file = new DataTransfer(); //本来、DtaTransferはDrag&Dropで使用しますが、今回は受け取ったfileの格納箱として使用しています。参考:https://developer.mozilla.org/ja/docs/Web/API/DataTransfer
        let reader = new FileReader();
        file.items.add(datas[i]); //fileに受け取ったfileを一個格納します。
        if(click_place=="click_label"){
          if($(".item__image--uploadBox_none").length){
            parent_label = $(`label[for="${$(".item__image--uploadBox_none:first label").attr("for")}"]`);
            $(".item__image--uploadBox_none:first>div").unwrap();
            parent_label.find('[id^="item_images_attributes_"][id$="_image"]')[0].files=file.files;
            parent_label.siblings('[type="checkbox"]').prop("checked",false);
            $(`#item_images_attributes_${image_number+i}_image`).removeAttr("required");
            delete_box_num+=1;
          }else{
            parent_label=$(`label[for="item_images_attributes_${image_number+i-delete_box_num}_image"]`);
            add_after_box(parent_label.closest(".item__image--uploadBox"),image_number+i+1-delete_box_num);
            $(`#item_images_attributes_${image_number+i-delete_box_num}_image`).removeAttr("multiple required");
            $(`#item_images_attributes_${image_number+i-delete_box_num}_image`)[0].files=file.files; //一個のinputに一個のfileを受け渡します。これで配列を使用せずにすみます。
          }
          upload_box_width();
        }else if(click_place=="item_image--change"){
          parent_label=$(`label[for="item_images_attributes_${image_number+i}_image"]`);
          $(this).closest(".click_label").siblings('input[id$="_cache"]').remove();
          $(`#item_images_attributes_${image_number+i}_image`)[0].files=file.files;
        }
        reader.readAsDataURL(file.files[0]);
        reader.onload = function(){
          let img_src = $("<img>").attr("src",reader.result);
          parent_label.children("img").remove();
          parent_label.prepend(img_src);
        }
      }
      $('[id^="item_images_attributes"][id$="_image"]').last().val("");
    }
  );
```

## 該当のview等（最終案）について
```ruby
#viewの該当部分
    .item__image--upload
      = f.fields_for :images do |img|
        .item__image--uploadBox
          = img.label :image,class:"click_label" do
            = icon('fa','camera')
            - if params[:action]=="new"
              = img.file_field :image,accept:"image/*",data:{index: img.index},name:"item[images_attributes][#{img.index}][image]",multiple:true,required:true #nameをaccepts_nested_attributes_forを使用する形に変更することで後述のcontrollerがスッキリかけました。
            - else
              = image_tag @item.images[img.index].image_url
              = img.file_field :image,accept:"image/*",data:{index: img.index}
          %span.item_image--delete 削除
          %span.item_image--change 変更
          = img.hidden_field :image_cache if params[:action]=="create"||params[:action]=="update"
          = img.check_box:_destroy if params[:action]=="edit"||params[:action]=="update"
      - unless params[:action]=="new"
        .item__image--uploadBox
          %label.click_label{for:"item_images_attributes_#{@item.images.length}_image"}
            = icon('fa','camera')
            %input{type:"file",id:"item_images_attributes_#{@item.images.length}_image",accept:"image/*","data-index":"#{@item.images.length}",name:"item[images_attributes][#{@item.images.length}][image]",multiple:"multiple"}
          %span.item_image--delete 削除
          %span.item_image--change 変更

#controllerの記述
  def new
    @item = Item.new
    @image = @item.images.build
  end

  def create
    @item = Item.new(item_params)
    if @item.save
      redirect_to new_item_path
    else
      render :new
    end
  end

  def edit
    @item = Item.find(params[:id])
  end

  def update
    @item = Item.find(params[:id])
    if @item.update(item_params)
      redirect_to root_path
    else
      render :edit
    end
  end
```

以上が私が作った画像登録部分です。
自分の覚書ですが、
ご質問等いただけたら可能な限り対応はさせていただきます。