Rails 3 Ajax "Double Post Request" Issue - javascript

My Ajax is posting twice (while i'm expecting only once) on a click, and I can't seem to figure why. I think it may be a double render issue, but I'm pretty new to rails, and need some insight as to where?
The JS:
$("select[name='order[city]']").on("blur",function() {
$("#triangle").fadeOut(800);
$("#cityFee").fadeOut(800);
if (feeSelected == 80 || feeSelected == 81){
$.ajax({
type: "POST",
url: '/line_items',
beforeSend: function(xhr){
xhr.setRequestHeader('X-CSRF-Token', $('meta[name="csrf-token"]').attr('content'))},
data: {product_id: feeSelected, qty_selected: 1, remote: true},
dataType: "script"
});
}
});
The Controller:
def create
#cart = current_cart
product = Product.find(params[:product_id])
ctlQty = params[:qty_selected] #parameter from itemBoxZoom user selected quantity in jquery dialog widget
#line_item = #cart.add_product(product.id, ctlQty) #passes in user selected quantity
respond_to do |format|
if #line_item.save
format.html { redirect_to(store_index_url) }
format.js { #current_item = #line_item }
format.json { render json: #line_item, status: :created, :location => #line_item }
else
format.html { render :action => "new" }
format.json { render json: #line_item.errors, status: :unprocessable_entity }
end
end
end
The method called in my Model:
def add_product(product_id, qty_selected)
current_qty = qty_selected || 1
current_item = line_items.find_by_product_id(product_id)
if current_item
current_item.quantity += current_qty.to_i
else
current_item = line_items.build(:product_id => product_id)
if qty_selected
current_item.quantity += current_qty.to_i - 1 #removes unnecessary default value of 1
end
end
qty_selected = nil
current_item
end
In my console, I see two almost identical post requests to LineItemsController#create, except while the 1st performs "INSERT INTO", the 2nd request performs "SET quantity = 2". All suggestions/help is SO much appreciated. Thanks

If it were a double render issue, you'd get a DoubleRenderError. Have you checked that the JavaScript isn't getting initialized/called twice? Does the form itself submit to the same action? If so, does it return false or otherwise cancel the form submission? I see that you are "submitting" the form on blur. I wonder if when you select the value and press enter, it triggers the blur event and submits the form? Or that the blur event is being triggered twice?
These are just the places I'd start looking.

Related

Rails + JS + Ajax request

Hi I'm making simple clone of game where is Waldo (Wally) with Rails + JS + Ajax. The idea is: player becomes 3 images and has to check where 3 characters (Waldo,Wilma,Wizard) are. After 3. image shows up field to submit name and after submit show high score list.
So far, i have code the mechanism for time (JS variable with setInterval), points (JS + AJAX + rails controller) but i can't code the action for submit name, points and time to model.
My view file:
<%= form_tag('/check/highscore', method: :post, format: :js, remote: true ) do %>
<%= text_field_tag 'name', 'enter your name' %>
<%= submit_tag 'submit' %>
<% end %>
<script type="text/javascript">
$.ajax({
url: "<%= highscore_check_index_path %>",
type: "POST",
format: "js",
dataType: "script",
data: { username: $(this).username, score: guessd, time: time },
success: function(data) {
$('send_highscore').append('data.username')
window.waldo.HS()
},
});
};
my controller file:
def highscore
#username = params[:name]
respond_to do |format|
format.js { render :layout => false }
format.html
end
end
def highscore2
record = Highscore.new
record.username = params[:username]
record.score = params[:score]
record.time = params[:time]
record.save
end
my highscore.js.erb
console.log("<%=j #username %>");
window.waldo.HS = function(username) {
$.ajax({
url: "<%= highscore2_check_index_path %>",
type: "POST",
format: "js",
data: { username: name, score: guessd, time: time },
success: function(data) {
console.log(data);
$('send_highscore').append('data.username')
}
});
});
I know, my oode is very bad quality, but i try to learn it now. Problem is that the highscore.js.erb is not executed although i see it in firebug rendered as a js script file.
The idea of highscore.js.erb is to mix ruby and js variables and together sent do highscore2 action in check controller and save to db.
js.erb is a bad way to learn.
Ruby on Rails can be used as a kind of poor mans Single Page Architecture by using remote: true and returning JS.erb responses. But all it really does it gives you enough rope to hang yourself.
Its a really bad way to learn ajax as it leads to poor code organisation and a very non RESTful application as it leads to a focus on procedures instead of resources.
I instead encourage you to try to learn ajax without mixing ERB and JS. Mixing server side and client side logic just makes everything much more complicated as you have to keep track of what is happening where.
Instead you might want to focus on setting up reusable javascript functions in app/assets/javascripts and fetching JSON data instead of javascript procedures.
This will teach you not only to do ajax in Rails but also how to do it with external API's and teach you valuable lessons about how to structure and organise code.
So lets start looking at how to refactor:
Highscores should be a resource.
Lets start by setting up the routes:
resources :highscores, only: [:create, :index, :show]
And the controller:
class HighscoresController < ApplicationController
respond_to :json, :html
def create
#highscore = Highscore.create(highscore_params)
respond_with #highscore
end
def index
#highscores = Highscore.order(score: :desc).all
respond_with #highscores
end
def show
#highscore = Highscore.find(params[:id])
respond_with #highscore
end
private
def highscore_params
params.require(:highscore).permit(:username, :time, :score)
end
end
Responders is a pretty awesome tool that makes it so that we don't have to do a bunch of boilerplate code when it comes to returning responses. Our create method will return 200 OK if it was successful and 422 BAD ENTITY or some other "bad" response code if it failed.
Some javascript scaffolding
Lets set up a little bit of scaffolding so that we can hook our javascript up to a specific controller and action without using inline script tags.
Open up layouts/application.html.erb and replace the <body> tag:
<%= content_tag :body, data: { action: action_name, controller: controller_name } do %>
<%= yield %>
<% end %>
Then add this little piece to applicaiton.js:
// Triggers events based on the current controller and action.
// Example:
// given the controller UsersController and the action index
// users:loaded
// users.index:loaded
$(document).on('page:change', function(){
var data = $('body').data();
$(this).trigger(data.controller + ":loaded")
.trigger(data.controller + "." + data.action + ":loaded");
});
Where is that ajax y'all was talkin' bout?
Lets say we have a list of highscores that we want to update either at regular intervals (polling) or when the user submits a highscore.
// app/assets/javascripts/highscores.js
function getHighscores(){
var $self = $('.highscores-list');
return $.getJSON('/highscores').done(function(highscores){
var elements = $.map(highscores, function(h){
return $('<li>').text([h.username, h.score].join(', '));
});
$self.empty().append(elements);
});
};
$(document).on('games:loaded highscores:loaded', function(){
// refresh highscores every 5 seconds
(function refresh(){
getHighscores().done(function(){
window.setTimeout(refresh, 5000);
});
}());
});
That last part is a bit hairy - its a recursive function that sets a new timer when the ajax call has completed. The reason we use this and not setInterval is that setInterval does not care if the previous call was completed or not.
Creating Highscores.
First lets russle up a form:
<%# app/views/highscores/_form.html.erb %>
<%= form_for(local_assigns[:highscore] || Highscore.new), class: 'highscore-form' do |f| %>
<div class="field">
<%= f.label :username %>
<%= f.text_field :username %>
</div>
<%= f.hidden_field :score %>
<%= f.hidden_field :time %>
<%= f.submit %>
<% end %>
And then lets spiff it up with some ajax goodness:
$(document).on('games:loaded highscores:loaded', function(){
$('#new_highscore').submit(function(e){
$.ajax(this.action, {
data: {
highscore: {
username: this.elements["highscore[username]"].value,
time: this.elements["highscore[time]"].value,
score: this.elements["highscore[score]"].value
}
},
method: 'POST',
dataType: 'JSON'
}).done(function(data, status, jqXHR){
getHighscores();
// todo notify user
});
return false; // prevent form from being submitted normally
});
});

Create/Save Users on Rails API using AJAX

I have a simple application that serves JSON through the API back end when creating a new user. I'm trying to add AJAX to the request so it doesn't reload the page when a new user is added. I'm really new to AJAX/API and have really no idea what needs to be done.
This is my Users API Controller:
class Api::UsersController < Api::BaseController
protect_from_forgery with: :null_session
def create
#user = User.create!(user_params)
respond_to do |format|
format.html { redirect_to users_list_path, notice: 'User was successfully created.' }
format.json { render json: #index }
format.js
end
end
private
def user_params
params.require(:user).permit(:name)
end
def query_params
params.permit(:name)
end
end
This is my index view(it should refresh with ajax call):
<% content_for :title do %>Users<% end %>
<h3>Here is the user list. Please click on one to see messages.</h3>
<br/>
<% User.all.each do |user| %>
<p><%= link_to String(user.id) + '. ' + user.name, messages_list_path(user_id: user.id) %></p>
<% end %>
<br/>
<%= form_tag("/api/users.json", method: "post") do %>
<%= label_tag(:add_user, "Add User:") %>
<%= text_field_tag("user[name]") %>
<%= submit_tag("Add") %>
<% end %>
The issue I am having is when I add a new user it takes me straight to http://localhost:3000/api/users.json which displays the JSON data. I need the app to basically stay on the same page where the form is and just simply update with AJAX.
I am really new to the building API's in rails as well as AJAX so any help would be great!
add remote: true to your form_tag to submit an ajax call.
see DOCS
form_tag("/api/users.json", method: "post", remote: true)
The problem is that the form automatically redirects you when submitted. You could put in a jQuery event listener to prevent that and then create the new user with an ajax call after, something like:
$('form').on('submit', submitForm.bind(this, event));
var submitForm = function (event) () {
event.preventDefault();
var user = $('form').serialize();
$.ajax({
url: "/api/users",
method: "POST",
data: user,
dataType: "json",
success: function(data){
this.handleSuccess(data);
}.bind(this)
});
};
Not sure if that's exactly what you're looking for, but hope it helps!

jQuery getJSON alert data

I have a problem with using jQuery getJSON method with alert javascript method. In my Ruby on Rails application I have a simple javascript file that looks like this:
$(document).ready(function() {
$.getJSON("/users", function(data) {
alert('It works!');
});
});
Application sends properly request and get proper json but not render this alert.But when I change this to:
$(document).ready(function() {
$.get("/survey_result_sets/120", function(data) {
alert(JSON.stringify(data));
});
});
then alert is showing properly. Why is that?
UPDATE:
My index action in users_controller looks like this:
def index
#users = User.all
respond_to do |format|
format.html
format.json { render json: 'It works' }
end
end

Dropzonejs with Rails Delete files from the page

I am using a carrierwave and dropzonejs. Everything seems fine however, when I try to press to remove picture button, even though it removes from the database, picture stays on the container.
Here how it looks like;
When I click to remove file link all of them, it becomes;
So I clicked all the remove file buttons, they are removed from the database however, stays on the page. I think it is because of the js code (end part) below,
<script type="text/javascript">
$(document).ready(function(){
// disable auto discover
Dropzone.autoDiscover = false;
// grap our upload form by its id
$("#picture-dropzone").dropzone({
// restrict image size to a maximum 5MB
maxFilesize: 5,
// changed the passed param to one accepted by
// our rails app
paramName: "picture[image]",
acceptedFiles: "image/*",
// show remove links on each image upload
addRemoveLinks: true,
// if the upload was successful
success: function(file, response){
// find the remove button link of the uploaded file and give it an id
// based of the fileID response from the server
$(file.previewTemplate).find('.dz-remove').attr('id', response.fileID);
$(file.previewTemplate).find('.dz-remove').attr('boat_id', response.boatID);
// add the dz-success class (the green tick sign)
$(file.previewElement).addClass("dz-success");
},
//when the remove button is clicked
removedfile: function(file){
//location.reload();
//removeFile(file); *******THIS DOES NOT WORK*******
// grap the id of the uploaded file we set earlier
var id = $(file.previewTemplate).find('.dz-remove').attr('id');
var boat_id = $(file.previewTemplate).find('.dz-remove').attr('boat_id');
// // make a DELETE ajax request to delete the file
$.ajax({
type: 'DELETE',
url: '/boats/' + boat_id + '/pictures/' + id,
success: function(file){
}
});
}
});
});
</script>
pictures controller if anyone wonders,
class PicturesController < ApplicationController
before_action :logged_in_user
before_filter :load_parent
def new
#picture = #boat.pictures.new
#pictures = #boat.pictures.all
end
def show
#picture = #boat.pictures.find(params[:id])
end
def create
#picture = #boat.pictures.new(picture_params)
if #picture.save
render json: { message: "success", fileID: #picture.id, boatID: #boat.id }, :status => 200
else
render json: { error: #picture.errors.full_messages.join(',')}, :status => 400
end
end
def edit
#picture = Picture.find(params[:id])
end
def update
#picture = #boat.pictures.find(params[:id])
if #picture.update_attributes(picture_params)
flash[:notice] = "Successfully updated picture."
render 'index'
else
render 'edit'
end
end
def destroy
#picture = #boat.pictures.find(params[:id])
if #picture.destroy
render json: { message: "File deleted from server" }
#redirect_to new_boat_picture_path(#boat, #picture)
flash[:notice] = "Successfully destroyed picture."
else
render json: { message: #picture.errors.full_messages.join(',') }
end
#flash[:notice] = "Successfully destroyed picture."
#redirect_to new_boat_picture_path(#boat, #picture)
#redirect_to boat_pictures_path(#boat)
#redirect_to boat_path(#boat)
end
private
def picture_params
params.require(:picture).permit(:name, :image)
end
def load_parent
#boat = Boat.find(params[:boat_id])
end
end
write below line in your delete ajax call success method
$(file.previewTemplate).fadeOut()

rails 4.0.1 can't show ruby code in view via javascript ajax

I'm trying to update vote count via ajax once user has voted. My code works fine except for something which should be pretty basic, which is showing the new total number of votes.
My javascript has the following code:
var voteCount = "<%= #trip.total_up_votes %>";
...
$.ajax({
...
success: function() {
console.log("SAVED TO VOTES TABLE SUCCESSFULLY");
$('#voting_up').html(voteCount);
},
...
});
Once the vote link has been clicked, everything gets added to the table fine except it shows the new vote count as <%= #trip.total_up_votes %> i.e. as a string. The total_up_votes method simply counts the number of up votes from the votes table. This works fine when the page is first loaded or when it's refreshed.
I've tried escape_javascript and many other suggestions after trawling through the internet but I'm still stuck. Could someone help please?
EDIT:
As requested, my votes_controller does this:
def cast_vote()
#vote = Vote.where("user_id = ? AND trip_id = ?", current_user, params[:id]).first || Vote.new(:user_id => current_user)
#vote.vote_type = params[:vote_type]
#vote.user_id = params[:user_id]
#vote.trip_id = params[:id]
respond_to do |format|
#vote.save
format.html {redirect_to :back}
format.js
end
end
Have you tried dropping the quotes?
var voteCount = <%= #trip.total_up_votes %>;
...
$.ajax({
...
success: function() {
console.log("SAVED TO VOTES TABLE SUCCESSFULLY");
$('#voting_up').html(voteCount);
},
...
});
This is all overkill. You should use the ruby ajax helper "remote". use this:
<%= link_to "Vote up", vote_up_path, remote: true %>
The remote: true above will tell the browser to handle the link via ajax, so the page will not get reloaded. vote_up_path is just an example. Lets say vote_up_path takes you to the controller action votes#upvote, then in your votes views folder you should have a file called upvote.js.erb. In that file, have the following code:
var voteCount = <%= #trip.total_up_votes %>;
console.log("SAVED TO VOTES TABLE SUCCESSFULLY");
$('#voting_up').html(voteCount);

Categories

Resources