Can you tell me please, how I could remove object before destroy method will be end. When I use next pattern, removing object happen when photo was delete, but it take 1 or 3 or more seconds.
_form(edit action)
<% listing.photos.each do |photo|%>
<%= image_tag photo.image.thumb, class: 'thumbnail', id: "test"%>
<%= link_to "remove", photo_path(photo),class: 'btn btn-primary', method: :delete, data: { confirm: 'Are you sure?' }, remote: true %>
destroy.js.erb
$('#test').remove();
How I can use this pattern
_form:
<div id="test_<%= photo.id %>">
<%= image_tag photo.image.thumb, class: 'thumbnail'%>
<%= link_to "remove", photo_path(photo),class: 'btn btn-primary', method: :delete, data: { confirm: 'Are you sure?' }, remote: true %>
Destroy.js.erb:
$('#edit_image_<%= #photo.id %>').remove();
If you want to remove the image from the DOM before its real destroying on server to avoid delay you can apply event.preventDefault() on the 'remove' button click.
This will allow you to rewrite the normal behavior of the 'remove' button.
Take a look at this example about performing some UI actions before the original event and then triggering it.
Also please notice that it's not a good idea to remove something from UI without being sure it's already removed in general. It's not clear enough for the user. So, maybe it would be better to hide the image at first and in case there will be a server error while destroying it you'll show it again and display some instructive message also.
UPD
Considering following markup
<div id="test_<%= photo.id %>">
<%= image_tag photo.image.thumb, class: 'thumbnail' %>
<%= link_to "remove", "#", class: 'remove btn btn-primary', data: { id: photo.id, url: photo_path(photo) } %>
</div>
another option is to rewrite remote: true with separate jQuery.ajax() call.
$('.btn.remove').click(function () {
var $target = $(this).parents('#test_' + $(this).data('id'));
$.ajax({
url: $(this).data('url'),
method: 'DELETE',
beforeSend: function() {
$target.hide() # hiding corresponding image
},
error: function () {
$target.show() # revert hiding on error
console.log("Sorry! Couldn't remove image.") # log message
}
})
})
There is a much cleaner way to do it without a js.erb template:
<div class="photo">
<%= image_tag photo.image.thumb, class: 'thumbnail'%>
<%= link_to "remove", photo_path(photo),class: 'destroy btn btn-primary', method: :delete, data: { remote: true, type: 'json', confirm: 'Are you sure?' } %>
<div>
Now just setup an ajax handler:
// app/assets/javascripts/photos.js
$(document).on('ajax:success', '.photo .destroy.btn', function(){
$(this).parent('.photo').remove();
});
And set your controller to return the correct response code.
class PhotosController
def destroy
#photo = Photo.find(params[:id])
#photo.destroy!
respond_to do |format|
format.json do
head :no_content
end
end
end
end
This keeps your client side logic in app/assets/javascripts where it can be cached and minified instead of spreading it around in a bunch of glorified script tags.
Related
I want to add confirmation modal when bank manager has to delete bank_employee without clients bank_employee.users = nil. If bank_employee has clients I want to render different modal - destroy_confirmation_modal. How to do it in a proper way? Where should I put if condition?
code snipped of
edit.html.erb
<div class="row">
<div class="col-sm-12 col-md-12 col-xs-12 text-center bank-employee__button-wrapper bank-employees-users-registration__registrations-submit--wrapper">
<%= t('.delete') %>
<%= f.submit t('.submit'), id: "formSubmit", class: "bank-employee__button bank-employee__button-submit"%>
</div>
</div>
<% end %> // this `end` comes from `form_for`
<%= button_to "", bank_employee_path(#bank_employee), method: :delete, form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy" %>
<div class="row">
<div class="col-xs-12 col-sm-12 col-md-12 text-center">
<%= link_to bank_employees_path do %>
<span class="bank-employee__back-button">
<%= image_tag "icon_back.svg", alt: "Back icon", class: ""%>
<%= t('.back') %>
</span>
<% end %>
</div>
</div>
</div>
<%= render "destroy_confirmation_modal" %>
I don't think I should update my controller method but maybe I'm wrong?
controller.rb
def destroy
authorize current_bank_employee
#bank_employee = find_bank_employee(params[:id])
if #bank_employee.users.any? && associated_bank_employees.present?
reassign_users_and_notify_bank_employee
elsif #bank_employee.users.any?
render :edit
else
#bank_employee.destroy
render :destroy_notice, locals: { old_bank_employee: #bank_employee, assigned: false }
end
end
EDIT
my routes.rb
resources :bank_employees, except: [:show], concerns: [:with_datatable] do
member do
get :confirm
end
end
rails routes showed me this path as confirm_bank_employee so I've changed if condition as follow
<% if #bank_employee.users.empty? %>
<%= button_to "", confirm_bank_employee_path(#bank_employee), form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy", remote: true %>
<% else %>
<%= button_to "", bank_employee_path(#bank_employee), method: :delete, form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy" %>
<% end %>
You need a different approach.
<% if #bank_employee.clients.empty? %>
<%= button_to "", bank_employee_confirm_path(#bank_employee), form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy", remote: true %>
<% else %>
<%= button_to "", bank_employee_path(#bank_employee), method: :delete, form: {id: "bankEmployeeDestroyForm" }, class: "bank-employee__button-destroy" %>
<% end %>
now you need two things:
inside routes.rb create a collection for your blank_employee_confirm_path:
whatever setup you have, i assume you have some kind of blank_employees resources path:
resources :blank_employees do
member do
get :confirm
end
end
now, inside your controller you need to add the method confirm:
def confirm
## do whatever you want do to here
render :js
end
this will then head over to confirm.js
create a confirm.js.erb file inside your blank_employees view folder. To confirm this is working, you can add a console.log('it works') in it.
Once you have confirmed that it is working you can add the javascript code to the confirm.js.erb file:
$('#modal-body').html('<%= escape_javascript(render :partial => 'shared/your_confirmation_modal'%>');
with this setup, you need in your edit.html.erb file a <div id="modal-body"></div> that will take the modal. Also notice that in my example the confirmation modal is stored in "views/shared/_your_confirmation_modal.html". Change the path to your setup or create the exact path in order to make this work!
Notice that the "confirm" path is for the blank_employees that have no clients. The button will be only rendered when there is no client. All the other logic you had before for the blank_employees with clients stay the same. You don't need to change there anything. If you had any logic inside there for blank_employees without any clients, move the code to the confirm method.
One more thing: Make sure to add to your destroy method a render :js as well, and inside destroy.js.erb add the same kind of logic like inside confirm.js.erb, beside that you want to render the modal for blank_employees with clients. Your destroy.js.erb file should look something like this:
$('#modal-destroy').html('<%= escape_javascript(render :partial => 'shared/your_destroy_modal'%>');
Very important: Just like with the first modal, add a <div id="modal-destroy"></div> to your edit.html.erb file, otherwise it wont render the modal.
If something is unclear, let me know!
Greetings!
I've got a web page that creates an "Activity." Using simple form, the user fills in some basic information about the activity:
#new.html.erb
<%= simple_form_for #activity do |f| %>
<%= f.input :name, label: "Activity name", input_html: { id: "create_activity_name_field" }, hint: "At least 7 characters" %>
<%= f.input :type, label: "Activity type", input_html: { id: "create_activity_type_field" }, as: :select, :include_blank => false, collection: Activity.types.keys.to_a %>
<%= f.input :date_field, input_html: { id: "create_activity_date_field" }, as: :string %>
<%= f.input :time_field, input_html: { id: "create_activity_time_field" }, as: :string %>
<% f.button :submit, :class => "btn btn-primary" -%>
<% end %>
However, I am also using MapBox to display a map. The user will click the points on the map to make a route for the activity. When the user is done drawing the route, I can then extract GeoJSON data for this route.
I would like to use javascript to POST the GeoJSON data with the form data. On the backend, I'll have rails convert the GeoJSON to a KML file, and use Paperclip to upload it to Amazon S3. The rest of the data can be saved, and Paperclip will give me a URL to the KML file which I can associate with the activity.
I'm a Rails noob, and I can't figure out how to do this nor track down anything to get me over this hurdle. I considered using javascript's FormData. I was very attracted to this approach, because implementation looks so simple, but apparently it can only really handle key/value pairs, not complex nested JSON data.
Any suggestions or strategies greatly appreciated. And greatly appreciated++ if anyone can give detailed answers because, like I said, I'm a few weeks fresh to rails and web development.
What you probably want to do here is to prevent the default behavior of your form using in JS :
$('form').submit(function(e) {
e.preventDefault();
var form = $('form');
var form_data_json = JSON.stringify(form.serializeArray());
form_plus_geojson = form_data_json.concat(Geojson);
Grab your data in JS ( this should be easy but you dont really say how do you extract the data from mapbox ).
Then send back the data via AJAX ( don't forget validation on either end )
if (my_data_is_not_good/empty/whatever) {
console.log('nop')
} else {
$.ajax({
type: 'post',
url: Yourcontrollerpostaction,
data: form_plus_geojson,
dataType: "JSON",
Once the data has been sent you want to resume the normal form submission post behavior and submit the form.
complete: function() {
this.off('submit');
this.submit();
}
});
}
});
Then you just need to parse the data in your controler.
def post_controller
if request.xhr?
my_hash = JSON.parse(params[:mydata])
else
normal post behavior
end
end
Everything is quick pseudocode you might have to fix some stuff as i haven't tested it but it should work.
Obviously now that we are doing everything in ajax you don't need the complet function call back and can remove that part.
Kudos to #jDay for pointing me in the right direction -- specifically with concatenating the JSON data. However, I couldn't get his specific solution to work on my app, and I went trying another method which, in my view, was a little more straightforward.
I used the Rails simple_form_for gem to make the form, specifying a remote post call:
#new.html.erb
<%= simple_form_for #activity, :url => '/activities', :method => :post, :remote => true, html: {id: :activity_create_form, "data-type" => :json} do |f| %>
<%= f.input :name, label: "Activity name", input_html: { id: "create_activity_name_field" }, hint: "At least 7 characters" %>
<%= f.input :type, label: "Activity type", input_html: { id: "create_activity_type_field" }, as: :select, :include_blank => false, collection: Activity.types.keys.to_a %>
<%= f.input :date_field, input_html: { id: "create_activity_date_field" }, as: :string %>
<%= f.input :time_field, input_html: { id: "create_activity_time_field" }, as: :string %>
<%= f.button :submit, :class => "btn btn-primary", input_html: {id: "create_activity_submit_btn"} -%>
<% end %>
Then I used jQuery to to hijack the form submit button and append an invisible form element bearing the GeoJSON data. The data, however, had to be stringified so it could be sent:
#activity_new.js
$("form").submit( function (e) {
e.preventDefault();
$('<input />')
.attr('type', 'hidden')
.attr('name', 'routeGeoJson')
.attr('value', JSON.stringify(polyline.toGeoJSON()))
.appendTo(this);
return true;
});
(Here, "polyline" is a Leaflet L.polyline object that I used to draw on the map.)
In the controller, you'll get params like this:
{"utf8"=>"✓", "activity"=>{"name"=>"A jaunt in the woods", "type"=>"walk",
"date_field"=>"2015-09-08", "time_field"=>"07:00"},
"routeGeoJson"=>"{\"type\":\"Feature\",\"properties\":{},\"geometry\":{\"type\":\"LineString\",\"coordinates\":[[-118.38855654001236,33.95361274499209],[-118.36624056100845,33.97681937760982]]}}",
"commit"=>"Create Activity", "controller"=>"activities",
"action"=>"create"}
Since the "routeGeoJson" data is still in string form, I reassembled the GeoJSON data by parsing it using the JSON gem (which I believe is included by default in Rails 4):
def create
geojson = JSON.parse(params[:routeGeoJson])
# do something...
end
And you get your GeoJSON hash:
{"type"=>"Feature", "properties"=>{},
"geometry"=>{"type"=>"LineString",
"coordinates"=>[[-118.42014223337173, 33.98407904797006],
[-118.37825685739517, 33.956175751601826]]}}
I'm using Devise with Acts As Follower. The setup is simple where users can follow celebrities. What I'd like to do is only allow signed in users to follow/unfollow celebrities. If a non-registered user clicks the follow button I'd like them to be redirected to the registration screen. I'm aware of Devise's before_action :authenticate_user! but having a hard time figuring out how to trigger this as a clickable event for my follow/unfollow actions in my celebrities controller.
Where my current code falls short is that it only works if the current_user is signed in. If not a receive the error: undefined method 'following?' for nil:NilClass. I'd like to know know how I could fix this and if there's anything that could be improved as my rails code and javascript's toggle functionality.
Celebrity Show View
<% if current_user.following?(#celebrity) %>
<%= link_to "Unfollow", unfollow_celebrity_path, method: :post, class: "unfollow", remote: true %>
<% else %>
<%= link_to "Follow", follow_celebrity_path, method: :post, class: "follow", remote: true %>
<% end %>
<%= render _toggle.html.erb %>
Toggle Button Partial
<script>
$(function() {
$('a').click(function() {
if ($(this).text() == "Unfollow") {
$(this).html("<%= escape_javascript( link_to 'Follow', follow_celebrity_path, method: :post, remote: true) %>").addClass('follow')
} else {
$(this).html("<%= escape_javascript( link_to 'Unfollow', unfollow_celebrity_path, method: :post, remote: true) %>").addClass('unfollow')
}
});
});
</script>
Celebrities Controller
...
def follow
#celebrity = Celebrity.find(params[:id])
if current_user
current_user.follow(#celebrity)
end
end
def unfollow
#celebrity = Celebrity.find(params[:id])
if current_user
current_user.stop_following(#celebrity)
end
end
Routes
resources :celebrities do
member do
post :follow
post :unfollow
end
end
This should fix it (Update your if condition):
<% if current_user.present? && current_user.following?(#celebrity) %>
Regarding User login redirect, this should work(sorry I don't have time to test):
<script>
$(function() {
$('a').click(function() {
if ($(this).text() == "Unfollow") {
$(this).html("<%= escape_javascript( link_to 'Follow', follow_artist_path, method: :post, remote: true) %>").addClass('follow').removeClass('unfollow');
} else {
$(this).html("<%= escape_javascript( link_to 'Unfollow', unfollow_artist_path, method: :post, remote: true) %>").addClass('unfollow').removeClass('follow');
}
})
$(".follow,.unfollow").bind("ajax:error", function(data, xhr, status, error) {
if (xhr.status == 401) {
window.location.replace("<%= new_user_session_path %>");
}
});
});
</script>
I am currently working with the Acts_As_Votable gem in Ruby to use a Like link on each of my posts so that users can vote. I found a great answer on here that helped be do exactly that but I'm trying to figure out how I can use a heart icon image instead of the Like/Dislike text. I want the image to update without refreshing, just like the text links do but can't figure out the logic. Here is the current code I am using:
controllers/prides_controller.rb
def like
#pride = Pride.find(params[:id])
#pride.liked_by current_user
if request.xhr?
render json: { count: #pride.get_likes.size, id: params[:id] }
else
redirect_to #pride
end
end
def dislike
#pride = Pride.find(params[:id])
#pride.disliked_by current_user
if request.xhr?
render json: { count: #pride.get_likes.size, id: params[:id] }
else
redirect_to #pride
end
end
prides/show.html.erb
<div class="single-heart-this">
<% if current_user.liked? #pride %>
<span class="heart-icon-loved">
<%= link_to "Dislike", dislike_pride_path(#pride), class: 'vote', method: :put, remote: true, data: { toggle_text: 'Like', toggle_href: like_pride_path(#pride), id: #pride.id } %></span>
<% else %>
<span class="heart-icon">
<%= link_to "Like", like_pride_path(#pride), class: 'vote', method: :put, remote: true, data: { toggle_text: 'Dislike', toggle_href: dislike_pride_path(#pride), id: #pride.id } %></span>
<% end %>
<span class="heart-no" data-id="<%= #pride.id %>"><%= #pride.get_likes.size %></span>
<% end %>
</div>
javascript/pride.js.erb
# Rails creates this event, when the link_to(remote: true)
# successfully executes
$(document).on 'ajax:success', 'a.vote', (status,data,xhr)->
# the `data` parameter is the decoded JSON object
$(".heart-no[data-id=#{data.id}]").text data.count
return
javascript/prides.js.coffee
# Place all the behaviors and hooks related to the matching controller here.
# All this logic will automatically be available in application.js.
# You can use CoffeeScript in this file: http://coffeescript.org/
$(document).on 'ajax:success', 'a.vote', (status,data,xhr)->
# update counter
$(".heart-no[data-id=#{data.id}]").text data.count
# toggle links
$("a.vote[data-id=#{data.id}]").each ->
$a = $(this)
href = $a.attr 'href'
text = '$a.text()'
$a.text($a.data('toggle-text')).attr 'href', $a.data('toggle-href')
$a.data('toggle-text', 'text').data 'toggle-href', href
return
return
Thanks for your help.
Try this?
Add: class: "Change_me" to your link
<%= link_to image_tag("Dislike.png"), dislike_pride_path(#pride), class: 'vote', method: :put, remote: true, data: { toggle_href: like_pride_path(#pride), id: #pride.id }, class: "change_me" %>
src: link_to image_tag with inner text or html in rails
In your coffeescript, change the image src
$(".change_me").attr 'src', '/like.png'
src: Rails 3.1 CoffeeScript/JQuery - How to change the source of an image in a div?
Using Ruby on Rails I'm trying to get a confirmation message to appear before the record is destroyed.
My application.html.erb file has:
<%= javascript_include_tag "application" %>`
The application.js file has:
//= require jquery
//= require jquery_ujs
This is the code in the index.html.erb file for the button
<%= button_to "Delete", admin_country, :confirm => 'Are you sure?', :method => :delete %>
This is the destroy function from the Controller:
def destroy
#admin_country = Admin::Country.find(params[:id])
#admin_country.destroy
respond_to do |format|
format.html { redirect_to admin_countries_url }
format.json { head :no_content }
end
end
Why isn't the confirmation message appearing? It deletes the record fine, it doesn't show the confirmation box though which I really would like it to.
What version of Rails are you using?
confirm: "message" is deprecated in Rails 4.
Use:
<%= button_to "Delete", admin_country, data: { confirm: 'Are you sure?'}, method: :delete %>
Have you tried with this
<%= button_to "Delete", { action: "admin_country" },
method: :delete, data: { confirm: "Are you sure?" } %>