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.
Related
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];
// ...
});
I have OrdersController show action (with order_info partial), which displays current status of the order ("paid", "canceled", etc.).
meanwhile, I have callback action order_callback, which executes update action and changes status of the order in the database when it receives the callback from a payment processor.
What I want to achieve is to update show action in real-time to capture changes in order status (e.g. order paid successfully).
I tried to use unobtrusive javascript, but did not succeed.
update.js.erb
$("#order").html("<%= escape_javascript(render 'order_info') %>")
show.html.erb
<div id="order">
<%= render 'order_info' %>
</div>
orders_controller.rb
def update
if #order.update_attributes(order_params)
flash[:success] = "Order updated."
redirect_to #order
else
render 'edit'
end
end
api/orders_controller.rb
def order_callback
signature = request.headers['X-Signature']
request_uri = URI(env['REQUEST_URI']).request_uri rescue env['REQUEST_URI']
if $processor.callback_valid?(signature, request_uri)
#order = Order.find(params["id"])
#order.update_attributes(status: params["status"])
render status: :ok,
json: { success: true,
info: "Successfully updated order." }
else
render status: :unprocessable_entity,
json: { success: false }
end
end
I am using rails 4.2.2 with turbolinks enabled.
I was able to resolve it with javascript polling. The critical line was to explicitly say which .js partial to render in respond_to block
show.js.erb
$("#order").html("<%= j render 'orders/order_info', locals: {order: #order} %>");
OrderPoller.poll();
orders_controller.rb
def show
#order = Order.find(params[:id])
respond_to do |format|
format.js { render "orders/show.js.erb" }
format.html
end
end
orders.coffee
#OrderPoller =
poll: ->
setInterval #request, 5000
request: ->
$.get($('#order').data('url'))
jQuery ->
if $('#order').length > 0
OrderPoller.poll()
show.html.erb
<%= content_tag :div, id: "order", data: { url: order_path(#order) } do %>
<%= render 'order_info' %>
<% 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 recently asked this question: Javascript Form Submition?
Now I'm trying to work out why, after form submission, the create.js.erb doesn't load the latest data (the data which was submitted).
If I submit the data again, the table will update with the second last data but not the absolute latest.
...why?
EDIT:
Latest code -
Categories controller:
class Admin::CategoriesController < Admin::AdminController
...
def create
#category = Admin::Category.new(params[:admin_category])
# You can't retrieve the #categories before attempting to add new one. Keep at end of create method.
#categories = Admin::Category.all
respond_to do |format|
if #category.save
save_to_history("The category \"#{#category.name}\" has been created", current_user.id)
format.json { render json: { html: render_to_string(partial: 'categories') } }
# I've tried both the above and bellow
#format.json { render json: { html: render_to_string('categories') } }
format.html { redirect_to(admin_categories_url, :notice => 'Category was successfully created.') }
format.xml { render :xml => #category, :status => :created, :location => #category }
else
format.html { render :action => "new" }
format.xml { render :xml => #category.errors, :status => :unprocessable_entity }
end
end
end
...
end
Javascript:
<script type='text/javascript'>
$(function(){
$('#new_admin_category').on('ajax:success', function(event, data, status, xhr){
$("#dashboard_categories").html(data.html);
});
});
</script>
SOLVED
I used #PinnyM's method and added a create.json.erb file with the following code:
<% self.formats = ["html"] %>
{
"html":"<%= raw escape_javascript( render :partial => 'categories', :content_type => 'text/html') %>"
}
and changed my create method to:
def create
#category = Admin::Category.new(params[:admin_category])
respond_to do |format|
if #category.save
# You can't retrieve the #categories before attempting to add new one. Keep after save.
#categories = Admin::Category.all
save_to_history("The category \"#{#category.name}\" has been created", current_user.id)
format.json
format.html { redirect_to(admin_categories_url, :notice => 'Category was successfully created.') }
format.xml { render :xml => #category, :status => :created, :location => #category }
else
format.html { render :action => "new" }
format.xml { render :xml => #category.errors, :status => :unprocessable_entity }
end
end
end
Please do offer suggestions if this is messy.
You need to handle the success callback for your remote (AJAX) submission. The data parameter (2nd argument) holds the response:
$(function(){
$('#new_category').on('ajax:success', function(event, data, status, xhr){
eval(data);
});
});
A better way to do this (and avoid the dangerous eval) might be to just return the partial, and have the callback decide what to do with it:
# in create action
format.json { render json: { html: render_to_string('categories') } }
# in your js, run at page load
$(function(){
$('#new_category').on('ajax:success', function(event, data, status, xhr){
$("#dashboard_categories").html(data.html);
});
});
UPDATE
Just noticed what #RomanSpiridonov wrote - he's absolutely correct. You can't retrieve the #categories before attempting to add your new one. Move the line #categories = ... to the end of the create method.
Also, I noticed that your Category model is namespaced - that means your default form id attribute is more likely something like 'new_admin_category'. You should check how it is actually being rendered and use that id in your jQuery selector when registering the success callback:
$(function(){
$('#new_admin_category').on('ajax:success', function(...
Instance variable #categories in method "create" defined before saving new category. That's why you can't receive the lastest category in your template.
I think you could write something like that:
$("#dashboard_categories").append("<%= escape_javascript(render("category")) %>");
You could add this piece of javascript in a file inside assets/javascripts folder
$(your_form_selector).on('ajax:success', function(){
// Read link to see what event names you can use instead of ajax:success and
// which arguments this function provides you.
})
To set your_form_selector you could add an id to your form and use #myFormId
Read more here
I want to the following thing to happen:
When I select a selectbox I want it to send a JSON object through AJAX to my controller like so :
var encoded = $.param(data);
jQuery.ajax ({
url: '/trips',
type: 'GET',
data: {'toSearch' : encoded},
success: function (response) {
//succes
console.log(encoded);
}
});
and use these as params for my query like so
respond_to :json, :html
def index
#trips = Trip.scoped
#trips.where("category_id = ?", params[:category_ids]) if params[:category_ids] # category_ids = array; select * from trips where category_id IN [1,3,4]
respond_with #trips
end
How do I do this? and what is the best way to serialize my JSON object?
You can do the following:
def index
#trips = Trip.scoped
#trips.where("category_id = ?", params[:category_ids]) if params[:category_ids] # category_ids = array; select * from trips where category_id IN [1,3,4]
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #trips }
format.json { render :json => #trips }
end
end
Then you should do whatever you want to in the javascript callback.
Edit
If you want just the json format you can do the following:
render :json => #trips
You can also check the Layouts and Rendering in Rails guide.