2

I have a simple form(product model) and I’m also using the cocoon gem for the nested fields(attachments model) that I have inside that form. When I create a product I use the cocoon gem for the nested fields in order to create attachments(images) and associate them to that product. The user can add a cocoon field and add an image to that field. I also have a preview inside each field so the user can view the image they attach to that field. But the only problem is that the preview only shows in the first cocoon field. If I try to add an image to another cocoon field the image preview doesn’t appear inside that field. I would like to be able to add a cocoon field, then add the image to that field and view the preview of that image inside that field. Any ideas on how I can change around my code in order to achieve the above?

enter image description here

This is the setup I have:

<%= f.simple_fields_for :attachments do |attachment| %>
  <%= render 'products/attachment_fields', form: attachment  %>
<% end %>

<div class="links" id="add_attachment">
  <%= link_to_add_association raw('<i class="fas fa-plus"></i>'), f, :attachments, form_name: 'form'  %>
</div>

attachment_fields partial :

<div class="nested-fields">
  <div class="col-md-12">
    <div class="image-border">
      <div class="image-border-content">
        <div class="parent">
          <div class="image-content" id="divImageMediaPreview">
            <div class="font-awesome-image"><i class="far fa-image"></i></div>
          </div>
        </div>
        <label class="btn btn-light btn-sm" style="width: 100%;">
          Add a file!
          <span style="display:none;">
            <%= form.input :image, label: false, as: :file, input_html: { class:"custom-file-input", id: "ImageMedias" } %>
          </span>
        </label>
      </div>
    </div>
    <div class="links" style="margin-bottom: 10px;">
      <%= link_to_remove_association raw('<i class="far fa-trash-alt"></i> Remove'), form, class: "btn btn-light btn-sm", :style => "width: 100%; margin-top: -15px;" %>
    </div>
  </div>
</div>

Image preview:

$("#ImageMedias").change(function () {
    if (typeof (FileReader) != "undefined") {
        let dvPreview = $("#divImageMediaPreview");
        dvPreview.html("");
        $($(this)[0].files).each(function () {
            let file = $(this);
            let reader = new FileReader();
            reader.onload = function (e) {
                let img = $("<img />");
                img.attr("style", "width: 100%; height:auto;");
                img.attr("src", e.target.result);
                dvPreview.append(img);
            }
            reader.readAsDataURL(file[0]);
        });
    } else {
        alert("This browser does not support HTML5 FileReader.");
    }
});

Update 1

I got it to work with a minor adjustment to @nathanvda answer. But I have a small glitch. If I add one field at a time, the preview appears in each field but if I add more then one field I get the issue that is showing in the image below. Any idea how I can fix this issue ?

enter image description here

This is the exact code I'm using:

<div class="nested-fields">
  <div class="col-md-12">
    <div class="image-border">
      <div class="image-border-content">
        <div class="parent">
          <div class="image-content image-media-preview">
            <div class="font-awesome-image"><i class="far fa-image"></i></div>
          </div>
        </div>
        <label class="btn btn-light btn-sm" style="width: 100%;">
          Add a file!
          <span style="display:none;">
        <%= form.input :image, label: false, as: :file, input_html: { class:"custom-file-input" } %>
          </span>
        </label>    
      </div>
    </div>
    <div class="links" id="remove_attachment" style="margin-bottom: 10px;">
      <%= link_to_remove_association raw('<i class="far fa-trash-alt"></i> Remove'), form, class: "btn btn-light btn-sm", :style => "width: 100%; margin-top: -15px;" %>
    </div>
  </div>
</div>




$(function() {
    $('input[type=file]').change(function(){
        if (typeof (FileReader) != "undefined") {
            let dvPreview = $(this).parents(".image-border").find(".image-media-preview");
            dvPreview.html("");
            $($(this)[0].files).each(function () {
                let file = $(this);
                let reader = new FileReader();
                reader.onload = function (e) {
                    let img = $("<img />");
                    img.attr("style", "width: 100%; height:auto;");
                    img.attr("src", e.target.result);
                        dvPreview.append(img);
                }
                reader.readAsDataURL(file[0]);
            });
        } else {
            alert("This browser does not support HTML5 FileReader.");
        }
    })
});
code_aks
  • 1,972
  • 1
  • 12
  • 28
Theopap
  • 715
  • 1
  • 10
  • 33
  • This concerns the update. Do you allow users to select multiple images? If so what do you expect to show the user? There seems to only be room for one image in your screenshot. – 3limin4t0r May 19 '20 at 16:00
  • The glitch is showing two images where there is only room for one. How did this happen? When you say "adding more than one field at a time" --> what does that mean precisely? – nathanvda May 23 '20 at 11:55

2 Answers2

2

You use an element with an id of #divImageMediaPreview: this means that html expect this element to be present only once on the page. You need to give the element a class, and then search the closest instance.

For instance, in your view write

<div class="nested-fields">
  <div class="col-md-12">
    <div class="image-border">
      <div class="image-border-content">
        <div class="parent">
          <div class="image-content image-media-preview">
            <div class="font-awesome-image"><i class="far fa-image"></i></div>
          </div>
        </div>
        <label class="btn btn-light btn-sm" style="width: 100%;">
          Add a file!
          <span style="display:none;">
            <%= form.input :image, label: false, as: :file, input_html: { class:"custom-file-input", id: "ImageMedias" } %>
          </span>
        </label>
      </div>
    </div>
    <div class="links" style="margin-bottom: 10px;">
      <%= link_to_remove_association raw('<i class="far fa-trash-alt"></i> Remove'), form, class: "btn btn-light btn-sm", :style => "width: 100%; margin-top: -15px;" %>
    </div>
  </div>
</div>

And then in your code do the following:

$("#ImageMedias").change(function () {
    if (typeof (FileReader) != "undefined") {
        let dvPreview = $(this).parents(".image-border").find(".image-media-preview");
        dvPreview.html("");
        $($(this)[0].files).each(function () {
            let file = $(this);
            let reader = new FileReader();
            reader.onload = function (e) {
                let img = $("<img />");
                img.attr("style", "width: 100%; height:auto;");
                img.attr("src", e.target.result);
                dvPreview.append(img);
            }
            reader.readAsDataURL(file[0]);
        });
    } else {
        alert("This browser does not support HTML5 FileReader.");
    }
});

so the main change is

let dvPreview = $(this).parents(".image-border").find(".image-media-preview");

this will search a parent element up in the DOM tree with class image-border and then we descend the DOM to find your preview element. It is possible that the simpler $(this).closest('.image-media-preview') also works, but I was not sure.

nathanvda
  • 49,707
  • 13
  • 117
  • 139
  • Thanks for the reply @nathanvda. I added the above code and only the first field shows the image preview, when I try to add another image to another field nothing shows. Also I get no errors in the console too. – Theopap May 16 '20 at 05:53
  • That was what you had before, right? You did remove the `id` of the `image-content` div? – nathanvda May 18 '20 at 11:42
  • Yes, I replaced the entire div with this: `
    `
    – Theopap May 18 '20 at 11:47
  • I got it to work with the help from your answer. I still have a small issue though, I have updated my question with all the details about the issue, can you please take a look and let me know what I can do to fix it. – Theopap May 19 '20 at 11:06
0

Facing same problem solved by below code, where I created a div with class="upload_preview" containing image tag given id="preview_image" then on change event class="upload_image" to form field of image :-

NOTE :- I used the same form for create and edit page that's why I used if else condition for preview image

<%= f.fields_for :images, f.object.images.present? ? f.object.images : f.object.images.build do |image_form| %>
    <div class="field-group">
      <div class="form-group">
        <label class="font-weight-bold">Image</label>
        <div class="upload_preview preview_style">
          <% if image_form.try(:object).try(:image).present? %>
            <img id="preview_image" class="preview_img" src="<%= image_form.try(:object).try(:image_url) %>" alt="" width="40px" height="40px" />  ## image preview
          <%else%>
            <img id="preview_image" class="preview_img" src="/assets/default-image.png" alt="" width="40px" height="40px" />  ## image preview
          <% end %>
        </div>  
        <%= image_form.file_field :image, class: "form-control upload_image"%> ## on change event
      </div>
      <div class="form-group">
        <%= image_form.link_to_remove "Delete image", class: "btn btn-danger btn-sm" %>
      </div>
    </div>
  <% end %>
  <div class="form-group">
    <%= f.link_to_add "Add more image", :images, class: "btn btn-info btn-sm" %>
  </div>

java script code

<script type="text/javascript">
  $(document).ready(function(){
    $(document).on('change', '.upload_image', function() {
      readURL(this);
    });
  });

 function readURL(input) {
   if (input.files && input.files[0]) {
     var reader = new FileReader();
     reader.onload = function(e) {

   $(input).siblings('.upload_preview').children('#preview_image').attr('src', e.target.result);
    }
   reader.readAsDataURL(input.files[0]);
  }
  }
</script>
code_aks
  • 1,972
  • 1
  • 12
  • 28