学生向けプログラミング入門 | 無料

学生向けにプログラミングを無料で解説。Java、C++、Ruby、PHP、データベース、Ruby on Rails, Python, Django

Rails7.1 | 仕事売買アプリ作成 | 26 | Dropzoneの利用

↓↓クリックして頂けると励みになります。



25 | 仕事登録ビュー】 << 【ホーム】 >> 【27 | 仕事公開



Dropzone.jsをRuby on Railsプロジェクトに統合することは、ファイルアップロードのユーザーエクスペリエンスを向上させ、アプリケーション全体の機能性と使いやすさを向上させるために非常に有益です。


Dropzone.jsは、ユーザーエクスペリエンスを向上させるために、ドラッグアンドドロップを使用してファイルをアップロードする方法を提供します。
ユーザーはファイルを選択するだけでなく、ファイルをブラウザウィンドウにドラッグしてアップロードできます。
これにより、使いやすさが向上し、アップロードプロセスがシームレスになります。


Rails7.1使用の場合、Importmapからの読み込みがうまくいかないため、CDN経由でスクリプトとスタイルシートを読み込ませるようにします。


「app/views/layouts/application.html.erb」ファイルのheadタグに以下の記述を追加します。

    <!-- Dropzone5.5.1 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.1/min/dropzone.min.js" integrity="sha512-jytq61HY3/eCNwWirBhRofDxujTCMFEiQeTe+kHR4eYLNTXrUq7kY2qQDKOUnsVAKN5XGBJjQ3TvNkIkW/itGw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.1/min/dropzone.min.css" integrity="sha512-zoIoZAaHj0iHEOwZZeQnGqpU8Ph4ki9ptyHZFPe+BmILwqAksvwm27hR9dYH4WXjYY/4/mz8YDBCgVqzc2+BJA==" crossorigin="anonymous" referrerpolicy="no-referrer" />



記述追加 【app/views/layouts/application.html.erb】24行目

<!DOCTYPE html>
<html>
  <head>
    <title>GigHub7</title>
    <meta name="viewport" content="width=device-width,initial-scale=1">
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
    <%= javascript_importmap_tags %>

    <!-- noty3.1.4 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/noty/3.1.4/noty.min.js" integrity="sha512-lOrm9FgT1LKOJRUXF3tp6QaMorJftUjowOWiDcG5GFZ/q7ukof19V0HKx/GWzXCdt9zYju3/KhBNdCLzK8b90Q==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/noty/3.1.4/noty.min.css" integrity="sha512-0p3K0H3S6Q4bEWZ/WmC94Tgit2ular2/n0ESdfEX8l172YyQj8re1Wu9s/HT9T/T2osUw5Gx/6pAZNk3UKbESw==" crossorigin="anonymous" referrerpolicy="no-referrer" />

    <!-- Google Fonts -->
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Kaisei+Opti&family=Kosugi+Maru&family=Rampart+One&display=swap" rel="stylesheet">

    <!-- Font Awesome -->
    <script src="https://kit.fontawesome.com/dd8c589546.js" crossorigin="anonymous"></script>

    <!-- Dropzone5.5.1 -->
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.1/min/dropzone.min.js" integrity="sha512-jytq61HY3/eCNwWirBhRofDxujTCMFEiQeTe+kHR4eYLNTXrUq7kY2qQDKOUnsVAKN5XGBJjQ3TvNkIkW/itGw==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/dropzone/5.5.1/min/dropzone.min.css" integrity="sha512-zoIoZAaHj0iHEOwZZeQnGqpU8Ph4ki9ptyHZFPe+BmILwqAksvwm27hR9dYH4WXjYY/4/mz8YDBCgVqzc2+BJA==" crossorigin="anonymous" referrerpolicy="no-referrer" />

  </head>

  <body>

    <!-- ナビゲーションバー -->
    <%= render  "shared/navbar" %>

    <!-- noty -->
    <%= render 'shared/notification' %>

    <%= yield %>
  </body>
</html>



「app\controllers\gigs_controller.rb」ファイルを以下の手順で編集していきます。


1.85行目からの「upload_photo()」メソッド、「delete_photo()」メソッドの内容を以下の記述に編集します。

  def upload_photo
    @gig.photos.attach(params[:file])
    render json: { success: true }
  end

  def delete_photo
    @image = ActiveStorage::Attachment.find(params[:photo_id])
    @image.purge
    redirect_to edit_gig_path(@gig, step: 4)
  end



2.3行目に「protect_from_forgery except: [:upload_photo]」の記述を追加します。


記述更新 app\controllers\gigs_controller.rb

class GigsController < ApplicationController
 
  protect_from_forgery except: [:upload_photo]
  before_action :authenticate_user!, except: [:show]
  before_action :set_gig, except: [:new, :create]
  before_action :is_authorised, only: [:edit, :update, :upload_photo, :delete_photo]
  before_action :set_step, only: [:update, :edit]

  def new
    @gig = current_user.gigs.build
    @categories = Category.all
  end

  def create
    @gig = current_user.gigs.build(gig_params)

    if @gig.save
      @gig.pricings.create(Pricing.pricing_types.values.map{ |x| {pricing_type: x} })
      redirect_to edit_gig_path(@gig), notice: "保存しました"
    else
      redirect_to request.referrer, flash: { error: @gig.errors.full_messages }
    end
  end

  def edit
    @categories = Category.all
  end

  def update

    if @step == 2
      gig_params[:pricings_attributes].each do |index, pricing|
        if @gig.has_single_pricing && pricing[:pricing_type] != Pricing.pricing_types.key(0)
          next;
        else
          if pricing[:title].blank? || pricing[:description].blank? || pricing[:delivery_time].blank? || pricing[:price].blank?
            return redirect_to request.referrer, flash: {error: "価格が無効です"}
          end
        end
      end
    end

    if @step == 3 && gig_params[:summary].blank?
      return redirect_to request.referrer, flash: {error: "詳細を空白にすることはできません"}
    end

    if @step == 4 && @gig.photos.blank?
      return redirect_to request.referrer, flash: {error: "写真がありません"}
    end

    if @step == 5
      @gig.pricings.each do |pricing|
        if @gig.has_single_pricing && !pricing.basic?
          next;
        else
          if pricing[:title].blank? || pricing[:description].blank? || pricing[:delivery_time].blank? || pricing[:price].blank?
            return redirect_to edit_gig_path(@gig, step: 2), flash: {error: "価格が無効です"}
          end
        end
      end

      if @gig.summary.blank?
        return redirect_to edit_gig_path(@gig, step: 3), flash: {error: "詳細を空白にすることはできません"}
      elsif @gig.photos.blank?
        return redirect_to edit_gig_path(@gig, step: 4), flash: {error: "写真がありません"}
      end
    end

    if @gig.update(gig_params)
      flash[:notice] = "保存しました"
    else
      return redirect_to request.referrer, flash: {error: @gig.errors.full_messages}
    end

    if @step < 5
      redirect_to edit_gig_path(@gig, step: @step + 1)
    else
      redirect_to dashboard_path
    end

  end

  def show
  end

  def upload_photo
    @gig.photos.attach(params[:file])
    render json: { success: true }
  end

  def delete_photo
    @image = ActiveStorage::Attachment.find(params[:photo_id])
    @image.purge
    redirect_to edit_gig_path(@gig, step: 4)
  end

  private

  def set_step
    @step = params[:step].to_i > 0 ? params[:step].to_i : 1
    if @step > 5
      @step = 5
    end
  end

  def set_gig
    @gig = Gig.find(params[:id])
  end

  def is_authorised
    redirect_to root_path, alert: "あなたには権限がありません。" unless current_user.id == @gig.user_id
  end

  def gig_params
    params.require(:gig).permit(:title, :video, :summary, :active, :category_id, :has_single_pricing, 
                                pricings_attributes: [:id, :title, :description, :delivery_time, :price, :pricing_type])
  end
end



ルートの設定を以下の内容に更新します。


記述更新 config\routes.rb(14行目)

  resources :gigs do
    member do
      delete :delete_photo
      post :upload_photo
    end
  end



config\routes.rb

Rails.application.routes.draw do

  # ルートを app\views\pages\home.html.erb に設定
  root 'pages#home'

  # get
  get 'pages/home'
  get '/dashboard', to: 'users#dashboard'
  get '/users/:id', to: 'users#show', as: 'user'

  # post
  post '/users/edit', to: 'users#update'

  resources :gigs do
    member do
      delete :delete_photo
      post :upload_photo
    end
  end

  # device
  devise_for :users, 
    path: '', 
    path_names: {sign_up: 'register', sign_in: 'login', edit: 'profile', sign_out: 'logout'},
    controllers: {omniauth_callbacks: 'omniauth_callbacks', registrations: 'registrations'}
  # Define your application routes per the DSL in https://guides.rubyonrails.org/routing.html

  # Reveal health status on /up that returns 200 if the app boots with no exceptions, otherwise 500.
  # Can be used by load balancers and uptime monitors to verify that the app is live.
  get "up" => "rails/health#show", as: :rails_health_check

  # Defines the root path route ("/")
  # root "posts#index"
end


Step4



「app/views/gigs/_step4.html.erb」ファイルに記述を追加します。


app/views/gigs/_step4.html.erb

<% if @step == 4  %>

<div class="container mt-4">

    <div class="mt-4 mb-4">
        <%= link_to '戻る', edit_gig_path(@gig, step: @step - 1), class: "btn btn-secondary" %>
        <%= link_to '次へ', edit_gig_path(@gig, step: @step + 1), class: "btn btn-danger" %>
    </div>    

    <div class="card mb-4">
        <div class="card-body">
            <h4 class="mt-4 mb-4"><b>写真アップロード</b></h4>
            <!-- 写真アップロード -->      
            <div class="dropzone" id="myDropzone"  style="height: 200px; border: dashed 1px #333; border-radius: 10px; text-align: center; padding-top: 1rem;" action="/gigs/<%= @gig.id %>/upload_photo"></div>
            <div class="container">
                <div class="row">
                    <% @gig.photos.each do |photo| %>
                        <div class="col-4">
                            <div class="card mt-2">
                                <%= image_tag url_for(photo), class: "card-img-top"  %>
                                <div class="card-body">               
                                    <p class="card-text">
                                        <%= link_to '削除', delete_photo_gig_url(photo_id: photo.id, id: @gig.id, step: @step), 
                                            data: { turbo_method: :delete, turbo_confirm: "本当に削除してもよろしいですか?" },
                                            style: "z-index: 100;",
                                            class: "btn btn-outline-danger btn-sm"
                                        %>
                                    </p>
            
                                </div>
                            </div>
                        </div>
                    <% end %>
                </div>
            </div>
        </div>
    </div>
  
</div>

<% end %>

<script>
    Dropzone.options.myDropzone = {
        paramName: "file",
        maxFilesize: 5,
        acceptedFiles: "image/*",
        dictDefaultMessage: "ここに写真をドラッグ&ドロップして下さい。<br/>または、クリックすることで写真を選択することもできます。",
        init: function() {
            this.on('complete', function (file) {
                location.reload();
            })
        }
    }
</script>



これで写真のアップロード、削除ができます。
複数の写真も同時にアップロードできます。
動作を確認して下さい。
ブラウザ確認
http://localhost:3000/gigs/1/edit?step=4

PCレイアウト
PCレイアウト


モバイルレイアウト
モバイルレイアウト

25 | 仕事登録ビュー】 << 【ホーム】 >> 【27 | 仕事公開





↓↓クリックして頂けると励みになります。

関連記事(外部サイト)