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

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

ただいまの
回答率

90.34%

【Rails JS】フォームで画像ファイルのプレビューをeditでも表示したい

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 3,224

besuko

score 14

環境

Ruby(2.3.3)
Ruby on Rails(5.0.1)

gem
carrierwave(アップローダー)
cocoon(ネストフォーム)

保存先
Cloudinary

保存されている画像ファイルを更新時に取得したい

こちらを参照してRailsの画像アップロードフォームにプレビューを表示させました。

枠内にプレビュー表示されます

新規登録時はこのままで問題ないのですが、現状edit(更新)画面では空欄のままとなっており事前に保存されている画像を枠内に表示させたいと考えております。

枠内にプレビュー表示されます
以下のJSファイルを編集が必要かと思いますが、どの部分を変更すれば良いのか教えて頂けませんでしょうか。。
なお、image(画像)モデルはshop(店舗)モデルにネストされています。

$(document).on('change', ':file', function() {
    var input = $(this),
    numFiles = input.get(0).files ? input.get(0).files.length : 1,
    label = input.val().replace(/\\/g, '/').replace(/.*\//, '');
    input.parent().parent().next(':text').val(label);

    var files = !!this.files ? this.files : [];
    if (!files.length || !window.FileReader) return; // no file selected, or no FileReader support
    if (/^image/.test( files[0].type)){ // only image file
        var reader = new FileReader(); // instance of the FileReader
        reader.readAsDataURL(files[0]); // read the local file
        reader.onloadend = function(){ // set image data as background of div
            input.parent().parent().parent().prev('.imagePreview').css("background-image", "url("+this.result+")");
        }
    }
});

サーバーサイドはこのようになっております

class Shop < ApplicationRecord
  has_many :images, dependent: :destroy
  accepts_nested_attributes_for :images, allow_destroy: true
class Image < ApplicationRecord
  belongs_to :shop, optional: true
  mount_uploader :file, ImageUploader
end
  def new
    @shop = Shop.new
    @shop.build_close_date
    2.times { @shop.images.build }
  end

  def edit
    @shop.close_date = CloseDate.new if @shop.close_date.blank?
  end

  def create
      @shop = current_user.shops.build(shop_params)
      respond_to do |format|
        if @shop.save
          format.html { redirect_to @shop, notice: '店舗情報を登録しました' }
          format.json { render :show, status: :created, location: @shop }
        else
          format.html { render :new }
          format.json { render json: @shop.errors, status: :unprocessable_entity }
        end
      end
  end

  def update
    if @shop.update(shop_params)
      format.html { redirect_to @shop, notice: '店舗情報を更新しました' }
      format.json { render :show, status: :uploded, location: @shop }
    else
      format.html { render :edit }
      format.json { render json: @shop.errors, status: :unprocessable_entity }
    end
  end
end
<%= simple_form_for(@shop, :authenticity_token => true, html: { multipart: true }) do |f| %>
<div class="nested-field">
    <%= f.simple_fields_for :images do |image| %>
        <%= render partial: 'image_fields', locals: {f: image} %>
    <% end %>
    <%= link_to_add_association "画像を追加", f, :images, :class => 'btn btn-primary' %>
</div>
<% end %>
<div class="nested-fields form_time_selects imgInput">
    <div class="col-sm-2">
          <div class="input-group">
              <label class="input-group-btn">
                  <span class="btn btn-primary">
                      選択<%= f.file_field :file, :class => "uploadFile", :style => "display:none" %>
                        <%= link_to_remove_association("削除", f, {}) %>
                    </span>
                </label>
            <input type="text" class="form-control" readonly="">
        </div>
    </div>
        <%#= link_to_remove_association("削除", f, {}) %>
</div>
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

質問への追記・修正、ベストアンサー選択の依頼

  • moke

    2017/02/23 13:37 編集

    おお素早い回答、お待たせしてすみません、一応確認なのですが、uploadする画像が2つになっているのは区別するためなんですか?例えば外観と店長の写真とか?関係なければ、一つに複数アップロード可能とか、外観、内装、商品、店員とかカテゴリー分けかつ複数登録可能とかできますが。
    あと当方attachementを使ってるので、読み替えていただく必要があるかもしれません

    キャンセル

  • besuko

    2017/02/23 13:52

    初期状態で2つフォームがあるのは特に意味はございません。複数枚アップロード出来るようにしておりますが、現時点ではカテゴリー分けや決まったルールはございません。ただ保存出来る枚数に制限が無い状態にしてあります。

    キャンセル

  • moke

    2017/02/23 14:38

    一応CarrierWave仕様で書いてみました。CarrierWaveを使ったことがないので間違ってたらすみません

    キャンセル

回答 2

0

うーん、もうちょっと頑張って調べましょうよ。
とりあえず環境を整えアップロー画面をべたのHTMLで作成し、デバッグ実行してみるとアップロードファイルを取得できましたが。

index.html.erb

<form id="my-awesome-dropzone" class="dropzone" action="/shops/" method="post">
    <div class="dropzone-previews" style="width:100%; height:100%"></div>
    <input type="email" name="username" />
    <input type="password" name="password" />
    <button type="submit">Submit data and files!</button>
</form>
<script>
    Dropzone.autoDiscover = false;
    Dropzone.options.myAwesomeDropzone = {
        autoProcessQueue: false,
        uploadMultiple: true,
        parallelUploads: 100,
        paramName: 'fuga',
        maxFiles: 100,
        init: function () {
            var myDropzone = this;
            this.element.querySelector("button[type=submit]").addEventListener("click", function (e) {
                e.preventDefault();
                e.stopPropagation();
                myDropzone.processQueue();
            });
        }
    }
    var dropzone = new Dropzone('#my-awesome-dropzone');
</script>

shop_controller.rb

class ShopsController < ApplicationController
  protect_from_forgery with: :null_session

  def fuga_params
    params.require(:fuga).permit(
      :name, :description, :price,
      images_attributes: [:fuga]
    )
  end

  def index
    hoge = 3
  end

  def create
    params[:fuga].each do |idx|
      # アップロードされたファイルのファイル名をコンソールに表示
      logger.debug(params[:fuga][idx].original_filename)
    end
    hoge = 3

  end
end

ブラウザー画面
イメージ説明

デバッグ画面
イメージ説明

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/02/23 10:55

    そうですね、、これまでもとてもご親切にして頂け甘えておりました。
    それにも関わらず環境の再現からデバッグのフィードバックまで教えて頂き本当に感謝致しております。

    残念ながら私の環境では同じように動かずにおります。
    何か他に要因があるかと思いますので、もう少し粘ってみます。

    キャンセル

0

とりあえず、プロトタイプなのでグタグタです。需要があれば、そのうちgemにまとめます。
複数構造にしたり、shopからstaffにネストしてとかも可能です。
まず、rails-assets-bootstrap-fileinputを追加します。
めんどくさいのでrails-assetsを使います。

gem file
source 'https://rails-assets.org' do
gem 'rails-assets-bootstrap'
gem 'rails-assets-bootstrap-timepicker'
gem 'rails-assets-bootstrap-html5sortable'
gem 'rails-assets-bootstrap-fileinput', '~> 4.3.8'
gem 'rails-assets-bootstrap-tagsinput'
end


application.css
application.js
は環境に合わせて修正してください

bundle install
して
rake assets:precompile

editでそのまま編集できるように

jsは外部に書いた方がかっこいいけどturbolinksと競合しないようにベタがき

shops/edit.html.erb
<%= from_for(@shop) do |f| %>
<%= f.text_fields :shopname %>
~
<%= f.fields_for :images_attributes do |image| %>
<%= image.file_field :file,class: 'file-upload',name: "#{image.object_name}[file]",'data-json'=>file_input_jbuilder(image.object.file) %>
<%= javascript_tag do %>
$(".file-upload").fileinput($(this).data('json'));
<% end %>

jbuilderがうまく使えなかったのでto_jsonで妥協
imageの削除用のmethodをformat.jsで作っておくこと
パラメータは必要最低限,他にカスタムしたいときは
ここら辺を参照

application_helper.rb
def file_input_jbuilder(images) 
images=Array(images)
({"initialPreview"=>images.map{|image|image.thumb.url},
"initialPreviewAsData"=>true,
"initialPreviewConfig"=>images.map{|image| {"caption"=>image.original_filename,"width"=> '120px',"url"=> url_for({controller: :images,action: :delete,id: image.id}),"key"=> 100}}}).to_json
end


最後にコントローラのstrong_paramaterを修正

shops_controllers.rb
~
def shop_params
params.require(:shop).permit(:shopname,~,images_attributes: [:file])


多分これでオッケーです。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2017/02/28 13:23

    上記のURLに合わせてhelperは以下のようにしてみましたが、やはり表示はされないですね、、

    images.map{|image|['http://res.cloudinary.com/hvrpgrvpi/image/upload/http://res.cloudinary.com/hvrpgrvpi/image/upload&#039;,image.url].join(&#039;/&#039;)},

    キャンセル

  • 2017/02/28 15:56

    ファ?ゲフンゲフン
    さすがに['http://res.cloudinary.com/hvrpgrvpi/image/upload/http://res.cloudinary.com/hvrpgrvpi/image/upload&#039;,image.url].join(&#039;/&#039;)
    はネタだと思いたい

    とりあえず。
    images.map{|image|image.url}
    のままでよかったようですね。
    ちなみに、そのurlとは
    http://res.cloudinary.com/hvrpgrvpi/image/upload/v1487902****/130.jpg
    のことです。これをブラウザに打ち込んで画像が表示されないようなら
    それが問題です。res.cloudinary.comに行って表示の仕方を調べてください。

    キャンセル

  • 2017/03/01 11:08

    有難うございます。
    そうですね、URLを直に打ち込んだ場合は表示されますので、どこかおかしい部分があるのだと思います。
    ゴールまでの道筋が全くイメージ出来ず心が折れそうですがもう少し粘ってみます。。

    キャンセル

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

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

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