I am trying to render a partial from a controller in rails 5 using ajax(on success) but the blank data is passed to ajax after the action renders a partial.
The Same code was working with rails4. I have also updated the responder gem too for rails5.
Controller:
respond_with do |format|
format.html do
if request.xhr?
#images = get_images
session[:prev_images] = submitted_ids
session[:prev_winner] = winner_id
#prev_images = Lentil::Image.find(submitted_ids).sort_by{ |i| i.id }
#prev_winner = session[:prev_winner]
render :partial => "/lentil/thisorthat/battle_form", :locals => { :images => #images, :prev_images => #prev_images, :prev_winner => #prev_winner }, :layout => false, :status => :created
end
end
Ajax:
$(document).on('ajax:success', function (evt, data, status, xhr) {
// Insert new images into hidden div
$('.battle-inner:eq(1)').replaceWith(data);
$('.battle-inner:eq(1)').imagesLoaded()
.done(function () {
// Remove old images
$('.battle-wrapper:eq(0)').remove();
// Div with new images moves up in DOM, display
$('.battle-wrapper:eq(0)').show();
// Hide Spinner
$('#spinner-overlay').hide();
});
_battle_form:
<div class="grid battle-inner">
<%= semantic_form_for :battle, :remote => true, :url => thisorthat_result_path do |form| %>
<% #images.each do |image| %>
<div class="battle-image-wrap grid__cell">
</div>
<% end %>
<% end %>
</div>
Is your request processing as JS?
Try replacing format.html to format.js
It was an issue with jquery-ujs which is no more a dependency for rails>5.1. rails-ujs dependency needs to be used instead.
https://blog.bigbinary.com/2017/06/20/rails-5-1-has-dropped-dependency-on-jquery-from-the-default-stack.html
//To read data from response
.on('ajax:error', function(event) {
var detail = event.detail;
var data = detail[0], status = detail[1], xhr = detail[2];
// ...
});
Related
I am trying to get follow buttons to change without refreshing the page on click. The following code works but only for the first post in the loop I am rendering in the view. The rest don't change/work.
In my view
<% #tweets.reverse.each do |tweet| %>
...
<% if current_user.id != tweet.user.id %>
<% if current_user.following?(tweet.user) %>
<%= link_to "Unfollow", relationships_path(user_id: tweet.user), data: { remote: true, type: :json, method: :delete }, :class => "follow-btn btn btn-primary" %>
<% else %>
<%= link_to "Follow", relationships_path(user_id: tweet.user), data: { remote: true, type: :json, method: :post }, :class => "follow-btn btn btn-primary" %>
<% end %>
<br>
<% end %>
<hr/>
<% end %>
<% end %>
application.js
$(document).on('ajax:success', '.follow-btn', function(event){
let $el = $(this);
let method = this.dataset.method;
if (method === 'post') {
$el.text('Unfollow');
this.dataset.method = 'delete';
} else if (method === 'delete') {
$el.text('Follow');
this.dataset.method = 'post';
}
});
How can I make it to update all the follow buttons that point to the same user in the loop?
Here is the newer code
application.js
$(document).on('ajax:success', '.follow-btn', function(event){
let $el = $(this);
let method = this.dataset.method;
if (method === 'post') {
$('.follow-btn[href="'+this.href+'"]').each(function(el){ $(el).text('Unfollow'); });
this.dataset.method = 'delete';
} else if (method === 'delete') {
$('.follow-btn[href="'+this.href+'"]').each(function(el){ $(el).text('Follow'); });
this.dataset.method = 'post';
}
});
Controller
def create
current_user.follow(#user)
respond_to do |format|
format.html
format.json { head :created }
end
end
def destroy
current_user.unfollow(#user)
respond_to do |format|
format.html
format.json { head :no_content }
end
end
Here the buttons still work but now don't change in the view. How can I get this to work?
Count
On the same view but rendered through a partial (users/_search_users.html.erb) I count like so. How can I make it so this count also updates without page refresh on button click?
<% #users.each do |user| %>
...
<td stlye="padding:0 60px 0 60px;" col width="130" align="right"><b><%= user.followers.count %> Followers</b></td>
<% end %>
I'd like to get both the button and count to update on click without refresh. I can add more code if needed.
ty
$(document).on('ajax:success', '.follow-btn', function(e) {
// the JSON fetched
let data = e.details[0];
// the method we are changing to
let method = this.dataset.method === 'post' ? 'delete' : 'post';
// lookup table for the text
let txt = {
post: 'Follow',
delete: 'Unfollow'
}[method];
// loop through elements with the same href
$(`.follow-btn[href="${this.getAttribute('href')}"]`).each(function() {
// change the attributes of the single node in the loop
this.dataset.method = method;
$(this).text(`${txt} (${data.count})`);
});
});
// This mocks the ajax call purely for the sake of this stacksnippets example.
// Do not include this in your actual implementation
$(document).on('click', 'a[data-remote]', function(e) {
window.setTimeout(function() {
let event = jQuery.Event('ajax:success');
event.details = [{ count: 5 }, 'No Content'];
$(e.target).trigger(event);
}, 25);
e.preventDefault();
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div>
<p>User 1</p>
Follow
Follow
</div>
<div>
<p>User 2</p>
Follow
Follow
</div>
You can provide counts in the JSON responses by using render json::
def create
current_user.follow(#user)
respond_to do |format|
format.html
format.json do
render json: { count: #user.followers.count },
status: :created
end
end
end
def destroy
current_user.unfollow(#user)
respond_to do |format|
format.html
format.json do
render json: { count: #user.followers.count },
status: :ok
end
end
end
I'm just guessing the association so adjust according to your models.
I have a form that renders partial 'form'.
= form_for(#booking, :as => :booking, :url => admin_bookings_path, :html => { :multipart => true }) do |f|
= render partial: "form", locals: { f: f }
Inside the partial form again another partial is rendered based on new_record?.
- if f.object.new_record?
#extra-products
= render partial: 'new_booking_extra_product', locals: { f: f }
- else
= render partial: 'extra_product', locals: { f: f }
In bookings#new, when the user selects particular car_id, I want to show associated products with it through ajax. For that I have used ajax to make a request to bookings#fetch_extra_product.
# Ajax request to fetch product
$('#booking_car_id').on('change', function(e){
var selectedCarId = $("#booking_car_id option:selected").val();
var url = "/admin/bookings/" + selectedCarId + "/fetch_extra_product";
$.ajax(url,{
type: 'GET',
success: function(msg){
},
error: function(msg) {
console.log("Error!!!");
}
});
});
# Bookings#fetch_extra_product
def fetch_extra_product
#car_id = params[:car_id] || Car.all.order('id desc').first.id
#extra_product = Car.find(#car_id).extra_products
respond_to do |format|
format.js
end
end
The fetch_extra_product.js.erb looks as follow:
$('#extra-products').html('$("<%= j render(:partial => 'new_booking_extra_product', :locals => {:f => f}) %>")');
But the form object (f) is undefined on this state. What's the best way to fetch the object or the best way to get through this problem?
You'll want to render a partial view with the associated products server-side when the Ajax request is received. Then you can send the HTML from that partial as part of your response and use the Ajax success callback to attach the partial view to the DOM however you want it. Then you can have (in your controller) code something like this:
def fetch_extra_product
# Unless you're using #car_id in a view or something, I'd just
# just use a local variable instead of an instance variable
#
car_id = params[:car_id] || Car.all.order('id desc').first.id
#extra_products = Car.find(car_id).extra_products
respond_to do |format|
format.js { render partial: 'extra_products_div', status: :ok }
end
Then your partial code is something like this:
<div class="extra-products">
<% #extra_products.each do |product| %>
<h2><%= product.name %></h2>
...
<% end %>
</div>
That way, your JavaScript can then go along the lines of:
$.ajax(url, {
type: 'GET',
success: function(msg) {
$('#foo').attach(msg);
},
error: function(msg) {
console.log('Error!!!');
}
});
One other comment: If your API is only going to get a request to this route via Ajax, you don't need the respond_to block. In that case you can just put the render partial: 'extra_products_div', status: :ok under the line where you
define #extra_products.
i need two buttons in my view but only one should use remote, so i decided to use a link and a submit button in the following way:
view
<%= form_for(#new_word , :url => {:action => "create"}) do |f| %>
... skipped code ...
<%= link_to("build word", '#', :class => "build", "data-button" => "build") %>
... skipped code ...
<div id="build_name"></div>
<%= submit_tag l(:btn_save), :name => 'btn_save' %>
<% end %>
<script type="text/javascript">
jQuery(".build").click(function(){
var form_value = jQuery('#new_nc_project').serialize();
var button = $(this).data("button");
var content = $('#nc_project_keyword_tokens').val();
if(button == "build"){
$.ajax({
type:'POST',
url: '<%= url_for :action => 'build' %>',
data: $.param({keyword_tokens: content})
});
}
});
</script>
controller
def build
#response = {resp: "text"} //just example text for testing
respond_to do |format|
format.json { render json: #response }
format.js { render js: #response }
end
end
build.js.erb
$('#build_name').append('<p><%= #response[:resp] %></p>');
If I look at my server console, I see no listing from "build.js.erb" file.
What am I doing wrong, is there a other way to make the "#respond" visible in at view?
Thanks, dot
To render build.js.erb you should do this
respond_to do |format|
format.json { render json: #response }
format.js # this will render build.js.erb by default.
end
I have a vote button, which simply displays the amount of votes and by clicking on it, a vote is automatically added. Now I would like to add an Ajax request, so that the page doesn't refresh. Unfortunately I have never used Ajax before and therefor have no idea how to use it with Rails. I tried going through a few tutorials, but nothing seems to help.
view:
<%= link_to vote_path(:the_id => Amplify.all.where(question_id: question.id)[0].id, :the_user_id => #current_user.id), :remote => true do %>
<button class="fn-col-2 fn-col-m-3 amplify">
<%= image_tag "logo-icon-white.png" %>
<p class="count"><%= Amplify.all.where(question_id: question.id)[0].users.count %></p>
</button>
<% end %>
controller:
def vote
#amplify = Amplify.find(params[:the_id])
#current_user = User.find(params[:the_user_id])
if #amplify.users.where(:id => #current_user.id).count != 0
#amplify.users.delete(#amplify.users.where(:id => #current_user.id).first)
else
#amplify.users << #current_user
end
#question = Question.where(id: #amplify.question_id)[0]
#amplify.update(:votes => #amplify.users.count)
#question.update_attributes(:votes => #amplify.votes)
redirect_to root_path
end
routes:
get '/vote' => "amplifies#vote", :as => "vote"
jQuery(document).ready(function($){
$('.amplify').on('click', function(){
var that = $(this);
$.ajax({
url: '/vote',
data: {id: 'your id'},
/**
* Response from your controller
*/
success: function(response) {
that.siblings('.count').first().text(response);
}
});
})
})
I like calling it like waldek does but with coffeescript.
However, in your example, you are using :remote => true which is the unobtrusive way
Basically you are then going into the controller where you will need a format.js
respond_to do |format|
format.html # if you are using html
format.js # this will call the .js.erb file
end
Then create a file vote.js.erb where you can then access your instance variables and write js
console.log('you are here');
I have to separate forms on my page that are similar in that I create a comment via ajax with each.
Not functioning quite right as they are both using the same create.js.erb file.
How would I separate them so that they only render the appropriate jQuery code?
Index:
Form #1
<% form_for(#snapshot_comment, :html => { :class => "snapshot_comment", :id => "snapshot_comment" } do |f| %>
Some stuff
<% end %>
<ol id="snapshot_comments_collection">
Append stuff here...
</ol>
Form #2
<% form_for(#photo_comment, :html => { :class => "section-photo-comments", :id => "section-photo-comments" } do |f| %>
Some stuff
<% end %>
<ol id="comments-collection">
Append stuff here...
</ol>
Controller:
# POST /comments
# POST /comments.json
def create
#comment = current_user.comments.build(params[:comment])
#comment.commentable.authorize(params[:auth_key])
unauthorized! unless can? :create, #comment
if #comment.save
respond_to do |format|
format.html { redirect_to(request.headers["Referer"]) }
format.js
format.json {
render :json => #comment, :status => :created, :location => #comment
}
end
else
redirect_to(request.headers["Referer"])
end
end
Create.js.erb:
$j("<%= escape_javascript(render(:partial => #comment)) %>").prependTo("#snapshot_comments_collection");
$j('#snapshot_comment')[0].reset();
$j("<%= escape_javascript(render(:partial => #comment)) %>").prependTo("#comments-collection");
$j('#section-photo-comments')[0].reset();
You should be able to just use $() but I fail to see any use of :remote => true for your forms? Do paste the log of your rails (dev) server.