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

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

ただいまの
回答率

90.36%

  • JavaScript

    18774questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • Ruby

    8771questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

  • Ruby on Rails

    8173questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

  • jQuery

    7551questions

    jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。

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

受付中

回答 2

投稿 編集

  • 評価
  • クリップ 0
  • VIEW 2,447

besuko

score 10

環境

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/24 10:27

    有難うございます!
    先日より取り組んでいるのですが、一部エラーが出てしまい且つ修正が出来ずにおります。。

    syntax error, unexpected ':', expecting '}' (SyntaxError)
    ...px',"url"=> url_for{controller: :images ,action: :delete ,id...
    ... ^

    キャンセル

  • 2017/02/24 11:25 編集

    すみません
    ブロック渡しになってしまっているようです。
    hashを()でくくってください
    url_for({controller: :images ,action: :delete ,id: image.id})

    キャンセル

  • 2017/02/24 12:03

    有難うございます。
    続いて同ヘルパーの「map」の部分で躓いております。
    mapというものが「配列内のオブジェクトをフォーマットするもの」ということは分かりましたが、それをRubyで扱う方法が分からずにおります。
    ヘルパー内に書かれている内容の意味はJSONというものを学べば理解できる内容なのでしょうか。

    NoMethodError in Shops#edit
    undefined method `map' for #<ImageUploader:0x007fa4be71ae48>
    Did you mean? tap

    キャンセル

  • 2017/02/24 23:30

    ごめんなさい、terateilの更新通知が出ませんでした。
    Carrer wave だと1つのレコードに1つのファイルしかアップロード出来ないのですね。勉強不足でした。
    一応修正しておきます。

    キャンセル

  • 2017/02/25 10:13

    Carriewaveだとそういった制限があるのですね。
    こちらの環境にご配慮頂き感謝致します。

    修正頂きましたコードも、同様に「undefined method `map'」と出てしまうのですが「map」というメソッドをどこかで定義しておく必要がございますでしょうか。

    キャンセル

  • 2017/02/25 10:34

    いやーmapはarrayのmethodです
    arrayを期待して作っていたので、
    引数imagesが単体の場合、Array(Images)で無理やり
    arrayにしてください

    キャンセル

  • 2017/02/25 12:33

    mapはRubyのメソッドなのですね、、知らずに見当違いな質問をしてお恥ずかしい限りです。

    「shop_images=Array(shop_images)」の追加で上記のエラーは出なくなりました。
    またshop/edit画面にも画像は表示されませんが既存ファイルの数だけフォームが表示されているので、かなりゴールに近づきいているように思えます。

    キャンセル

  • 2017/02/25 15:35 編集

    image.thumb.url

    image.url
    にしてみてください
    また、image.urlが相対パスになっていないか確認してください
    ここの値はhttpから始まる絶対パスでないと動かないようです。
    相対パスの場合、images.map{|image|['あなたのroot_path',image.thumb.url].join('/')},
    と置き換えてみてください

    キャンセル

  • 2017/02/26 18:08

    開発・本番ともファイルの保存はCloudinalyというクラウドの画像管理サービスを使っております。
    保存されたファイルは「http://res.cloudinary.com/hvrpgrvpi/image/upload/v1487*******/***.jpg」といったURLとなります。これは絶対パスという理解で良いのでしょうか。

    念のため本番環境(heroku)でも試してみましたが画像は表示されずにおります。
    JSのコンソールを確認してもエラーは表示されず、誤り箇所は特定出来かねている状況でございます。
    なお、現状helperはこのようにしております。
    (imageモデルのfileカラムに保存されたデータを呼び出す)

    def file_input_jbuilder(image)
    images = Array(images)
    ({"initialPreview"=>images.map{|image|image.file.url},
    "initialPreviewAsData"=>true,
    "initialPreviewConfig"=>images.map{|image| {"caption"=>image.file,"width"=> '120px',"url"=> url_for(@shop), "key"=> 100}}}).to_json
    end

    キャンセル

  • 2017/02/26 18:46

    この場合imageがfileに相当するので
    image.file.urlではなく
    image.urlで大丈夫です。
    デバッグするか
    fileimputのdata-json属性をみて
    image.urlがどんな値を表示するか調べ
    例えばimage.urlが'v1487*******/***.jpg'と表示されれば
    images.map{|image|[’http://res.cloudinary.com/hvrpgrvpi/image/upload’,image.url].join('/')}
    といった感じに置き換えてください。

    キャンセル

  • 2017/02/27 13:08

    有難うございます。
    デバッグから値を確認する方法は分からずにおります。。
    「data-json属性」とはソートから確認できるのでしょうか。
    グーグルで調べても分からずにおり初歩的過ぎる質問で申し訳ございません。。

    キャンセル

  • 2017/02/27 13:27 編集

    Webブラウザから表示中のHtmlのソースを読めばわかります。
    対象を右クリックして検証とか
    なければソースを表示とかおしてみて下さい。

    キャンセル

  • 2017/02/28 12:18

    ソースで該当箇所を確認出来ました。
    image.urlの末尾には「'v1487*******/***.jpg'」と表示されておりますので、helperには
    images.map{|image|[’http://res.cloudinary.com/hvrpgrvpi/image/upload’,image.url].join('/')}
    と設置致しました。
    それでも残念ながらファイルは表示されずにおります。
    せっかく親身に教えて頂いているのに結果が出ずに申し訳ございません。

    Shop/edit.html.erb (HTMLソース)
    <h1>Editing shop</h1>
    <p id="image_130">
    <img width="200px" src="http://res.cloudinary.com/hvrpgrvpi/image/upload/v1487902****/130.jpg" alt="130" />

    キャンセル

  • 2017/02/28 12:45

    そのurlをブラウザに打ち込むとちゃんと表示されますか?
    ログインとか必要ないですかね?
    あと末尾がという表現が気になります。
    htmlのinputタグてclassがfile-upload のもののdata-json内に表示されているデータです。もう一度確認下さい

    キャンセル

  • 2017/02/28 12:56

    はい、edit画面はログイン時のみアクセス可能なようにしてあります。
    (ログイン機能はDeviseを用いています)
    改めてソース(HTML)を見たところ、date-json箇所には以下のように表示されていることを確認致しました。

    <input class="file-upload" name="shop[images_attributes][3][file]" data-json="{&quot;initialPreview&quot;:[],&quot;initialPreviewAsData&quot;:true,&quot;initialPreviewConfig&quot;:[]}" type="file" id="shop_images_attributes_3_file" />

    キャンセル

  • 2017/02/28 13:16

    失礼致しました。
    先ほどのソースは別の表示箇所でございました。

    shop/_form.html.erb
    <%= image.file_field :file ,class: 'file-upload',name: "#{image.object_name}[file]",'data-json'=>file_input_jbuilder(image.object.file) %>
    に該当する箇所は以下のソースです。
    (同一ファイルを複数のサイズで保存しているので煩雑になってしまっております。。)

    ソース(HTML)
    <input class="file-upload" name="shop[images_attributes][3][file]" data-json="{&quot;initialPreview&quot;:[&quot;http://res.cloudinary.com/hvrpgrvpi/image/upload/http://res.cloudinary.com/hvrpgrvpi/image/upload/v1487902***/130.jpg&quot;],&quot;initialPreviewAsData&quot;:true,&quot;initialPreviewConfig&quot;:[{&quot;caption&quot;:{&quot;uploader&quot;:{&quot;url&quot;:&quot;http://res.cloudinary.com/hvrpgrvpi/image/upload/v1487902***/130.jpg&quot;,&quot;standard&quot;:{&quot;url&quot;:&quot;http://res.cloudinary.com/hvrpgrvpi/image/upload/c_fill,g_north,h_150,w_100/v1487902***/130.jpg&quot;},&quot;thumbnail&quot;:{&quot;url&quot;:&quot;http://res.cloudinary.com/hvrpgrvpi/image/upload/c_fit,h_200,w_200/v1487902***/130.jpg&quot;},&quot;_100x100&quot;:{&quot;url&quot;:&quot;http://res.cloudinary.com/hvrpgrvpi/image/upload/b_rgb:fff,c_pad,h_100,w_100/v1487902***/130.jpg&quot;},&quot;_280x280&quot;:{&quot;url&quot;:&quot;http://res.cloudinary.com/hvrpgrvpi/image/upload/b_rgb:fff,c_pad,h_280,w_280/v1487902***/130.jpg&quot;},&quot;_540x540&quot;:{&quot;url&quot;:&quot;http://res.cloudinary.com/hvrpgrvpi/image/upload/b_rgb:fff,c_pad,h_540,w_540/v1487902***/130.jpg&quot;},&quot;_800x800&quot;:{&quot;url&quot;:&quot;http://res.cloudinary.com/hvrpgrvpi/image/upload/b_rgb:fff,c_pad,h_800,w_800/v1487902***/130.jpg&quot;}},&quot;identifier&quot;:&quot;image/upload/v1487902***/130.jpg&quot;,&quot;resource_type&quot;:&quot;image&quot;,&quot;storage_type&quot;:&quot;upload&quot;,&quot;version&quot;:&quot;1487902***&quot;,&quot;filename&quot;:&quot;130.jpg&quot;,&quot;public_id&quot;:&quot;130&quot;,&quot;format&quot;:&quot;jpg&quot;},&quot;width&quot;:&quot;120px&quot;,&quot;url&quot;:&quot;/shops/3&quot;,&quot;key&quot;:100}]}" type="file" id="shop_images_attributes_3_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',image.url].join('/')},

    キャンセル

  • 2017/02/28 15:56

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

    とりあえず。
    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を直に打ち込んだ場合は表示されますので、どこかおかしい部分があるのだと思います。
    ゴールまでの道筋が全くイメージ出来ず心が折れそうですがもう少し粘ってみます。。

    キャンセル

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

  • JavaScript

    18774questions

    JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

  • Ruby

    8771questions

    Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

  • Ruby on Rails

    8173questions

    Ruby on Railsは、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

  • jQuery

    7551questions

    jQueryは、JavaScriptライブラリのひとつです。 簡単な記述で、JavaScriptコードを実行できるように設計されています。 2006年1月に、ジョン・レシグが発表しました。 jQueryは独特の記述法を用いており、機能のほとんどは「$関数」や「jQueryオブジェクト」のメソッドとして定義されています。