Railsアプリに画像アップロード機能を実装できる「carrierwave」は手軽で便利ですよね。

しかし、公式ドキュメントに沿ってそのまま作成したデフォルトの状態は、お世辞にも「カッコいい」とは言えない状態の見た目になっています。

ブラウザの違いなど人によって微妙に見え方は異なるかもしれませんが、大体こんな感じですよね。

 

最低限動きさえすれば良いという方は別ですが、自分の場合は割と見た目も重要視したい人間なので、できる事ならもう少しお洒落に仕上げたい…。

 

今回は、carrierwaveで実装した画像アップロード機能をデフォルトの地味な見た目からお洒落なものへ改造していきたいと思います。

完成イメージ

実際のコード

参照記事

 

大まかな手順については上の記事が非常に参考になるので読んでみてください。

自分の場合は複数画像アップロードに加え、「simple_form」というgemを使っていたり、スタイルシートはscssで書いているため微妙にコードが異なる結果となりました。

 

前提
  • ラーメンのレビューサイトを開発しており、これからRamenShopクラスに紐づいたReviewクラスのインスタンスを作成

※細かい部分は自分の作っているアプリに置き換えながら書いてみください。

 

app/views/reviews/_form.html.erb
...

<%= simple_form_for(@review, url: reviews_path(@ramen_shop.id), method: :post, local: true) do |f| %>
  <% (0..2).each do |n| %>
    <div id="img_field_<%= n %>" onClick="$('#file_<%= n %>').click()" >
      <% if review.images[n].present? %>
        <%= image_tag(review.images[n].url) %>
      <% else %>
        <i class="fas fa-images"></i>
      <% end %>
    </div>
    <%= f.input :images, as: :file, label: false, input_html: { multiple: true, class: "d-none", id: "file_#{n}" } %>
  <% end %>
<% end %>

...
ポイント
  • (0..2)という範囲オブジェクトをeach文で回す事により短いコードで3つのimg_filedを作る。(DRYを意識)
  • simple_formを使う場合、classやidはinput_htmlタグの中に入れる。
app/assets/stylesheets/review.scss
...

$border: #DEE2E6;

$gray: #E8ECF1;
$dark-gray: #aaa;


@for $i from 0 through 2 {
  
  #img_field_#{$i} {
    padding: 5px 8px;
    border: solid 1px $border;
    text-align: center;
    position: relative;
    padding: 0;
    border-radius: 5px;
    cursor: pointer;
    background-position: center;
    background-repeat: no-repeat;
    background-size: contain;
    background-color: $white;
    overflow: hidden;
    box-sizing: border-box;
    transition: 0.3s ease-out;
  
    &:hover {
      background-color: $gray;
      transition: 0.3s ease-out;
      opacity: 0.9;
    }
    
    i {
      font-size: 30px;
      color: $dark-gray;
      line-height: 150px;
      transition: 0.3s ease-out;
    }
  }
}

...
ポイント

 

app/assets/javascripts/image-preview.js
$(function(){
  $fileField = $('#file_0')
 
  $($fileField).on('change', $fileField, function(e) {
    file = e.target.files[0]
    reader = new FileReader(),
    $preview = $('#img_field_0');
 
    reader.onload = (function(file) {
      return function(e) {
        $preview.empty();
        $preview.append($('<img>').attr({
          src: e.target.result,
          width: "100%",
          class: "preview",
          title: file.name
        }));
      };
    })(file);
    reader.readAsDataURL(file);
  });
});

$(function(){
  $fileField = $('#file_1')
 
  $($fileField).on('change', $fileField, function(e) {
    file = e.target.files[0]
    reader = new FileReader(),
    $preview = $('#img_field_1');
 
    reader.onload = (function(file) {
      return function(e) {
        $preview.empty();
        $preview.append($('<img>').attr({
          src: e.target.result,
          width: "100%",
          class: "preview",
          title: file.name
        }));
      };
    })(file);
    reader.readAsDataURL(file);
  });
});

$(function(){
  $fileField = $('#file_2')
 
  $($fileField).on('change', $fileField, function(e) {
    file = e.target.files[0]
    reader = new FileReader(),
    $preview = $('#img_field_2');
 
    reader.onload = (function(file) {
      return function(e) {
        $preview.empty();
        $preview.append($('<img>').attr({
          src: e.target.result,
          width: "100%",
          class: "preview",
          title: file.name
        }));
      };
    })(file);
    reader.readAsDataURL(file);
  });
});

 

app/assets/javascripts/application.js
...

//= require image-preview

...

 

これで上の完成イメージと同じように動作するはずです。お疲れ様でした。

おすすめの記事