carrierwaveを用いて画像投稿機能を作成

自分用です！！

##carrierwaveをインストール

```ruby:Gemfile
gem carrierwave
```

bundle installしてください。

##uploaderを生成

```ruby:ターミナル
rails g uploader board_image
```
これによって、「この設定をこのモデルとこのモデルに適応する」といったことを柔軟に行えるような仕様となっているのです。

```ruby:uploader
def store_dir
  "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
end
```
といった記述が施されています。

これは、carrierwaveを通じた画像のアップロード先をどこにするのかを指定しており、指定されたディレクトリに、アップロードされたファイルが保存されていくことになります。

DBに保存されるのは、ファイルそのものではなく、ファイルへの参照データになります.

##モデルの関連付け

boardモデルと、boardカラムに保存される画像の関連付けを行います。

```ruby:board.rb
class Board < ApplicationRecord
  belongs_to :user
  mount_uploader :board_image, BoardImageUploader
  validates :title, presence: true, length: { maximum: 255 }
  validates :body, presence: true, length: { maximum: 65_535 }
end
```

##uploadファイルでの設定

```ruby:uploard
class BoardImageUploader < CarrierWave::Uploader::Base
  storage :file

  def store_dir
    "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
  end
 
  def default_url
    'board_placeholder.png'
  end

  def extension_whitelist
    %w[jpg jpeg gif png]
  end
end
```
ここで、

def default_url
  'board_placeholder.png'
end

は、デフォルトでの画像を指定しています。
プレビューでは最初にここで指定した画像が表示されます。


def extension_whitelist
  %w[jpg jpeg gif png]
end

は、アップロードできる拡張子を制限します。


##formを作成

formをパーシャルで作成します。

ここでは、画像用のformはをfile_fieldを用います。

```ruby:_form.html.erb
<%= form_with model: board, local: true, class: "form-group" do |f| %>
  <%= render "shared/error_messages", object: f.object %>
  <div class="form-group">
    <%= f.label :title %>
    <%= f.text_field :title, class: "form-control", id: "board_title" %>
  </div>  
  <div class="form-group">
    <%= f.label :body %>
    <%= f.text_area :body, class: "form-control", id: "board_body", rows: "10" %>
  </div>
  <div class="form-group">
    <%= f.label :board_image %>
    <%= f.file_field :board_image, class: "form-control", accept: 'image/*', onchange: 'previewFileWithId(preview)' %>
    <%= f.hidden_field :board_image_cache %>
    <div class='mt-3 mb-3'>
    <%= image_tag board.board_image.url, id: 'preview', size: '300x200' %>
    </div>
  </div>
  <%= f.submit (t 'defaults.create'), class: "btn btn-primary"%>
<% end %>

```

ここでの、accept: 'image/*は、画像ファイル全般を指定しています。


```
<%= image_tag board.board_image.url, id: 'preview', size: '300x200' %>
```
プレビューを表示しています。boardモデルのboard_imageのurlを呼び出し、表示しています。

```
<%= f.hidden_field :board_image_cache %>
```

hidden属性でimage_cacheは、画像を指定したけれども、バリデーションエラーなどにより保存が失敗した場合の画面再表示時などに、画像情報をキャッシュしておくための領域。

```
<%= f.file_field :board_image, class: "form-control",
 accept: 'image/*',onchange:'previewFileWithId(preview)' %>
```

onchangeとは、イベントハンドラープロパティで、ユーザーの入力の応じて動的に表示内容を変えるときに発生するchangeイベントを処理します。

input,select,textarea要素において、ユーザーによる要素の変更が完了した際に行われます。

onchangeの書き方は、

```
onchange: '関数名()'
```
で表します。

##JSファイルを設定する

javascriptの記載は、application.jsには記載せず、別の専用ファイルを作成する。

ここでは、application.jsは個別のJavaScriptを読み込む専用のファイルのため、preview.jsファイルを作成し記載する。

```ruby:preiew.js
function previewFileWithId(id) {
    const target = this.event.target;
    const file = target.files[0];
    const reader  = new FileReader();
    reader.onloadend = function () {
        preview.src = reader.result;
    }
    if (file) {
        reader.readAsDataURL(file);
    } else {
        preview.src = '';
    }
}
```

1行ずつ解説していくと、

```
const target = this.event.target;
```
は、

traget・・・操作を差し込む対象のオブジェクト
event.target・・・最初にイベントが起こった要素です。今回だと、ファイルを選択した時のイベントを示します。

```
const file = target.files[0];
```
targetには「files」というプロパティが用意されています。
管理されているファイルは、「File」というオブジェクトの形をしています。
このFileオブジェクトには、ファイルに関する各種の情報や、ファイルアクセスのためのメソッドなどがまとめられているのです。

files[0]で一つ目のFileオブジェクトを取り出しています。

```
const reader  = new FileReader();
```
FileReaderオブジェクトで、ファイルを取得します。

```
reader.onloadend = function () {
        preview.src = reader.result;
    }
```

onloadend、FileReaderのイベントです。データの読み込みが成功か失敗に関わらず終了した時にloadendイベントが発生し、ここに設定したコールバック関数が呼び出されます。

そして、取得されたファイルの結果を、previewのsrc属性に指定しています。


```
if (file) {
        reader.readAsDataURL(file);
    } else {
        preview.src = '';
    }
```

readAsDataURL()は、FileReaderのメソッドです。ファイルを、Data URIとして読み込むメソッドです。例えば画像ファイルをこのメソッドで読み込んで、読み込んだデータをimg要素のsrc属性に指定すればブラウザに表示できます。

前文でscr属性を指定したので、ここで、画像を表示させます。

### エラーメッセージを設定

最後にエラーメッセージを設定していきます。

carrierwave.ja.ymlを新たに生成し、記載していきます。

```ruby:carrierwave.ja.yml
ja:
  errors:
    messages:
      carrierwave_processing_error: 処理できませんでした
      carrierwave_integrity_error: は許可されていないファイルタイプです
      carrierwave_download_error: はダウンロードできません
      extension_whitelist_error: "は %{allowed_types}の形式でアップロードしてください"
      extension_blacklist_error: "%{extension}ファイルのアップロードは許可されていません。アップロードできないファイルタイプ: %{prohibited_types}"
      content_type_whitelist_error: "%{content_type}ファイルのアップロードは許可されていません。アップロードできるファイルタイプ: %{allowed_types}"
      content_type_blacklist_error: "%{content_type}ファイルのアップロードは許可されていません"
      rmagick_processing_error: "rmagickがファイルを処理できませんでした。画像を確認してください。エラーメッセージ: %{e}"
      mini_magick_processing_error: "MiniMagickがファイルを処理できませんでした。画像を確認してください。エラーメッセージ: %{e}"
      min_size_error: "を%{min_size}以上のサイズにしてください"
      max_size_error: "を%{max_size}以下のサイズにしてください"
```

これで完成です！