同じにやっても動かない

書いてある通りやっているのに動かないのです

FacebookとかTwitterの画像アップロードみたいな動作をさせたい(form_for(multipart) + ajax + ファイル送信)

[追記]
multipartのform_forをAjax化する場合の方法について記載しています。
普通のtext formであればform_forのオプションで:remote=>trueするだけでAjax化できるはずです。


FacebookとかTwitterで記事を作成するときに、写真を選択するとすぐにサーバーにアップルされてサムネイル表示されるじゃないですか、あれをやってみたかったのだけど、結構時間がかかってしまいました。

何かいいプラグインをご存知の方いらっしゃいましたら教えてくださいませ。

さて、実現したい事を箇条書きにすると

  • 画像を選択 (file_field)
  • 画像が選択されたら自動でsubmit
  • submitした画像を自動で表示

これをajaxでもって画面遷移無しでやりたいということになります。

色々調べた結果、以下の組み合わせで実現することができました。

  • Dragonfly(画像管理)
  • jquery form プラグインの ajaxForm (FormのAjax化)

以下、手順になります。

Dragonflyのインストール

まずはnon Ajaxで画像ファイルを扱えるようにします。
Dragonflyの設定はこのあたりを参考にすると簡単にインストールできます。

画像を選択すれば自動でsubmit

これは、jqueryのchangeメッソドを使用してみました。

Dragonflyを使うと、選択ファイル名を表示するフィールドが下記のようなhtmlになります。

<form accept-charset="UTF-8" action="/photos" class="new_photo" enctype="multipart/form-data" id="new_photo" method="post">
  <div class="field">
    <input id="photo_photo_image" name="photo[photo_image]" type="file" />
  </div>
</form>

なので、ここ(id="photo_photo_image")が変化したらフォーム(id="new_photo)のsubmit()をトリガーするようにjqueryを書きます。

  $(document).ready(function(){
    $("#photo_photo_image").change(function(){
      $("#new_photo").submit();
    })
  });

これで、submitボタンが必要なくなりますので、上述のjqueryも合わせるとviews/photos/_form.html.erbはこんな感じになります。

<script type="text/javascript">
  $(document).ready(function(){
    $("#photo_photo_image").change(function(){
      $("#new_photo").submit();
    })
  });
</script>
<%= form_for(@photo, :html=>{:multipart=>true}) do |f| %>
  <div id="image_preview"></div>
  <div class="field">
    <%= f.file_field :photo_image  %>
  </div>
<% end %>

これで、ファイルを選択しただけでファイルがUploadされるようになります。

MultipartフォームのAjax

jquerのプラグインを使うと簡単にできると、ここで教えて頂きましたので、この方法で試してみます。

フォームのajax化は非常に簡単で、下記javascriptを書くだけです。
#new_photoはform_forが生成するidになります。

<script src="http://malsup.github.com/jquery.form.js"></script> 
<script>
  $(document).ready(function(){
    $(function(){
      $('#new_photo').ajaxForm({dataType: 'script'});
    });
  });
</script>

これでフォームがajax化されました。

次に、コントローラーの修正。
create.js.erbに飛ばしてくれるように、photos_controller.rbに "format.js" を追記します。

 def create
    @photo = Photo.new(params[:photo])

    respond_to do |format|
      if @photo.save
        format.js # <=ここを追記
        format.html { redirect_to @photo, notice: 'Photo was successfully created.' }
        format.json { render json: @photo, status: :created, location: @photo }
      else
        format.html { render action: "new" }
        format.json { render json: @photo.errors, status: :unprocessable_entity }
      end
    end
  end

今回は、formがajaxで送信されたときに、送信された写真をプレビュー表示させていので、テンプレートにid='image_preview'というブロックを用意しておき、
そこに画像を描画することにします。

views/photos/create.js.erb

$("#image_preview").html(
"<%= escape_javascript(render :partial=>"image_preview", :locals=>{:photo=>@photo}) %>"
);

views/photos/_image_preview.html.erb

<%= image_tag photo.photo_image.thumb('400x200').url %>

これで、ファイルを選択すると、フォームが自動で送信されプレビュー表示までを画面遷移無しで行うようになります。
_form.html.erbの最終的な内容はこんな感じです。

<script src="http://malsup.github.com/jquery.form.js"></script> 
<script type="text/javascript">
  $(document).ready(function(){
    $(function(){
      $('#new_photo').ajaxForm({dataType: 'script'});
    });
    $("#photo_photo_image").change(function(){
      $("#new_photo").submit();
    })
  });
</script>
<%= form_for(@photo, :html=>{:multipart=>true}) do |f| %>
  <div id="image_preview"></div>
  <div class="field">
    <%= f.file_field :photo_image  %>
  </div>
<% end %>