質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

87.59%

Cocoonで追加された画像にプレビューを表示したい(rails)

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 1,911

score 177

いつもお世話になっております!

RailsのCocoonを使ってネスト化された画像を追加していきたいと思っているのですが、下記のJSプレビューですと、追加してでてきた項目のプレビューidがユニークでないため、2枚目以降のプレビューを表示させることができません。
追加されるアイテムにidを振ることができれば良いのですが、何か良いアイデアなどはございますでしょうか。

開発環境

  • Ruby 2.4.0
  • Rails 5.2.2
  • Cocoon 1.2.12

apps/views/users/new.html.erb

・・・
      <div class="formImage">
        <h3>画像</h3>
        <%= f.fields_for :images do |m| %>
          <div id="links">
            <%= link_to_add_association "画像を追加", f, :images, partial: 'image' %>
          </div>
        <% end %>
      </div>
・・・

apps/views/user/_image.html.erb

<div class="nested-fields">
  <div class="input-file updateIcon">
    <div class="imgPreview">
      <%= f.file_field :image, type: "file", id: "imageFile", accept: "image/*" %>
    </div>
    <%= link_to_remove_association "Remove image", f %>
  </div>
</div>

<script data-turbolinks-eval="false">
  $("#imageFile").change(function() {
    if (!this.files.length) {
      return;
    }

    var file = $(this).prop("files")[0];
    var fr = new FileReader();
    $(".imgPreview").css("background-image", "none");
    fr.onload = function() {
      $(".imgPreview").css("background-image", "url(" + fr.result + ")");
    };
    fr.readAsDataURL(file);
    $(".imgPreview img").css("opacity", 0);
  });
</script>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

cocoon の追加されるフォーム内でIDを取得できればというのは自分も思います

とりあえず cocoon で追加される view は JS にうめこまれる静的なものなので
その中で ID を指定するとかならず重複してしまい HTML 規約的にNGなので
<%= f.file_field :image, type: "file", class: "imageFile", accept: "image/*" %>
のように id は消すかクラスに変えるしかありません

その上で1つの解決方法は cocoon:after-insert を捉えてじぶんでユニーク ID を付与してしまう方法です

let counter = 0;

$(document).ready(function() {
    $('.formImage') // 
      .on('cocoon:after-insert', function(e, insertedItem) {
        $(insertedItem).find('.imageFile').attr('id', 'imageFile_' + counter++);
      })
  :


などとしておけば

$(".imageFile").change(function() {
  const id = $(this).attr('id');


で取り出すことができます

もう1つの解決方法は $(".imgPreview") とかいてしまうとドキュメント内すべてが対象になってしまうので
$(this).parent(".imgPreview") 
に置き換えてスコープを自分の親にだけ限定することでも実現できるかと思います

どちらも実際にコードを動かしたわけではないので
文法ミス記述ミス等あったらごめんなさい

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/03/24 05:45

    myptさま、ご回答いただきましてありがとうございます!
    非常に参考になりました。
    Cocoonのドキュメントにもちゃんと書いてありましたね。。

    なんとかそれぞれのCocoonにidを付与することに成功いたしました!
    ですが、下記の部分が反応しないのですが、、
    JSの書き方に問題ありますかね(汗)

    $("#imageFile_" + counter++).change(function() {
    if (!this.files.length) {
    return;
    }

    キャンセル

  • 2019/03/24 11:08 編集

    counter++ は常に変化し続けるので作ったのと別のIDになってると思います

    あと cocoon の中に JS を書くと静的な同じIDしかかけないので
    - cocoon:after-insert で追加される<script>内のJSのIDの値も書き換える
    - JSはnew側においてIDは動的に習得する
    のどちらかが必要になると思います

    イベントを捉えるのはあくまで全クラス $(".imageFile") にしておいて
    とらえたイベントの中でIDを取得してどのファイルフィールドが発火したか識別する必要があります

    やりたいことはファイルフィールドのイベントを捉え
    .imgPreview のCSSを捉えることなので
    .on('cocoon:after-insert', function(e, insertedItem) {
    const id = 'imgPreview_' + counter++;
    // .imageFile の target 属性に対応する .imgPrevies の #id をうめこむ
    $(insertedItem).find('.imageFile').data('target', "#" + id);
    // .imgPreview に ID を設定する
    $(insertedItem).find('.imgPreview').attr('id', id);
    }
    のように imageFile に対応する imgPreview のIDをうめこんでおいて
    .imageFile が change したらそのターゲットを取り出してターゲットのCSSを変化させる
    というようなコードになると思います
    (bootstrap でよく使われる方法)

    イベント発火時のコードは
    $(".imageFile").change(function() {
    // .imageFile (= $this) にセットしてあるターゲット属性を取り出す
    const target = $(this).data('target');
    // ターゲット(.imgPreview の #id 文字列)から.imgPreview のCSSを変化させる
    $(target).css("background-image", "none");
    }

    のような感じでしょうか

    キャンセル

  • 2019/03/26 06:11

    詳しい説明をいただきまして、ありがとうございます。
    精査してトライしてみたいと思います!

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 87.59%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る