Rails: Image preview inside a cocoon gem field - javascript

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?
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 ?
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.");
}
})
});

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.

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>

Related

notify user, on image sbmission , before submit rails

<div class="form-group">
<label><%= (I18n.t 'user.form.your_avatar')%></label>
<div class="push">
<%= image_tag #user.avatar, class: 'img-avatar' if #doctor.avatar.attached? %>
</div>
<div class="custom-file">
<%= form.file_field :avatar, class: 'custom-file-input', data: { toggle: 'custom-file-input' } %>
<%= form.label :avatar, I18n.t('doctor.form.choose_new_avatar'), class: 'custom-file-label' %>
</div>
</div>
I want to inform user once he chooses image image uploaded successfully , before submitting the data. How can this be handled? Any logic?
Then you just need a little jQuery script to add a notification after an image was chosen.
$(document).ready(function () {
$(document).on('change', '.custom-file-input', function (_e) {
alert("You've added an image!");
});
});

Rails AJAX / Spinner not working properly

My spinner show but only from the second search occurence on...
When I search for a stock the first time nothing appears. Here's my repo: https://github.com/Ardzii/finance-tracker and the heroku: https://finance-tracker-ardzii.herokuapp.com/
And, so that you don't have to look the repo:
Here's the view: _lookup.html.erb (partial)
<div id="stock-lookup">
<h3>Search for stocks</h3>
<%= form_tag search_stocks_path, remote: true, method: :get, id: 'stock-lookup-form', spinner: true do %>
<div class="form-group row no-padding text-center col-md-12">
<div class="col-md-10">
<%= text_field_tag :stock, params[:stock], placeholder: "Stock ticker symbol", autofocus: true, class: 'form-control search-box input-lg' %>
</div>
<div class="col-md-2">
<%= button_tag(type: :submit, class: "btn btn-lg btn-success") do %>
<i class="glyphicon glyphicon-search"></i> Look up a stock
<% end %>
</div>
</div>
<%= render 'common/spinner' %>
<div id='stock-lookup-results'></div>
<% end %>
</div>
The spinner partial: _spinner.html.erb
<div id="spinner" class="row col-md-12 text-center spinner" style="display: none;">
<i class="glyphicon glyphicon-refresh glyphicon-spin"></i> Processing...
</div>
My Javascript file: search.js.erb
var init_stock_lookup;
init_stock_lookup = function(){
$('#stock-lookup-form').on('ajax:before', function(event, data, status){
show_spinner();
});
$('#stock-lookup-form').on('ajax:complete', function(event, data, status){
hide_spinner();
});
$('#stock-lookup-form').on('ajax:success', function(event, data, status){
$('#stock-lookup-results').html("<%= j render 'stock', {stock: #stock} %>");
init_stock_lookup();
});
}
$(document).ready(function() {
init_stock_lookup();
})
The view it renders: _stock.html.erb
<div class="well results-block">
<% if stock.present? %>
<strong>Symbol:</strong> <%= stock.ticker %>
<strong>Name:</strong> <%= stock.name %>
<strong>Price:</strong> <%= stock.price %>
<% else %>
<i>Stock not found</i>
<% end %>
</div>
My application.js file:
var hide_spinner = function (){
$("#spinner").hide();
}
var show_spinner = function (){
$("#spinner").show();
}
I'm not sure the problem was stated really clearly, so I will restate it here:
The spinner works only after a first search. On the first lookup, I have no spinner being displayed...
Thanks in advance and as usual for your help!

change style to display: block;

I have a Rails application which has some <li> elements with style display: none; CSS property. I want to make it appear on the page only while dragging that element. However, some elements don't have display: none; style. How do I do it ?
Following is my HTML code :
<div class="col-xs-12 col-md-6 col-lg-3">
<li id="<%=video_upload.id %>" <% if video_upload.invisible == true %> <%= "style=display:none;" %> <%end%> >
<% if mobile? %>
<a href="<%= showvideo_path(video_upload.id) %>">
<div style="background-color: white">
<%= image_tag("/images/upload_images/"+"#{video_upload.imagename}", height: '130', width: '200') %>
</div>
</a>
<% else %>
<div style="background-color: white">
<img src="<%= '/images/upload_images/'+"#{video_upload.imagename}" %>" class="img-responsive" style="cursor:pointer" onclick="popVideo('https://app.box.com/embed/s/<%= video_upload.link[/([^\/]+)$/] %>');" >
</div>
<% end %>
<span style="float: left">
<%= File.basename(video_upload.imagename, '.*') %>
</span>
<span style="float: right">
<%= link_to '', edit_video_uploads_path(video_upload), {class: 'btn btn-primary glyphicon glyphicon-pencil', :style => 'color: white'} %>
<%= button_tag(:del_flag => 'button', :id => video_upload.id ,:class => 'btn btn-danger user-approve-btn glyphicon glyphicon-trash') do
""
end %>
</span>
</li>
</div>
I am using this javascript for drag and drop.
Select all your <li> elements, and show them while dragging. Then hide them when the drag ends:
document.querySelectorAll('li').forEach(li => {
// Show the <li> on drag
li.addEventListener('dragstart', event => {
event.target.style.display = 'block';
});
// Hide each <li> back
li.addEventListener('dragleave', event => {
event.target.style.display = 'none';
});
});
This is using the HTML5 native drag and drop feature as you can see in this blog article. Let me know if it works as you want.
UPDATE:
In case that you want to select 1 element like you said, you just have to add an id="myElement" in that element and then select it like so:
let element = document.querySelector('#myElement');
element.addEventListener('dragstart', event => {
event.target.style.display = 'block';
});
element.addEventListener('dragleave', event => {
event.target.style.display = 'none';
});

I need a refresh before my redirection, why?

I'm doing a web app, and this night, I try to make a preview of the upload image of my post.Like in the photo :
before|| After
And since that, I have to refresh my :edit and :new page post. If I don't do that, the page is frozen !
views/posts/_form :
<div class= "posts-wrapper">
<div class= "post">
<div class= "post-body">
<div class= "image-wrap">
<%= image_tag 'placeholder.jpg', id: 'image-preview', class: 'img-responsive' %>
<%= simple_form_for #post, html: { multipart: true } do |f| %>
</div>
<div class= "row">
<div class= "col-md-12.text-center">
<%= f.error_notification %>
</div>
<div class= "container-fluid">
<div class= "form-group.text-center">
<h4> Upload an image (this is required): </h4>
<%= f.input :image, label: false, input_html: { onChange: 'loadFile(event)' } %>
</div>
<div class= "form-group.text-center">
<%= f.input :caption, label: false, placeholder: 'Add your caption' %>
</div>
<div class= "form-group.text-center">
<%= f.button :submit, class: 'btn-success btn-block' %>
</div>
</div>
</div>
</div>
</div>
<%end%>
</div>
assets/js/posts.js :
var loadFile = function(event) {
var output = document.getElementById('image-preview');
output.src = URL.createObjectURL(event.target.files[0]);
};
And the helpers/appli :
def form_image_select(post)
return image_tag post.image.url(:medium),
id: 'image-preview',
class: 'img-responsive' if post.image.exists?
image_tag 'placeholder.jpg', id: 'image-preview', class: 'img-responsive'
end
How I can solve this problem ?
I try a lot of things, even an automatic refresh of the page when the user is arriving, but it's so dirty !
Can you try this function ?
I replaced URL.createObjectURL by a FileReader call.I think your page is freezed because the object is too heavy.
var loadFile = function(event) {
var reader = new FileReader();
reader.onload = function(e) {
var output = document.getElementById('image-preview');
output.src = e.target.result;
}
reader.readAsDataURL(event.target.files[0]);
};
$("#file").on("change", function(e) {
loadFile(e);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<img id="image-preview" />
<input type="file" id="file" value="input" />
Edit:
in posts.js (after jquery import):
var loadFile = function(event) {
var output = document.getElementById('image-preview');
output.src = URL.createObjectURL(event.target.files[0]);
};
$(document).ready(function() {
$("#file").on("change", function(e) {
loadFile(e);
});
});

Rails: Submit html table content in form to controller

I have some trouble with an rails formular.
I have a form to create "projects".
Project form
<%= bootstrap_form_for(#project) do |f| %>
...
<div class="container-fluid">
<%= f.static_control label:"Vordefinierte Aufgaben" do %>
<div class="actions form-group nopadding">
<div class="center-block">
<%=link_to "", data: {toggle:"modal", target:".newTask-modal"} , class: "btn btn-success center-block btn-sidebar-ok", id: "singlebutton" do%>
<i> <span class="glyphicon glyphicon-plus pull-left btn-sidebar-icon"></span>Neue Aufgabe hinzufügen </i>
<% end %>
</div>
</div>
<div class="task-table">
<%=render 'tasks/table'%>
</div>
<% end %>
</div>
...
<!-- task Modal to create a new task -->
<%=render 'tasks/newTaskModal' %>
<% end %>
<!-- Button -->
<div class="actions form-group">
<div class="center-block">
<%= button_tag( type: 'submit', class: "btn btn-success center-block btn-sidebar-ok", id: "singlebutton") do%>
<i> <span class="glyphicon glyphicon-ok pull-left"></span>Projekt anlegen </i>
<% end %>
</div>
</div>
Within this form a have a table which shows created tasks and a button to open an bootstrap modal. This modal contains a new form to create a task.
Task Form within modal
<%= bootstrap_form_tag() do |f| %>
<div class="col-sm-8">
<%= f.text_field :task_title, label: "Titel", placeholder: "Betreff", icon: "pencil",wrapper: { class: 'icon-addon addon-md'} %>
</div>
<div class="col-sm-4">
<%= f.number_field :task_in_min, label: "Arbeitszeit in Min", placeholder: "Bsp.: 25", icon: "time", wrapper: { class: 'icon-addon addon-md'}%>
</div>
<div class="col-sm-12">
<%= f.text_area :task_description, label: "Beschreibung*", placeholder: "Aufgabenbeschreibung", icon: "pencil", rows: 10, wrapper: { class: 'icon-addon addon-md'}%>
</div>
<%= button_tag( type: 'button', id:"taskSubmit", class: "btn btn-success center-block btn-sidebar-ok") do %>
<i > <span class="glyphicon glyphicon-ok pull-left btn-sidebar-icon"></span> Speichern </i>
<% end %>
<% end %>
If I click to "taskSubmit" a javascript put this task (title, description, min) into the table within the project form. JS create a new table row with the task content.
Javascript
//take data from task form and at a new task element to the task table in project form
$(function TaskSubmitFunction(){
$('#taskSubmit').click(function(){
var task_title = $('#task_title').val();
var task_description = $('#task_description').val();
var task_in_min = $('#task_in_min').val();
//Remove spaces from title
var id = task_title.replace(/ /g,'');
$('#taskTable tr:last').after('<tr id="task_'+id+'"> <td>'+task_title+'</td> <td>'+task_description+'</td><td>'+task_in_min+'</td><td> <span class="pull-right"><span data-toggle="tooltip" data-placement="right" data-original-title="Löschen"><button class="btn btn-sm btn-danger btn-colored delete-position" id="'+id+'"name="button" type="button" task="'+task_title+'"><i><span class="glyphicon glyphicon-remove"/></i></button></span></span></td></tr>');
$('#taskTable').after('<input type="hidden" id="1" name="1" value="'+task_title+'" />');
$('#task_title').val('');
$('#task_description').val('');
$('#task_in_min').val('');
//initialize task delete
$(function TaskDeleteFunction(){
$('#'+id).click(function(){
var task_title = $(this).attr('task');
if (confirm("Soll die Aufgabe wirklich gelöscht werden?")) {
$('#'+id).remove();
}
return false;
});
});
});
});
What I want to do now is to submit these table elements to the project controller to create the project and all necessary tasks activeRecords. Is this possible?
How can I do this? Maybe create a hidden field for every table element which will be submit to the project form? But every table element consists of three values... how can I do this?
Do you have any idea?
best regards
Why not just use three hidden fields (one for each value)? Straightforward and they will be easy to access in the params hash.

Categories

Resources