LoginSignup
5
4

More than 3 years have passed since last update.

_destroyキーの使い方 ~~親(item)の子(image)を削除する~

Posted at

一次ソースはこちらです。

問題

出品した商品(item)の画像を削除したいが、削除ボタンを押し更新ボタンを押しても削除できない

edit.html.haml
  .main-items
    = form_with model: @item, local: true do |f|
      .wrapper.image-wrapper
        #image-box.image-wrapper__image-box
          = f.fields_for :images do |i|
            - if @item.persisted?
              = i.check_box :_destroy, data:{ index: i.index}, class: 'hidden-destroy'
            .image-wrapper__image-box__js.js-file_group{:id => "item_images_attributes_#{i.index}_id", data:{index: "#{i.index}"}}
              = i.label :content, class: "image-wrapper__image-box__js__label" do
                .image-wrapper__image-box__js__label__image.img_field{id: "img_field--#{i.index}", onClick: "$('#file').click()"}
                  - if @item.images[i.index][:content].present?
                    = image_tag asset_path(@images[i.index].content), class:"preview "
                  - else
                    = image_tag 'icon_camera.png', class: "image-wrapper__image-box__js__label__image__url", id: "default-img"
                = i.file_field :content, class: "image-wrapper__image-box__js__label__file js-file", id: "item_images_attributes_#{i.index}_content"
              .js-remove
                %span.js-remove__text
                  削除

前準備

問題コードの
= i.check_box :_destroy, data:{ index: i.index}, class: 'hidden-destroy'
によって各画像のチェックボックスにチェックが入ったら_destroyに値"1"がついた画像を削除するようにしているよ!(チェックボックスは隠してます)

モデル
item.rb
accepts_nested_attributes_for :images, allow_destroy: true

accepts_nested_attributes_forを使用し、paramsのimages_attributes:キー内で値を取り、送ることで親モデル(item)に紐つく子モデル(image)の削除、更新を行える。

allow_destroy: trueで_destroyキーが使えるようになる。

コントローラー
items_controller.rb
  private
  def item_params
    params.require(:item).permit(
      :name, :description, :price, :brand_id, :area, :condition, :fee, :category_id,
      :shipping_days, images_attributes: [:content, :id, :_destroy]
      ).merge(user_id: current_user.id)
  end

ストロングパラメーターにimages_attributes: [:content, :id, :_destroy]を記述

原因追求

Image from Gyazo

コントローラー
items_controller.rb
  def update
    binding.pry #デバック
    if @item.update(item_params)
      redirect_to item_path(@item)
    else
      flash.now[:alert] = '画像を1枚以上添付してください'
      redirect_to edit_item_path(@item)
    end
  end

binding.pryを使用し、更新ボタンを押した時のparams中身をターミナルで確認する

ターミナル

※横スクロール長くてすみません。
※配列の添字(index)により一枚目は"0"から始まっています。

"item"=>{"images_attributes"=>{"0"=>{"_destroy"=>"0", "id"=>"2"}, "1"=>{"_destroy"=>"1", "id"=>"47"}, "2"=>{"_destroy"=>"1", "id"=>"48"}, "3"=>{"_destroy"=>"1", "id"=>"49"}, "4"=>{"_destroy"=>"0", "id"=>"50"}, "5"=>{"_destroy"=>"0"}},

今回は5枚ある画像から2、3、4枚目を削除した。
しかし、paramsに入る内容は"4"=>{"_destroy"=>"0", "id"=>"50"}までで良いはずだが、不要な"5"=>{"_destroy"=>"0"}}が入ってしまっている。
("5"は一番右の新規画像追加用のカメラマークのこと)

ブラウザ

この不要な"5"をChromeの検証ツールElementsで確認してみると
Image from Gyazo
不要なhtmlタグみっけ

checkboxによりこのタグがある。
nameがつけられparamsに入ってしまっていた。

解決方法

- if @item.images[i.index][:content].present?をfields_forの直下に持ってきて、既存の画像のみにチェックボックスが付与されるように分けよう!!

edit.html.haml
  .main-items
    = form_with model: @item, local: true do |f|
      .wrapper.image-wrapper
        #image-box.image-wrapper__image-box
          = f.fields_for :images do |i|
            - if @item.images[i.index][:content].present?
              - if @item.persisted?
                = i.check_box :_destroy, data:{ index: i.index}, class: 'hidden-destroy'
              .image-wrapper__image-box__js.js-file_group{:id => "item_images_attributes_#{i.index}_id", data:{index: "#{i.index}"}}
                = i.label :content, class: "image-wrapper__image-box__js__label" do
                  .image-wrapper__image-box__js__label__image.img_field{id: "img_field--#{i.index}", onClick: "$('#file').click()"}
                    = image_tag asset_path(@images[i.index].content), class:"preview "
                .js-remove
                  %span.js-remove__text
                    削除
            -else
              .image-wrapper__image-box__js.js-file_group{:id => "item_images_attributes_#{i.index}_id", data:{index: "#{i.index}"}}
                = i.label :content, class: "image-wrapper__image-box__js__label" do
                  .image-wrapper__image-box__js__label__image.img_field{id: "img_field--#{i.index}", onClick: "$('#file').click()"}
                    = image_tag 'icon_camera.png', class: "image-wrapper__image-box__js__label__image__url", id: "default-img"
                  = i.file_field :content, class: "image-wrapper__image-box__js__label__file js-file", id: "item_images_attributes_#{i.index}_content"
                .js-remove
                  %span.js-remove__text
                    削除

これでparamsを確認すると

"item"=>{"images_attributes"=>{"0"=>{"_destroy"=>"0", "id"=>"2"}, "1"=>{"_destroy"=>"1", "id"=>"47"}, "2"=>{"_destroy"=>"1", "id"=>"48"}, "3"=>{"_destroy"=>"1", "id"=>"49"}, "4"=>{"_destroy"=>"0", "id"=>"50"}}

"5"が入ってないOK

binding.pryを外して更新すると削除できました!

5
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4