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
Related
I'm building a rails 5 App and integrating a Tagging feature using acts_as_taggable_on, select2, and simple_form gems.
So essentially what I'm trying to do is tag a product on the new product form, if the tag exists in an autocomplete dropdown box then use the existing tag, if not create the tag.
Right now, I'm at a bit of loggerhead and not sure where I'm going wrong.
All that I get on the form is an empty multi-select box (below)
this should appear as a text field with a dropdown that fills as the user enters in the tag names.
Product Controller "new" and "create" actions and the product_params method:
def new
#product = Product.new
respond_to do |format|
format.html
format.json { render json: #product }
end
end
def create
#product = Product.new(product_params)
respond_to do |format|
if #product.valid?
#product.save
format.html { redirect_to #product, flash: { success: "#{#product.name.titleize} has been created & added to the store." } }
format.json { render json: #product, status: :created, location: #project }
else
format.html { render action: "new" }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
def product_params
params.require(:product).permit(:name, :description, :price, :tag_list, tag_list: [])
end
My Js:
// JS Initializer
$(document).on('turbolinks:load', function() {
// Select2 Tag Creation
$('.multiple-input').each(function() {
$(this).select2({
tags: true,
tokenSeperators: [','],
theme: 'bootstrap',
placeholder: 'seperated by space'
});
});
// Select2 Autocomplete
$('#product-autocomplete').select2({
ajax: {
url: "/products.json",
dataType: json,
results: function(data, page) {
return {
results: $.map( data, function(product.tag_list, i) {
return { id: product.id, text: product.tag.name }
} )
}
}
}
});
});
My simple_form input:
<%= f.input :tag_list, input_html: { class: 'multiple-input', id: 'product-autocomplete', multiple: "multiple"}, collection: #product.tag_list %>
I've tried a few other ways of implementing this with no luck, I think this is going to really boil down to the JS, and that is my Achilles heel. I'm learning but really struggling with this. Any assistance here would be greatly appreciated! Please if I have missed any pertinent info let me know I'll be happy to post!
EDIT 1:
Im adding a pic of the error my google inspect console is spitting out.
Note: the url: reads /products.json not the #product shown in the image.
So I needed to do a few things to hook this all up..
1. Add Tags Controller and a resource route to call the tags_list
class TagsController < ApplicationController
def index
tags = ActsAsTaggableOn::Tag
.where("name ILIKE ?", "%#{params[:term]}%")
.select("id, name as text")
respond_to do |format|
format.json {render json: tags}
end
end
end
resources :tags, only: [:index]
2) Fix JS to populate the autocomplete dropdown:
// JS Initializer
$(document).on('turbolinks:load', function() {
// Select2 Tag Creation
$('.multiple-input').each(function() {
$(this).select2({
tags: true,
tokenSeperators: [','],
theme: 'bootstrap',
placeholder: 'seperated by space',
minimumInputLength: 3,
ajax: {
url: "/tags.json",
dataType: 'json',
processResults: function(data) {
console.log(data)
return {results: data};
}
}
});
});
});
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'm trying to fetch data from database in rails, this is my controller file
class PathsController < ApplicationController
before_filter :authenticate_user!
skip_before_filter :verify_authenticity_token
def getall
#result = Path.select('x', 'y')
end
respond_to do |format|
format.json { render json: #result }
end
end
Here is my js function
function makelines()
{
$.ajax({
type: "GET",// => method type
url: "/path", // => Target function that will be return result
contentType:"application/json",
success: function(result){
result = JSON.parse(result);
console.log(result);
}
});
}
Here is the route
match '/path => 'Paths#getall', via: [:get, :post], :default => { :format => 'json' }
The first thing you should do in this circumstance is consult your console or logs; that will be the most helpful in pinpointing the exception.
That said, I'll take a guess and warrant that the issue is that you are invoking a respond_to outside of a controller action
def getall
#result = Path.select('x', 'y')
end
respond_to do |format|
format.json { render json: #result }
end
Should be:
def getall
#result = Path.select('x', 'y')
respond_to do |format|
format.json { render json: #result }
end
end
Let's improve your code a little bit:
def getall
#results = Path.select('x', 'y')
respond_to do |format|
if #results.count > 0
format.json { render json: #results, , status: :ok }
else
format.json { render json: #results.errors, , status: :not_found }
end
end
end
Per Rails conventions it would be better to return results instead of result as you are returning more than 1 item.
I also think that as you are returning a JSON object to your AJAX method it would be good to return either a 200 (:ok) or a 404 (:not_found, in the event no record is in the database)
For some reason after a click my button that requests data from my controller, my table disappears. Here is my table:
<div id="SelectedTm" style= float:right>
<table id = "PlyrsTm2" style = float:right>
<tr><th id="PTTitle" colspan=2>List of players on selected team</th></tr>
<tr><th id="PTHRows">Player</th><th id="PTHRows">Team</th></tr>
<% #pl.each do |f| %>
<tr><td class="player"><%= f.Plyr %></td><td class="team"><%= f.Team%></td></tr>
<% end %>
</table>
</div>
Here is the button that triggers my jquery with ajax
<button id="showbtn">Select Team</button>
Here is the jquery and ajax:
$(document).ready(function(){
$('#showbtn').on('click', function() {
ids = $('#teams').val()
IM = false
$.ajax({
url: "http://localhost:3000/teamplayers.json?resolution="+ids+"&import="+IM,
type:"get",
dataType: "json",
cache: true,
success:function(data){
$('#PlyrsTm2').html(data);
alert("Loading Players....");
},
error: function(error) {
alert("Failed " + console.log(error) + " " + error)
}
});
$('#PlyrsTm2').trigger('create');
return false;
});
});
Now as you can see, my table is populated by rails. Every time i select a new team the table disappears. And only re-appears if I reload the page, but that is the originally selected team, which by default is the first one.
UPDATE
Here is my controller:
class TeamplayersController < ApplicationController
before_filter :set_id
before_action :set_id, :set_teamplayer, only: [:show, :edit, :update, :destroy]
# GET /teamplayers
# GET /teamplayers.json
def index
#teamplayers = Teamplayer.all
#fteams = Fteam.all
tid = params[:resolution]
toimport = params[:import]
puts tid
if tid.nil? == true
tid = 1
#ids = tid
#pl = Teamplayer.joins(:live_player).where(:teamid => #ids).all
else
tid = tid.to_i;
#ids = tid
#pl = Teamplayer.joins(:live_player).where(:teamid => #ids).pluck(:Plyr, :Team)
end
if toimport == "true"
#turl = Fteam.where(:id => #ids).pluck(:TeamUrl)
#turl = #turl[0]
system "rake updateTm:updateA[#{#turl},#{#ids}]"
end
end
# GET /teamplayers/1
# GET /teamplayers/1.json
def show
end
# GET /teamplayers/new
def new
#teamplayer = Teamplayer.new
end
# GET /teamplayers/1/edit
def edit
end
# POST /teamplayers
# POST /teamplayers.json
def create
#teamplayer = Teamplayer.new(teamplayer_params)
respond_to do |format|
if #teamplayer.save
format.html { redirect_to #teamplayer, notice: 'Teamplayer was successfully created.' }
format.json { render action: 'show', status: :created, location: #teamplayer }
else
format.html { render action: 'new' }
format.json { render json: #teamplayer.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /teamplayers/1
# PATCH/PUT /teamplayers/1.json
def update
respond_to do |format|
if #teamplayer.update(teamplayer_params)
format.html { redirect_to #teamplayer, notice: 'Teamplayer was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #teamplayer.errors, status: :unprocessable_entity }
end
end
end
# DELETE /teamplayers/1
# DELETE /teamplayers/1.json
def destroy
#teamplayer.destroy
respond_to do |format|
format.html { redirect_to teamplayers_url }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_teamplayer
#teamplayer = Teamplayer.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def teamplayer_params
params.require(:teamplayer).permit(:playerid, :teamid)
end
end
So what is going wrong here, because i notice that it is calling every records individual page but why is it now giving my back the the information from my query as the data instead?
Looks like the statement ids = $("#teams").val(); will return undefined, since there is no element with id="teams".
If ids is undefined, it's likely that data is null or undefined in this function call:
function(data){
$('#PlyrsTm2').html(data);
alert("Loading Players....");
}
If data is null, calling html(null) will cause all your table data to disappear.
So, the solution is to populate ids correctly. I'm not sure what ids is supposed to do, but that's most likely the solution.
$('#PlyrsTm2').html(data);
That's why your table disappears
The problem is you're replacing the content of this element (not the element itself). To fix this, I'd do this:
$('#SelectedTm').html(data);
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.