Rails - button_to and submit - ajax

In my Rails 3.2.3 application I'm using Ajax and jQuery. There is a button on a page. By clicking this button it must be disables, a spinner must be appeared and an ajax request begins. It's working only if I don't disable the button. If I disable it then an ajax request is not run.
So what I need to do is to be able send an ajax request, show a spinner and disable a button at the same time.
<script>
$(document).ready( function() {
$("#lnk_more").click(function() {
//if I uncomment this then an ajax request won't be be sent
// $("#lnk_more").attr('disabled','disabled');
$("#spinner").show();
});
</script>
<%= button_to "More", {:controller => 'home', :action => "test_method", :page=>#current_page }, {:remote => true,:id => 'lnk_more', :method => :get} %>
<%= image_tag('ajax-loader.gif',:style => 'display:none', :id => 'spinner') %>
#result html
<form action="/home/test_method?page=1" class="button_to" data-remote="true" method="get"><div>
Any suggestions?

Check the button_to documentation. In the last example you'll find an example with :disable_with parameter, which you can use it to disable the button after submit.

Related

Rails 6 action cable chat app not working in firefox

I made a Rails 6 app and added a private messaging feature in it using action cable. The chat works fine in chrome and edge but crashes in firefox. Upon investigation in firefox, I think whenever the 'send' button in the create message form is clicked, it seems it reloads the page, disconnecting the current subscription. It says in the terminal that the async job is performed successfully before it gets disconnected. The data (in received(data)) doesn't get console logged in the browser.
I thought I just need to prevent the page to reload once the 'send' button is clicked so I tried placing preventDefault in submit listener in both application.js and conversation_channel.js but didn't work. Also added onclick: false in the submit tag in message form but didn't work also. It's maybe my theory is wrong or maybe I executed the solution wrong.
Please tell me if you need more snippets of my code:
// this is app/javascript/channels/conversation_channel.js
import consumer from "./consumer"
document.addEventListener('turbolinks:load', () => {
const conversation_element = document.getElementById('conversation-id');
const conversation_id = Number(conversation_element.getAttribute('data-conversation-id'));
const input_box = document.getElementById('message-input-box');
const send_button = document.getElementById('send-btn');
// for terminating other subscriptions when connected to a new subscription
consumer.subscriptions.subscriptions.forEach(subs => {
consumer.subscriptions.remove(subs);
});
consumer.subscriptions.create({ channel: "ConversationChannel", conversation_id: conversation_id }, {
connected() {
// Called when the subscription is ready for use on the server
console.log('Connected to conversation id: ' + conversation_id);
send_button.addEventListener('submit', function(e) { e.preventDefault(); });
},
disconnected() {
// Called when the subscription has been terminated by the server
},
received(data) {
// Called when there's incoming data on the websocket for this channel
console.log(data);
const el_user_id = document.getElementById('user-id');
const user_id = Number(el_user_id.getAttribute('data-user-id'));
let html;
user_id === data.message.user_id ? html = data.own_message : html = data.not_own_message;
const messageContainer = document.getElementById('messages-container');
messageContainer.innerHTML += html;
send_button.disabled = false;
input_box.value = '';
}
});
});
this is where the user creates a message: app/views/messages/_form.html.erb
<%= form_with scope: :message, url: item_conversation_messages_path(#conversation.item.id, #conversation.id), local: true, remote: true do |f| %>
<div class="form-inline">
<%= f.hidden_field :conversation_id, value: #conversation.id %>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.text_field :content, placeholder: 'Type your message here...', class: 'form-control mr-2', id: 'message-input-box' %>
<%= f.submit 'Send', id: 'send-btn', class: 'btn btn-primary' %>
</div>
<% end %>
It was pointed out by user even_progression in my reddit post https://www.reddit.com/r/rails/comments/ol9ygj/rails_6_action_cable_chat_app_not_working_in/ that I should check the local and remote options in the message form. I set local to false and remote to true and now it works. Idk yet why but I'm about to find out.

How to pass parameters in axios url?

I am trying to use Axios in my Ruby Sinatra Project.
There is a url defined in my ruby file.
post '/follow/:id' do
# do something
end
In my erb file, I am trying to pass the variable in view to my inline script.
<body>
<div id="follow_element">
<button v-on:click="followUser">Follow</button>
</div>
<script>
window.addEventListener('load', function(){
const app = new Vue({
el: "#follow_element",
methods:{
followUser(){
axios.post("/follow", {
params: {
id: <%= #user.id %>
}
})
}
}
})
})
</script>
</body>
What I want is if the user click the button, the app would hit the /follow/:id url to update result. However, I met two problems.
First, the <%= #user.id %> doesn't work (#user is a variable available in this erb). The second is the app hits the '/follow' endpoint, instead of '/follow/:id' endpoint.
Could you please give me some suggestions? Thanks very much.
Note that as you wrote this:
post '/follow/:id' do
# do something
end
You'll now accept POST requests like: /follow/oneRandomId, follow/124184965 and so on...
Right here, you are making a request to /follow, passing POST params that is not in url. This will not work:
axios.post("/follow", {
params: {
id: <%= #user.id %>
}
})
In order to fix it, you can generate an URL with the available #user.id and make the POST request, like this:
axios.post("/follow/<%= #user.id %>")
Or, if you want to pass some data to the user (POST data, not URL params), you can do this:
axios.post("/follow/<%= #user.id %>", {
name: 'A beautiful name to insert to an user with that ID'
})
If you're still getting stuck, you can also use axios(config), that I think a bit clearer about what it is doing, from axios README page:
axios({
method: 'post',
url: "/follow/<%= #user.id %>",
data: {
firstName: 'Fred',
lastName: 'Flintstone'
}
});

How to add confirm message with link_to Ruby on rails

I wanted to add confirmation message on link_to function with Ruby.
= link_to 'Reset message', :action=>'reset' ,:confirm=>'Are you sure?'
Any ideas why it's not working?
I might be mistaken but you don't specify a controller along with the :action option. Have you tried the following? Assuming you have a messages resource configured in your route:
link_to 'Reset', message_path(#message), :confirm => 'Are you sure?'
EDIT: Above is deprecated. Rails 4.0 now accepts the prompt as a data attribute. See the doc here (Thanks #Ricky).
link_to 'Reset', message_path(#message), :data => {:confirm => 'Are you sure?'}
First, you should verify that your layout have jquery_ujs.
Best practice to do it by including it in your main application.js:
//= require jquery_ujs
Check that you included application.js in your layout:
= javascript_include_tag :application
While, in development mode, view your source html and verify jquery_ujs.js exists.
Run your server and verify your link tag has data-confirm value, for example:
<a href="/articles/1" data-confirm="Are you sure?" data-method="delete">
If all those steps are correct, everything should work!
Note: check this RailsCast http://railscasts.com/episodes/136-jquery-ajax-revised
Can't remember how this was done in Rails 3, but in Rails 4 you can simply:
<%= link_to 'Reset message', { controller: 'your_controller', action: 'reset' }, data: {confirm: 'Are you sure?'} %>
<%= link_to 'Reset Message', data: {confirm:"Are you sure?"} %>
remember to add the path, between 'reset message' and data
<%= link_to "Delete this article", article_path(article), method: :delete,
data: { confirm: "Are you sure you want to delete the
article?"}, class: "btn btn-xs btn-danger" %>
A button link where article_path is the prefix and (article) is passing the id which is required by the method: :delete method.
The later part of the codes adds a confirmation msg.
Try this:
= link_to 'Reset message', {:action=>'reset'}, :confirm=>'Are you sure?'
or to be more clear
= link_to('Reset message', {:action=>'reset'}, {:confirm=>'Are you sure?'})
Refer http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
You will see that there are 3 parameters, when you are giving url as options like {:action => ..., :controller => ...}
link_to(body, url_options = {}, html_options = {})
In ruby, if the last parameter in a function call is a hash, you need not wrap it in {} characters (in other words, you can omit that in case, if the hash is the last parameter), so the code you have provided will be interpreted as a function call with only 2 parameters, 'Reset message' string and {:action=>'reset', :confirm=>'Are you sure?'} hash and the :confirm=>'Are you sure?' will be interpreted as a url_option instead of a html_option
Somehow does not work those code only Safari browser So I was involved button...
<%= button_to('', delete_path(), method: "delete", data: { confirm: 'Are you sure?', disable_with: 'loading...' }) %>
Look at your javascript_include_tag and it should work fine:
<%= link_to("Reset message", :method => :reset, :class => 'action', :confirm => 'Are you sure?') %>
watch this railscasts video for better understanding.
http://railscasts.com/episodes/205-unobtrusive-javascript
rails documentation for link_to helper.
http://api.rubyonrails.org/classes/ActionView/Helpers/UrlHelper.html#method-i-link_to
First, we need to understand what Js package respond to this kind of alerts in rails application. So for this, jquery_ujs package is reponsible for showing the alerts in rails.
So you must have jquery & jquery_ujs in your application.js file.
//= require jquery
//= require jquery_ujs
Now, we need to confirm, that application.js file is included in your required layout or not. By default layout file remains in application.html.erb in layout folder of views.
<%= javascript_include_tag 'application' %>
Next the link should have data-confirm & data-method attributes as
<a href="/message/1/reset" data-method="delete" data-confirm="Are you sure?">
In erb, this can be written as,
= link_to 'Reset', message_path(#message), data: {method: 'delete', confirm: 'Are you sure?'}
This should work if everything is aligned in same fashion.

How to use inline :confirm option for html helpers with AJAX calls?

I am trying to have an AJAX implementation of record deletion associated with a button. The problem is that ajax:success event doesn't seem to be triggered in such case.
I have implemented the suggestion from this post: Rails :confirm modifier callback?) but I am uncertain if it's the preferred way.
I was wondering if communal wisdom could help in this case. What's the right approach here?
app/views/competitions/show.html.haml:
%td= button_to 'Delete', contender, remote: true, method: :delete, class: 'delete_contender', confirm: 'Are you sure?' if owns?
app/assets/javascripts/competitions.js:
$(document).ready(function() {
$('.delete_contender').on('confirm:complete', function(e, res) {
if (res) {
$(e.currentTarget).closest('tr').fadeOut();
}
});
});
app/controllers/contenders_controller.rb:
def destroy
#contender = Contender.find(params[:id])
#competition = #contender.competition
#contender.destroy
respond_to do |format|
format.js
format.html { redirect_to #competition, notice: "Contender #{#contender.sn_name} has been deleted" }
format.json { head :no_content }
end
end
The quick answer is: that is not the right approach. The long answer is below.
Instead of using .delete_contender class as an anchor for action binding, I should have used "form[data-remote]" since *button_to* helper generates a form. Also, there is no need to keep the JS hook inside the asset pipeline, it's better to move it to the views and convert to CoffeeScript. The Rails 3 style solution is:
app/views/competitions/show.html.haml:
%td= button_to 'Delete', contender, remote: true, method: :delete, confirm: 'Are you sure?' if owns?
app/views/competitions/destroy.js.coffee:
jQuery ->
$("form[data-remote]").on "ajax:success", (e, data, status, xhr) ->
$(e.currentTarget).closest('tr').fadeOut()
app/controllers/contenders_controller.rb:
respond_to :js, only: :destroy
def destroy
#contender = Contender.find(params[:id])
#competition = #contender.competition
#contender.destroy
end

When i call ajax function its working first time only in first ajax call i reload the same div from which i m calling ajax request

// main htm page from where aja x call happened
<div id ="vote_count_<%=answer.id %>" >
<%= render :partial => '/votes/count', :locals =>{ :answer => answer} %>//render a partial
<div id="wait" style=" float:center">// for loading image
</div>
</div>
// partial page :- count
<div class ="look6">
<%= image_tag("thumbs_up.jpg", :alt => "like",:value => answer.id,:id => 'like',:class => 'vote')%>// image tag for like reload during ajax call
(<%= Vote.count(answer.id,'like')%>)// no of like vote
| <%= image_tag("thumbs_down.jpg", :alt => "unlike",:value => answer.id,:id =>'unlike',:class => 'vote',:onclick=>"return vote()") %>// image tag for unlike reload during ajax call
(<%= Vote.count(answer.id,'unlike')%>)// no of unlike vote
</div>
// ajax function :-
<script type="text/javascript" >
function showLoadingImage()
{
$('#wait').append('<div id="loading-image"><%= image_tag("ajax-loader.gif", :alt=>"Loading..." )%></div>');// for ajax loading
}
in first page:
//.vote is a claas name
$(".vote").click(function(){
alert("hi");
var answer_id =$(this).attr("value");// for ans id
alert(answer_id);
showLoadingImage();// call loading image function
var result = $(this).attr("id");// whether like or unlike
$.ajax({
cache: false,
//path for controller
url: '/create_vote/'+answer_id,
data: "result="+result,// data for ajax call
complete: function(){
$('#loading-image').remove();// remove loading image
}
});
cache: false;
return false;
});
</script>
//ontroller:
def create_vote
#vote = Vote.new // create new vot
#vote.user_id = current_user.id// user id
#vote.answer_id = params[:id]// answer id
#vote.result = params[:result] == 'like' ? '1':'0'// like or unlike
answer = Answer.find(params[:id])// answer id find
if #vote.save// save vote
#message = "thanks"// message
else
#message = "sorry"// mesage
end
#vote_count = Vote.count(params[:id], params[:result])// total vote
respond_to do |format|
format.js { render '/votes/create_vote.js.erb', :locals => {:result =>params[:result],:answer =>answer}}// result return back
end
end
///votes/create_vote.js.erb
$("#vote_count_<%=#vote.answer_id %>").html("<%= escape_javascript(render :partial => '/votes/count', :locals => {:result => result ,:answer =>answer }) %>")// render js part
//_count partial
<%= image_tag("thumbs_up.jpg", :alt => "like",:value => answer.id,:id => 'like',:class => 'vote')%>
(<%= Vote.count(answer.id,'like')%>)
| <%= image_tag("thumbs_down.jpg", :alt => "unlike",:value => answer.id,:id =>'unlike',:class => 'vote',:onclick=>"return vote()") %>
(<%= Vote.count(answer.id,'unlike')%>)
use:- $(".vote").live("click", function(){
in place of : $(".vote").click(function(){

Resources