rookieRailer
rookieRailer

Reputation: 2341

Show image preview after selecting attachment, using Paperclip

I am trying to use paperclip to attach multiple images, and show preview of any image that is attached, before the form is submitted. Has anyone done this already, or can anyone suggest me ways in which this can be done? I know it will require use of jQuery and AJAX, but I have no idea where to look for an attached image, before the form is submitted. Thanks.

Upvotes: 3

Views: 5525

Answers (3)

Ankur Shukla
Ankur Shukla

Reputation: 365

Wrap an image tag in class = "target"

<div class="target">
  <img class="card_image" src="">
</div>

Add the below listener in your js file

$(document).on('change', 'input[type="file"]' ,function(event) {
  var obj_custom = this;
  var files = event.target.files;
  var image = files[0]
  var reader = new FileReader();
  reader.onload = function(file) {
    var img = new Image();
    img.src = file.target.result;
    img.width=84;
    $(obj_custom).siblings('.target').html(img);
  }
  reader.readAsDataURL(image);
});

Upvotes: 2

RyanB
RyanB

Reputation: 707

I've come across this problem several times, and I finally came up with a working (somewhat) easy solution. I don't claim that this code is perfect, but it does work.

edit.html.erb

<%= image_tag "ajax-loader.gif", class: "ajax-loader" %>

<div class="image_preview">
  <% if resource.picture.url %>
    <%= image_tag resource.picture.url %>
  <% end %>
</div>

<iframe id="upload_target" name="upload_target" style="display: none" onLoad="onload_test()"></iframe>

registrations_controller.rb

class RegistrationsController < Devise::RegistrationsController
  def upload_image_preview
    # sleep 3.seconds # for testing how delay looks
    @user = User.find(params[:id])
    @user.update_attributes(preview_picture: params[:user][:picture])
    render text: ""
  end

  def get_image_preview
    @user = User.find(params[:id])
  end
end

user_image_preview.js

var first_pass = true;

function onload_test() {
  if (first_pass == false) {
    $.getScript("/users/5/get_image_preview.js");
  } else {
    first_pass = false;
  }
  $(".ajax-loader").hide();
  $(".image_preview").show();
}

$(function() {
  $("input[type=file]").change(function(){
    var form = $("form#edit_user");
    var original_action = form.attr("action");

    form.attr("action", "/users/5/upload_image_preview");
    form.attr("target", "upload_target");
    form.submit();

    form.attr("action", original_action);
    form.attr("target", "");

    $(".ajax-loader").show();
    $(".image_preview").hide();
  });
});

Note: I hard-coded some of the URL values. This was just for debugging, I will have data attribute(s) on the file_field tag that will tell my javascript what URL to post the image to.

get_image_preview.js

$("div.image_preview").html('<%= escape_javascript(image_tag @user.preview_picture.url) %>');

Upvotes: 0

Rene van Lieshout
Rene van Lieshout

Reputation: 372

You can't use AJAX for file uploads. Sadly browsers do not support that to be passed using an XHR. What I would suggest is to add an iframe (which is also done by all gems / plugins that allow async image upload) and add an unload to it. When the file input changes, change the target of the main form to the name of the iframe, submit it and wait for the onload of that iframe.

It would be something like this:

<iframe name="image_preview" id="image_preview" src="some_blank_page" onload="show_image_preview()"></iframe>

and

function show_image_preview() {
  // read content here. Note: this function is also called when the some_blank_page is loaded!
  var data = window.image_preview.document;
  if (data && data.getElementById("preview")) {
    // now either do something with the preview or just place the iframe so it
    // is already positioned correctly
  }
}

function file_field_changed() {
  var form = $('..');
  var original_action form.action;
  form.action = "special_preview_path";
  form.target = "image_preview";
  form.submit();

  form.action = original_action;
}

Have your special_preview_path return a preview inside a

You can also create the iframe in the onchange event and place it below each of the file input fields.

I hope this makes sense! Just let me know if you need some more info about this.

Upvotes: 3

Related Questions