하나의 게시물로 여러 이미지를 미리보기하면서 저장

개요



Rails 어플리케이션에서, 투고에 첨부되는 화상을 복수장, 프리뷰 표시시키면서 투고할 수 있는 기능을 구현했으므로 비망록으로서 정리합니다.

제품에 여러 개의 이미지가 붙습니다.
하나의 트윗으로 여러 이미지를 게시할 수 있는 이미지입니다.

뷰의 곳은 특히 더 좋은 방법이 있을 것이라고 생각하고 있습니다만,
개선점이나 실수가 있으면 꼭 코멘트하실 수 있으면 다행입니다!

완성 이미지





도입한 젬



· carrierwave
・mini_magick

마이그레이션



마이그레이션은 평소대로 ...
class CreateProducts < ActiveRecord::Migration[5.2]
  def change
    create_table :products do |t|
      t.string :name, null: false
      t.timestamps
    end
  end
end
class CreateImages < ActiveRecord::Migration[5.2]
  def change
    create_table :images do |t|
      t.string :image
      t.references :product, null: false, foreign_key: true
      t.timestamps
    end
  end
end

모델



accepts_nested_attributes_for에서 "Product에 연결되는 Image"라는 중첩 관계를 만들 수 있습니다.
class Product < ApplicationRecord
  has_many :images, dependent: :destroy
  accepts_nested_attributes_for :images
end

optional : true는 외래 키 nil을 허용하는 역할.
class Image < ApplicationRecord
  belongs_to :product, optional: true
  mount_uploader :image, ImageUploader
end

컨트롤러



build는 new와 같은 역할입니다.
_attibutes를 사용하여 Product의 params에서 일괄 받을 수 있도록 작성합니다.
class ProductsController < ApplicationController

  def new
    @product = Product.new
    @image = @product.images.build
  end

  def create
    @product = Product.new(product_params)
      if @product.save
          params[:images]["image"].each do |image|
            @image = @product.images.create!(image: image)
          end
        redirect_to root_path
      else
        @product.images.build
        render action: 'new'
      end
  end

  private

  def product_params
    params.require(:product).permit(:name, images_attributes: [:name]).merge(user_id: current_user.id)
  end

end

보기



이번에는 최대 3장까지 했습니다.
화상수가 많아지면 쓰는 방법은 더 궁리하는 것이 좋을까라고 생각하고 있습니다・・・

fields_for를 사용하면 form_for 내에서 다른 모델에 저장할 수 있습니다.
  = form_with(model: @product, local: true) do |f|
    .upload__box__head
      出品画像
      %p.upload__box__head__sub
        最大3枚までアップロードできます
    .upload__box__images
      = f.fields_for :images do |i|
        .upload__box__image
          %label{for: "image1"}
            = image_tag "pict/item_upload_dummy.png", alt: "Item upload dummy" ,height: "100%", width: "100%", id: "preview1", class: "preview-image"
          = i.file_field :image, multiple: true, id:"image1", type: "file", accept: "image/*", onchange: "previewImage1(this);", style: "display: none;", name: "images[image][]"
        .upload__box__image
          %label{for: "image2"}
            = image_tag "pict/item_upload_dummy.png", alt: "Item upload dummy" ,height: "100%", width: "100%", id: "preview2", class: "preview-image"
          = i.file_field :image, multiple: true, id:"image2", type: "file", accept: "image/*", onchange: "previewImage2(this);", style: "display: none;", name: "images[image][]"
        .upload__box__image
          %label{for: "image3"}
            = image_tag "pict/item_upload_dummy.png", alt: "Item upload dummy" ,height: "100%", width: "100%", id: "preview3", class: "preview-image"
          = i.file_field :image, multiple: true, id:"image3", type: "file", accept: "image/*", onchange: "previewImage3(this);", style: "display: none;", name: "images[image][]"

JS



여기도 하나로 정리되는 것 같네요・・・
function previewImage1(obj){
  var fileReader = new FileReader();
  fileReader.onload = (function() {
    document.getElementById('preview1').src = fileReader.result;
  });
  fileReader.readAsDataURL(obj.files[0]);
}

function previewImage2(obj){
  var fileReader = new FileReader();
  fileReader.onload = (function() {
    document.getElementById('preview2').src = fileReader.result;
  });
  fileReader.readAsDataURL(obj.files[0]);
}

function previewImage3(obj){
  var fileReader = new FileReader();
  fileReader.onload = (function() {
    document.getElementById('preview3').src = fileReader.result;
  });
  fileReader.readAsDataURL(obj.files[0]);
}

이상이 됩니다!
아무래도 초보자 때문에, 여기는 이렇게 쓰면 리팩토링할 수 있어! 라든지 있으면 가르쳐 주시면 감사하겠습니다!

좋은 웹페이지 즐겨찾기