Rails 4 show ajax loader on remote=true form submission - ruby-on-rails

I'm new to rails and am having trouble figuring out where in the "magic" I can handle an event to show an ajax loader when my ajaxified form is submitted, and hide the loader when my ajax response is returned and displayed to the user.
Here's what I have, where can I add a .js file or function to bind/run on click to show the loader? I know I can remove the loader in the runCMD.js file.
Controller:
def runCMD
#cmdType = params['commit'].downcase #commit is name of submit button clicked
//do some stuff, removed for brevity...
#output = //buncha stuff that happens, but returns output of the commands ran
#outputVersions = //buncha other stuff, but sets this var
respond_to do |format|
format.js { }
end
#render 'testServer'
end
View:
<%= form_tag('/runCMD', :method => 'post', :remote => true) do %>
<%= select("versions", "version", #outputVersions) %>
<%= submit_tag "Activate", class: "run-cmd-button" %>
<%= submit_tag "Stop", class: "run-cmd-button" %>
<%= submit_tag "Start", class: "run-cmd-button" %>
<%= hidden_field_tag 'component', #component %>
<% end %>
<br />
<div id="ajax_output" contenteditable="true" style="background-color:black;font-size:10pt;font-family:Courier New;overflow-y:auto;height:1000px;width:1180px;">
</div>
.js file
$('#ajax_output').html("<%= escape_javascript(render partial: 'cmdOutput', locals: { output: #output } ) %>");
Partial
<% output.each do |line| %>
<% line.gsub! '[33m', '<font color="yellow">' %>
<% line.gsub! '[32m', '<font color="green">' %>
<% line.gsub! '[31m', '<font color="red">' %>
<% line.sub! '[0m', '<font color="white">' %>
<% line.sub! '[0m', '</font>' %>
<% line.gsub! '[0m', '' %>
<%= raw(line) %>
<br />
<% end %>

you can add your ajax loader on jquery submit form event.
$( "form" ).submit(function( ) {
$('#ajax_output').html("loading...")
});
Now on ajax success your ajax_output div html will be replaced with new content.
as per your comment, you can add it in same view where u have the form
<script type="text/javascript">
$( "form" ).submit(function( ) {
$('#ajax_output').html("loading...")
});
</script>
make sure you have jquery.

Related

Remote: true not working after submit via Jquery

Im submitting my form with checkboxes via Jquery with remote true but its not working. Its keeps rendering the html format in the controller and i can work out why.
Here is my submit button and form:
<%= button_to "Read sel", root_path, class: "btn btn-info btn-sm padlr", id: "notification-submit" %>
<%= form_tag read_selected_notifications_path, :authenticity_token => true, remote: true ,id: "notification-form" do %>
<% #notifications.each do |x| %>
<div class="notification-body flerowspb">
<div class="flerowspb" style="width:80%">
<div class="notif-check flecolcen">
<%= check_box_tag "read_notif[]", value="#{x.id}" %>
</div>
<div class="notif-details ">
<div class="notif-time flerowspb">
<% if !x.viewed %>
<span class="glow"> New <%= x.notification_type.capitalize %></span>
<% else %>
<span><span class="text-grey"> New <%= x.notification_type.capitalize %></span></span>
<% end %>
<span class="text-grey font-small"> <%= time_ago_in_words(x.created_at) %> ago</span>
</div>
<div class="notif-body font-medium text-grey">
<span class="text-blue"><%= link_to x.sender.username, profile_path(x.sender) %> </span> <%= x.title %> <br>
Click <span class="text-blue"><%= link_to "Here", entry_path(x.entry_id, notification_id: x.id) %> </span> to view it
</div>
</div>
</div>
<div class="notif-image">
<%= image_tag x.entry.image.small_thumb %>
</div>
</div>
<% end %>
<% end %>
So there is no actual submit button for the form as i want the button above the form not below it like this,
I understand i have to add :authenticity_token => true as rails doesnt add this to form_tag with remote: true
Here is my controller:
def read_selected
Notification.read_selected(current_user.id, params[:read_notif])
respond_to do |format|
format.html{
flash[:success] = "Selected notifications marked as read"
redirect_to notifications_path
}
format.js{
render 'notifications/jserb/read_selected'
}
end
end
Here is the js code:
$(document).on('turbolinks:load', function(){
if (window.location.href.match(/\/notifications/)) {
$('#notification-submit').on('click', function(e){
console.log('oisdjfilds');
e.preventDefault();
$('#notification-form').submit();
});
}
});
EDIT: I added in a submit button at the bottom of the form and it works as it should do, So it something to do with the way i'm submitting the form
I assume you're using Rails 5, since there is such an inconsistency in its behavior. Please, check out the issue
As far as I understand the quick solution for your problem is going to be:
Get the form HTML element:
form = document.getElementById('notification-form');
or
form = $('#notification-form')[0]
And then fire Rails submit:
Rails.fire(form, 'submit');
or
form.dispatchEvent(new Event('submit', {bubbles: true}));

Ajax not working inside iteration

I have a AtpRank model containing the first 100 Atp tennis players.
My goal is to create in the view a table listing all tennis players and their attributes, along with a button for each player useful for the user to choose a list of tennis players. The home.html.erb code is below:
<% #atp_ranks.each do |tennis_player| %>
<tr id="tennist-<%= tennis_player.ranking %>">
<td class="atpranking"> <%= tennis_player.ranking %> </td>
<td class="atpname"> <%= tennis_player.name %> </td>
<td class="atppoints"> <%= tennis_player.points %> </td>
<% unless Time.now.month == 12 %>
<td>
<div id="atpenlist_form">
<% if current_user.atpenlisted?(tennis_player) %>
<%= form_for(current_user.atp_selections.find_by(atp_rank_id: tennis_player.id),
html: { method: :delete }, remote: true) do |f| %>
<%= f.submit "Dump", class: "btn btn-warning btn-sm" %>
<% end %>
<% else %>
<%= form_for(current_user.atp_selections.build, remote: true) do |f| %>
<div><%= hidden_field_tag :atp_id, tennis_player.id %></div>
<%= f.submit "Choose", class: "btn btn-primary btn-sm" %>
<% end %>
<% end %>
</div>
</td>
<% end %>
</tr>
<% end %>
As you can see, the form uses Ajax having set remote: true in the form_for helper.
Requests are handled by the atp_selections controller. Below is an extract of the create action of this controller:
current_user.atpenlist(tennist)
respond_to do |format|
format.html { redirect_to root_url }
format.js
end
The destroy action uses the atpdiscard method instead of the atpenlist method.
In app/views/atp_selections I created the create.js.erb and destroy.js.erb files.
Below is the app/views/atp_selections/create.js.erb file:
$("#atpenlist_form").html("<%= escape_javascript(render('users/atpdiscard')) %>");
$("#atp_count").html('<%= current_user.atp_ranks.count %>');
Each of the app/view/users/_atpenlist.html.erb and app/view/users/_atpdiscard.html.erb partials contain the respective form (the same exact part of the code above starting with form_for).
I have to say that in the original code for the home page I did not explicitly included the entire code for the forms, but I just rendered the partials. This did not work: rails warned me that it could not find the variable or method tennis_player used in the iteration, for some reason to me unknown. So I had to renounce to render the partials and decided to include the entire code.
The issue is now that Ajax does not work: I have to refresh the page to see the results of submitting the form. I checked my code and could not find errors or explanation for this.
tennis_player is a local variable for you home.html.erb .
So you need to pass it in js.erb like
$("#atpenlist_form").html("<%= escape_javascript(render('users/atpdiscard'), locals: {tennis_player: your_tennis_player_object}) %>");
Set the tennis player object in your controller & use that in above js.erb

Changing data stored in a div using ajax and ruby on rails

Does anyone know a good guide or example for me to follow on how to change data on the screen using ajax?
I have a div that stores the last #event_status and every 5 minutes I want to go get the last event status created and display it in the div using ajax.
<div id="last_status">
Last Status:
<% if #event_status.present? %>
<span id="last_status_content">[<%= #event_status.user.initials %> # <%= #event_status.created_at.strftime("%I:%M%p") %>]<br />
Audio: <%= #event_status.audio ? 'Good' : 'None' %> |
Video: <%= #event_status.video ? 'Good' : 'None' %> |
<% if #event_status.notes.present? %>
Notes: <%= #event_status.notes %>
<% else %>
Notes: No Notes
<% end %>
</span>
<% else %>
<span id="last_status_content">None</span>
<% end %>
</div>
You can use javascript to periodically call an action and place the contents into a div. First extract the content of your last_status div to a partial, then return said partial from a controller action.
Javascript in your page:
$(document).ready(
function() {
setInterval(function() {
$('.last_status').load('/controller_name/action_name');
}, 36000);
}
);
Your controller action needs to then initialize all variables used by the partial, then render the partial.
def my_action
#event_status = get_latest_event
render :partial => 'my_partial'
end

Rails: 'Load More' button with Ajax & Kaminari

I would like to create a "load more" ajax pagination, with Kaminari.
I'm using this code :
class BienvenueController < ApplicationController
def index
#articles = Admin::Article.page(1).per(2)
respond_to do |format|
format.html
format.js
end
end
end
# Bienvenue#index
<div class="container" style="min-width:<%= #width %>px">
<%= render "shared/articles" %>
<%= link_to_next_page #articles, 'Load More', :remote => true, :id=>"load_more_link" %>
# Shared/articles
<% #articles.each do |a| %>
<article class="<%= a.rubrique.color %>">
<div class="sharing">
<%= image_tag "facebook-32.png" %>
</div>
<p class="color<%= a.rubrique.color %>"><i>Le <%= I18n.localize(a.created_at, :format => :long) %> par David Perrotin</i></p>
<h1><%= a.titre %></h1>
<div class="excerpt">
<%= a.chapo.html_safe %>
</div>
<div class="image">
<%= image_tag a.mainphoto.url(:medium), :width=>"100%" %>
</div>
<div class="contenu">
<%= a.contenu.html_safe %>
</div>
<div class="readmore">
<%= link_to "Continuer la lecture", article_path(a) %>
</div>
</article>
<% end %>
# index.js.erb
$('.container').append("<%= escape_javascript(render 'shared/articles')%>");
$('#load_more_link').replaceWith("<%= escape_javascript(link_to_next_page(#articles, 'Load More', :remote => true, :id=>'load_more_link'))%>");
But the problem is that when I click on "Load More", it always shows the two same articles, the partial is never refreshed with two more articles, like I would like.
I just ran into an issue with this that might help others. Depending on your version of jQuery, don't use replaceWith on the #load_more_link in index.js.erb.
There is a regression (http://bugs.jquery.com/ticket/13401) that an empty replaceWith does nothing, so on the very last page of your set, the link_to_next will be empty, making the line: $('#load_more_link').replaceWith(''); and thus will not replace the last "more" button, so you'll continually load the last page of your data set.
Fixed by updating jQuery version or use empty().html('...') instead of replaceWith.

Rails Modal Form - Can't Get Validation Errors to Display in Modal

EDIT: I forgot to mention I am using validation at the model level, and that works fine. So the validations are preventing the modal form from being submitted (and ajax:error is getting called), but I'm having no luck dealing with the resulting errors object and properly displaying the errors. For now I'm just using placeholder text ("FORM HAS ERRORS"). Again, validations and displaying errors are working fine with my non-modal (non-ajax) forms (where I'm using the error messages partial). I really wish I could just render that partial in my modal dialog box, which you would think would be simple.
I'm pulling my hair out over this.
I have an "add tour" form, and within that form you can select buildings from the database to add to the tour (I'm using jquery tokeninput to search for and select buildings). All that works great.
I added the ability for the user to add a new building by providing a "Add Building" link, which brings up a modal form. I process the results and everything works great (building gets saved, modal gets dismissed, token gets added, etc).
Everything works great except validation in the modal form . . . which doesn't work at all. I've tried client_side_validations, I've tried writing coffee script to iterate through the error object returned by the controller, etc.
Anyway, I've tried everything I know to try, so now I'm coming to you guys for help. For now I just have placeholder error-handling code in the coffeescript file (that just displays "FORM HAS ERRORS" in a very rudimentary way). I took out all my previous attempts at making this work because it was getting ugly, and I'm really just looking for the best way to do this.
Here are the pertinent files.
building.js.coffee
$ ()->
$("form.new_building").on "ajax:success", (event, data, status, xhr) ->
$("form.new_building")[0].reset()
$('#new-building-modal').modal('hide')
fulladdress = "#{data.address} (#{data.name}, #{data.city}, #{data.zip})"
$('#tour_building_tokens').tokenInput("add", {id: data.id, address: fulladdress} )
$("form.new_building").on "ajax:error", (event, xhr, status, error) ->
$('#display_errors').append('<font color="red"><strong>FORM HAS ERRORS</strong></font><br><br>')
$('#display_errors').show()
buildings_controller.rb
....
def create
#building = Building.new(params[:building])
respond_to do |format|
if #building.save
format.html { redirect_to #building, notice: 'Building Created!' }
format.json { render json: #building, status: :created, location: #building }
else
format.html { render 'new' }
format.json { render json: #building.errors, status: :unprocessable_entity }
end
end
end
new.html.erb
<% provide(:title, 'Add Tour') %>
<h1>Add Tour</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#tour) do |f| %>
<%= render 'fields', f: f %>
<%= link_to 'Add Building', '#new-building-modal', 'data-toggle' => "modal" %>
</br>
</br>
<%= f.submit "Add Tour", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
<div id='new-building-modal' class='modal hide fade'>
<div class = "modal-body">
<%= form_for(Building.new, remote:true, html: {"data-type" => :json}) do |f| %>
<div id="display_errors" style="display:none;">
</div>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :address %>
<%= f.text_field :address %>
<%= f.label :city %>
<%= f.text_field :city %>
<%= f.label :zip %>
<%= f.text_field :zip %>
</div>
<div class = "modal-footer">
<%= f.submit "Add Building", class: "btn btn-large btn-primary" %>
</div>
<% end %>
</div>
Oh, and I also have a shared error messages partial that I'm not using with this modal right now (because I couldn't get the modal to "refresh" to display the errors). In a perfect world I'd use this same partial with the modal, because it works great with my other non-modal forms.
Here is the partial (again, not rendering this currently in the above code).
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-error">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<% if msg != "Password digest can't be blank" %>
<li>* <%= msg %></li>
<% end %>
<% end %>
</ul>
</div>
Thanks in advance for any help anyone can provide. I need as much detail as possible. I've researched related questions on Stack Overflow, and every time I think I'm close, I come up empty.
Thanks to muttonlamb for pointing me in the right direction. I was 50% sure it had to do something with parsing the JSON, and he convinced me to stay on that path. Ironically it was this SO question that helped me get to the final answer. The answer to that question, which involved printing the error to the console, was what I was looking for. It turns out that it wasn't so much that I was parsing the error wrong . . . I was parsing the wrong object. Here is the final implementation that works . . .
new coffeescript (notice how I show, hide, and clear the div as necessary):
$ ()->
$("form.new_building").on "ajax:success", (event, data, status, xhr) ->
$("form.new_building")[0].reset()
$('#new-building-modal').modal('hide')
fulladdress = "#{data.address} (#{data.name}, #{data.city}, #{data.zip})"
$('#tour_building_tokens').tokenInput("add", {id: data.id, address: fulladdress} )
$('#error_explanation').hide()
$("form.new_building").on "ajax:error", (event, xhr, status, error) ->
errors = jQuery.parseJSON(xhr.responseText)
errorcount = errors.length
$('#error_explanation').empty()
if errorcount > 1
$('#error_explanation').append('<div class="alert alert-error">The form contains ' + errorcount + ' errors.</div>')
else
$('#error_explanation').append('<div class="alert alert-error">The form contains 1 error</div>')
$('#error_explanation').append('<ul>')
for e in errors
$('#error_explanation').append('<li>' + e + '</li>')
$('#error_explanation').append('</ul>')
$('#error_explanation').show()
new view:
<% provide(:title, 'Add Tour') %>
<h1>Add Tour</h1>
<div class="row">
<div class="span6 offset3">
<%= form_for(#tour) do |f| %>
<%= render 'fields', f: f %>
<%= link_to 'New Building', '#new-building-modal', 'data-toggle' => "modal" %>
</br>
</br>
<%= f.submit "Add Tour", class: "btn btn-large btn-primary" %>
<% end %>
</div>
</div>
<div id='new-building-modal' class='modal hide fade'>
<div class = "modal-header">
<div id="error_explanation" style="display:none;">
</div>
</div>
<div class = "modal-body">
<%= form_for(Building.new, remote:true, html: {"data-type" => :json}) do |f| %>
<%= f.label :name %>
<%= f.text_field :name %>
<%= f.label :address %>
<%= f.text_field :address %>
<%= f.label :city %>
<%= f.text_field :city %>
<%= f.label :zip %>
<%= f.text_field :zip %>
</div>
<div class = "modal-footer">
<%= f.submit "Add Building", class: "btn btn-large btn-primary" %>
</div>
<% end %>
</div>
new controller:
def create
#building = Building.new(params[:building])
respond_to do |format|
if #building.save
format.html { redirect_to #building, notice: 'Building Created!' }
format.json { render json: #building, status: :created, location: #building }
else
format.html { render 'new' }
format.json { render json: #building.errors.full_messages, status: :unprocessable_entity }
end
end
end
Finally, to keep the modal from scrolling when the errors are added (I wanted it to auto-resize instead), I added this CSS:
#new-building-modal {
max-height: 600px;
}
Hope all these details prevent someone else from having to waste nearly a week on silly modal validation errors.
The issue looks like your ajax request error section does not run.
The reason is this, the error you're looking for would be something like 'page not found' or some other HTTP error.
In your controller, the HTTP request will still be successful, i.e. it will still return data.
Your logic for handling whether errors are present should be in the ajax success block.
Hope this makes sense
$ ()->
$("form.new_building").on "ajax:success", (event, data, status, xhr) ->
$("form.new_building")[0].reset()
$('#new-building-modal').modal('hide')
fulladdress = "#{data.address} (#{data.name}, #{data.city}, #{data.zip})"
$('#tour_building_tokens').tokenInput("add", {id: data.id, address: fulladdress} )
You need to be checking for the error variable in this section.

Resources