Ajax call and rails - javascript

I have to make an ajax call in my application. There is 3 different tabs (I use bootstrap tabs), and each tab call a request, and the different request are displaying in different tab-content. I create a loader which is display when I make the ajax call, for UI. But I have a problem, when I have clicked on a tab, the Ajax call is processing well, but when I click an other time on the same tab, the ajax call restart.
I juste want that data be loaded only one time, at the first request.
Here is the code :
show.html.erb (link for the call)
<li role="presentation" class="<%= 'active' if #school.is_subscribed? %>">
<%= link_to "Vérifiés (#{#count_verified})", verified_rating_path(school_id: #school.id), id: 'verified_link', 'data-toggle' => 'tab' , 'data-target' => '#verifie', remote: true %>
</li>
**The loader **
<div class="rating_loader"><i class="icon-spinner icon-spin icon-large"></i> Chargement...</div>
<script>
var loader = $(".rating_loader");
loader.hide();
$(document).ajaxStart(function() {
loader.fadeIn('slow');
}).ajaxComplete(function() {
loader.hide();
});
</script>
School_controller.rb (the method)
def verified_rating
#school = School.find(params[:school_id])
#ratings = #school.ratings.desc(:created_at)
respond_to do |format|
format.js
end
end
verified_rating.js.erb
$('#verifie').html("<%= escape_javascript render(:partial => 'rating', collection: #ratings.where(:verified => true)) %>");
$('#verified_link').one('click', function(e){
e.preventDefault();
$('#verifie').addClass('active');
$('#non_verifie').removeClass('active');
$('#all_avis').removeClass('active');
});
Is there a way to kill the call after the data are loaded and keep them in the page ?
Thank !

Related

Get different html with different buttons with AJAX

I got this menu on my website
%button.dropdown-button
.current-user{onclick:'showMenu()'}
%img.current-user-image{src:current_user.picture_url}
= current_user
%i.fa.fa-bars{'aria-hidden':true}
.dropdown-content
.menu-option{onclick:'showFilters()'}
Filter Transactions
%i.fa.fa-paper-plane{'aria-hidden':true}
.transaction-filters
.filter-option
My Transactions
%i.fa.fa-square-o
%a{href:'#'}
.menu-option
Balance History
%i.fa.fa-history{'aria-hidden':true}
%a{href:destroy_user_session_path}
.menu-option
Sign Out
%i.fa.fa-sign-out{'aria-hidden':true}
And I got this timeline with transactions
.timeline-container
- #transactions.each do |transaction|
.transaction-container
.transaction-header-container
.transaction-kudos-container
= "+#{transaction.amount}"
%span.currency
₭
.transaction-avatar-container
%div
= image_tag transaction.receiver_image, class:'avatar'
.transaction-body-container
.transaction-content
%span
= "#{transaction.sender.name}:"
%span.highlighted
= "+#{transaction.amount} ₭"
%span
to
%span.highlighted
= transaction.receiver_name_feed
%span
for
%span.highlighted
= transaction.activity_name_feed
%div
-#%button#like-button
-# Like
%span.post-time-stamp
= "#{distance_of_time_in_words(DateTime.now, transaction.created_at)} ago"
= paginate #transactions
They are both rendered on my index.html.haml
So when I click the div .filter-option.sent I want to change the code change from
- #transactions.each do |transaction|
to
- #all_transactions.each do |transaction|
to filter out the transactions of the current user without reloading the page.
These variables are defined in my controller
#transactions = Transaction.order('created_at desc').page(params[:page]).per(20)
#all_transactions = Transaction.all_for_user(current_user)
With in my model the method all_for_user
def self.all_for_user(user)
Transaction.where(sender: user).or(Transaction.where(receiver: user)).order('created_at desc').page.per(20)
end
I tried a lot of things with AJAX but nothing seems to please me. Somebody can help me?
So if you'd like to replace that #transactions list with AJAX you will need to do a few things..
1) Move the #transactions block into a partial that takes a local variable.
#transactions-wrapper
= render partial: 'transactions/list', locals: {transactions: #transactions}
2) Create a link that submits to a route, that hits a controller action as #js ('data-remote': true ) ( or write a javascript function that triggers $.ajax request: https://www.w3schools.com/jquery/ajax_ajax.asp
%a.transaction-filter{href: "/transactions/#{transaction_type}", 'data-remote': true}
or ex..
%a.study{href: "/transactions/recent", 'data-remote': true}
%a.study{href: "/transactions/past", 'data-remote': true}
3) define the route
get '/transactions/:type' => 'transactions#filter'
4) re-assign the variable based on the filter, and re-render that partial with the new data in a filter.js.erb file thats in the view directory -> app/views/transactions
def filter
#filtered_transactions = Transactions.where(type: params[:type] ).order('created_at DESC')
respond_to do |format|
format.html
format.js { render :action => 'filter.js.erb', :layout => false}
end
end
$("#transactions-wrapper").html("<%= j render(:partial => 'transactions/list'', :locals => { transactions: #filtered_transactions } ) %>");
alert('Transactions have been filtered, yo')
Let me know if that makes sense! Except please don't actually javascript alert at the end ;)

How to send data from ruby controller to application javascript using AJAX?

I have an AJAX call in my application.js which sends 3 pieces of data to my events_controller#check action:
//application.js
$(document).on('click', "#check-button", function(){
...
$.ajax({
url: '/events/check',
data: {checkList: checkList , selected_date: selectedDate , length: length },
}
);
});
my check action:
#events_controller.rb
def check
checkList = params[:checkList]
selected_date = params[:selected_date]
length = params[:length]
busy_array = Array.new #this needs to be rendered in the view
...
busy_array.push(user.id) #busy_array is a list of user ids from database
end
#routes.rb
resources :events do
get :check, on: :collection
end
The view:
<button id="check-button" type="button">Check</button>
<div class = "col-md-6" id="unavailable">
<h2>Unavailable on this day:</h2>
<ol id="unavailable-list">
<li>THIS LIST SHOULD BE POPULATED BY BUSY_ARRAY</li>
</ol>
</div>
Now I need to send back data from events_controller#check to the view, but I don't know how to send busy_array to be rendered in events\new.html.erb
Thanks for you help, I am a ruby/JS beginner
busy_array = Array.new #this needs to be rendered in the view
If that has to be available in the view, you need to define an #instance variable:
def check
checkList = params[:checkList]
selected_date = params[:selected_date]
length = params[:length]
#busy_array = Array.new
...
#busy_array.push(user.id) #busy_array is a list of user ids from database
end
Each time you call a controller#action, the data/variables within the action are bound by local scope.
Outputting data in the view requires the variable to be made available across the entire instance of the class (not just the action/function). Thus #busy_array.
Fix
#app/views/events/....
<%= button_to "Check", events_check_path %>
<div class = "col-md-6" id="unavailable">
<h2>Unavailable on this day:</h2>
<ol id="unavailable-list"> </ol>
</div>
#app/assets/javascripts/application.js
$(document).on('click', "#check-button", function(e){
...
$.get('/events/check', {checkList: checkList , selected_date: selectedDate , length: length});
});
#app/controllers/events_controller.rb
class EventsController < ApplicationController
def check
#busy_array = User.joins(:event).where(event: { ... }).pluck(:id) #-> return user ids where event.x = y
respond_to do |format|
format.js #-> invokes app/views/events/check.js.erb
format.html #-> invoked when HTML request sent
end
end
end
#app/views/events/check.js.erb
<% #busy_array.each do |id| %>
$("ol#unavailable-list").append("<%=j id %>")
<% end %>
I don't know how to send busy_array to be rendered in events\new.html.erb
The variable will be available in new if it's an #instance var:
#app/views/events/new.html.erb
<%= #busy_array %>
The view is part of the instance of your EventsController class, but outside the scope of the check method. When you send a request to Rails, it uses an instance of the Controller to compile a set of HTML to return to your browser.
For example, you could sum up the EventsController flow as such:
Request > Routing > EventsController.new(request).action > Returned HTML
If you set EventsController.new(request) manually, you'd have the following:
#event = EventsController.new request
#event.check #-> "busy_array" locally scoped to this function
#event.response #-> outputs view code.... #busy_array needs to be instance var
You'll understand better if you read up about MVC:
For a real AJAX request in Ruby on Rails, you need to send response from check action in JS format.
events_controller.rb
def check
checkList = params[:checkList]
selected_date = params[:selected_date]
length = params[:length]
#events = Event.where("checklist = ? and date = ?, checkList, selected_date).limit(length.to_i)
...
respond_to do |format|
format.js
end
end
Now, in your view, instead a check.html.erb, you will have check.js.erb. In this Javascript file, you can access to your # variables and render partials or wherever.
check.js.erb
$("#unavailable-list").append('<%= escape_javascript( render :partial => "events/event", :collection => #events ) %>');
_event.html.erb
<li><%= event.name %></li>
I change busy_array for events array because I don't know what's exactly busy_array but you can change it.
This is the flow for an AJAX request.

Acts As Votable Ajax Vote Size Not Updating Rails

I am using the acts as votable gem on my rails 4 app.
I have finally gotten it to work using ajax, so the page does not refresh when a user votes, however, the vote.size doesn't update until the page is refreshed.
Here is my controller action:
def upvote
#article = Article.find(params[:article_id])
#subarticle = #article.subarticles.find(params[:id])
session[:voting_id] = request.remote_ip
voter = Session.find_or_create_by(ip: session[:voting_id])
voter.likes #subarticle
respond_to do |format|
format.html {redirect_to :back }
format.json { render json: { count: #subarticle.get_upvotes.size } }
end
end
and view:
<%= link_to like_article_subarticle_path(#article, subarticle), class: "vote", method: :put, remote: true, data: { type: :json } do %>
<button type="button" class="btn btn-success btn-lg" aria-label="Left Align" style="margin-right:5px">
<span class="glyphicon glyphicon-thumbs-up" aria-hidden="true"></span> Helpful
</button><span class="badge" style="margin-right:10px"><%= subarticle.get_upvotes.size %></span>
<% end %>
<script>
$('.vote')
.on('ajax:send', function () { $(this).addClass('loading'); })
.on('ajax:complete', function () { $(this).removeClass('loading'); })
.on('ajax:error', function () { $(this).after('<div class="error">There was an issue.</div>'); })
.on('ajax:success', function(e, data, status, xhr) { $(this).html("Thank you for voting!"); });
</script>
As of now, when a user votes, it completely get's rid of the "helpful" button and upvote size, and displays the html"Thank you for voting".
I am having trouble figuring out how to keep the button and simply update the upvote.size to the correct number. I have tried assigning a class to the upvote size and then using something like this: $('.item-class').html(data.count) but no luck.
Any recommendations? Thank you!
$('.item-class').html(data.count)
item-class doesn't seem to be declared in your template anywhere... but even if it did, if you call it like this, it will likely match more than one thing on the page, so this replace won't work.
The reason why the button is being replaced is you are replacing the html of "this" (which is defined as the whole section marked with the class .vote)
If you want to replace just the item-class within the .vote section (ie leave the button intact) then you need to a) add something with the class of 'item-class' and b) reduce what you are replacing to that. eg:
$(this).first('.item-class').html("Thank you for voting!");
(note I've not bug-tested this - you may need to google javascript first to make sure this is the correct usage).

Getting model in Rails through ajax from another controller

Solved this:
I have a following situation:
I have Graph and Node model.
While on graph/show.html.erb I make an ajax post with an id of a Node:
var val = document.getElementById('nodeName').value;
if(val){
$.post("/nodes/this_node/", {id:val});
console.log(val);
}
How do I get that Node to render on the same page in, for example, ?
EDIT::
I have a custom POST action in node_controller :
def this_node
#node = Node.find(idd)
respond_to do |format|
format.js
end
end
I had to add app/views/nodes/this_node.js.erb with:
$("#node_div").html('<%= escape_javascript(render :partial => "node_content", :locals => { :node => #node}) %>');
and a partial in views/nodes/_node_content.html.erb
<%= #node.content %>
All works.
EDIT:
Actually fixed it (above code works)

How do you call a controller action with jQuery from your application.js file?

I'm using the jQuery autocomplete plugin, and I want to customize this event:
select: function(event, ui) {
$('.topic_field').val(ui.item.topic.name);
return false;
Essentially, it triggers callbacks when an element from the dropdown list is selected. As of now, it only adds the selected element to the text field. I want both the field to be populated and for my application to send a POST request to the video update controller action, so that the user does not need to explicitly press the button. How can I do this?
UPDATE:
Here is the form in the show view of the video controller:
<%= form_for #video, :url => {:action => "update"}, :remote => true do |f| %>
<div class="field">
<%= f.text_field :topic_names, :class => "topic_field" %>
</div>
<%= f.submit "Add Topic" %>
<% end %>
Here is my jQuery code:
var url = $('.edit_video').attr('action');
var val = ui.item.topic.name;
$.post(url, {data:val});
This is in my routes.rb:
resources :videos
resources :video_votes
resources :users
resources :profiles
resources :genres
resources :topics
resources :topicables
resource :session
Here's my update action:
def update
#video = current_user.videos.find(params[:id])
respond_to do |format|
if #video.update_attributes(params[:video])
format.html { redirect_to(#video) }
format.js
else
format.html { render :action => "edit" }
end
end
end
Check out the jQuery.post documentation on how to post the data from your select box to the relevant controller action.
I'm not familiar with the autocomplete plugin, but I presume it follows an onChange event on your select box. When the user has made a selection, you should execute the following (untested):
var url = $('myForm').attr('action');
var val = $('mySelectBox').val();
$.post(url, {data:val})
In your controller, you could access this using:
params[:data]
which would return the value of your select box. You can then use this as normal in your controller code.
use the jquery ajax function to fetch the data from your action url ("/topics/list") or something
$.post('video/update',
data: {yourdata},
function(data) {
//callback function
}
);
in your video#update be sure to return something for your js request:
def update
blah
respond_to do |format|
format.js { return somethinghere }
end
end

Categories