How to respond to ajax call in Rails - ruby-on-rails

I know there are many questions about that already on stackoverflow but none of them has been useful for me. Here is my ajax code:
function update_schedule($task){
var taskId = $task.data("taskId"),
startHour, endHour,
userId = $('#users').val();
if( !$task.hasClass('daily-task')){
startHour = getStartHour($task),
endHour = startHour + (+$task.data('time'));
}
console.log(startHour, endHour)
$.ajax({
url: '/heimdall/update_scheduled_task',
method: 'POST',
data: { "task_id": taskId, "start_hour": startHour, "end_hour": endHour, "user_id": userId },
success: function (){
console.log("SUCESS!!", data['head'])
},
error: function () {
console.log("FAILURE");
},
async: true
});
}
The controller code:
def update_scheduled_task
scheduled_task = ScheduledTask.find_or_create_by(task_id: params[:task_id])
scheduled_task.update_attributes(start_hour: params[:start_hour], end_hour: params[:end_hour], task_id: params[:task_id], user_id: params[:user_id])
end
I want to return the id of found or created task. I don't know how to send/receive this information. I've read about respond to but still I don't know how to use it in this case.

You may do render json: ... to return the required info to ajax in JSON format:
def update_scheduled_task
scheduled_task = ScheduledTask.find_or_create_by(task_id: params[:task_id])
scheduled_task.update_attributes(start_hour: params[:start_hour], end_hour: params[:end_hour], task_id: params[:task_id], user_id: params[:user_id])
render json: {scheduled_task_id: scheduled_task.id}
end
And in the ajax function's success, use it like:
success: function (data){
var data = JSON.parse(data);
console.log(data["scheduled_task_id"]);
},

First, you need to improve your controller code structure (You can rely on the default scaffold generator methods). Then, you must indicate that your method will respond to json format only, with the answer you want to return, something like this:
def update_scheduled_task
scheduled_task = ScheduledTask.find_or_create_by(task_id: params[:task_id])
if (scheduled_task && scheduled_task.update_attributes(start_hour: params[:start_hour], end_hour: params[:end_hour], task_id: params[:task_id], user_id: params[:user_id]))
render json: { scheduled_task_id: scheduled_task.id }
else
render json: { error: l18n.t("error.messages.request_error") }
end
end
Then, you must modify the success and failure response methods of the jquery ajax request. Here's an example of how it might look:
$.ajax({
url: "/heimdall/update_scheduled_task",
method: "post",
data: { task_id: taskId, start_hour: startHour, end_hour: endHour, user_id: userId },
success: function(result) {
if (result["scheduled_task_id"] != null) {
console.log("Schedule record => " + result["scheduled_task_id"])
} else {
console.log("Error: " + result["error"])
}
},
error: function() {
console.log("Ajax request error");
},
// async: true => By default JQuery set true to async param.
});
Do not forget that you need to add a rule to access this method in the file config/ruotes.rb, something like this:
post update_scheduled_task, :defaults => { :format => 'json' }
I hope this helped you, regards!

jQuery's success and error callbacks rely on the status code returned by the controller. Make sure that you return the right status code when you are unable to create/read/update the object. On success, simply render a JSON with the id property set. I beleive update_attributes returns true on success:
if scheduled_task && scheduled_task.update_attributes(...)
render json: { id: scheduled_task.id }
else
head :unprocessable_entity
end

Related

How to reload particular partial value with response from rails api?

I have set up a rails api and it only response in the following format which is done through the serializer:
data: { user_name: 'Test User' }
And on the view side, I have got the following partial:
.user-change
= render partial: 'test_user', locals: {user_name: current_user.name}
What would be the correct way to change the user_name as per the API response from the following successful ajax request?
$("#change-me").click(function() {
var user_id;
user_id = $("#user_id").val();
$.ajax({
url: "/api/v1/user/change/" + user_id,
method: "POST",
dataType: "json",
data: JSON.stringify({
{
user_id: user_id,
}
}),
success: function(res, status, xhr) {
let response = res.data;
let userName = response.user_name;
// Update user_name after this event on the partial?
}
},
error: function(res) {
}
});
})
Partial code: _test_user.html.haml
.col-md-9
%p= "Hi! This is the request change from #{user_name}."

Rails Ajax call 404 error for the edit view

I have a button on my new and edit views that sends a post request to my Letter controller through an Ajax call. If the Ajax call works perfectly in the new view, it throws a 404 error for my edit view.
Route:
post 'letters/ajax_send_test_botletter', to: 'letters#send_test_botletter', as: 'send_test_botletter'
The form is defined like this:
<%= form_for(letter, :html => {class: "directUpload", remote: true}) do |f| %>
The button triggering the Ajax call in the form:
<button class="cta3" id="send_test_letter">Send a test campaign to yourself</button>
Ajax call:
$('#send_test_letter').on('click', function(){
$('form').submit(function() {
var valuesToSubmit = $(this).serialize();
$.ajax({
type: "POST",
url: "/letters/ajax_send_test_botletter",
data: valuesToSubmit,
dataType: "JSON" // you want a difference between normal and ajax-calls, and json is standard
}).success(function(json){
if(json['value'] == "No Recipient") {
$('#send_test_letter').css('display', 'none');
$('#save_test_user').css('display', 'block');
} else {
console.log("Success")
$('#confirmation_test_sent').html('Test successfully sent. Check your Messenger.')
}
$('form').unbind('submit');
});
return false; // prevents normal behaviour
});
});
My send_test_botletter method
def send_test_botletter
#message_content = params[:letter]['messages_attributes']['0']['content']
#button_message = params[:letter]['messages_attributes']['0']['buttons_attributes']['0']['button_text'] if params[:letter]['messages_attributes']['0']['buttons_attributes']['0']['button_text'] != ''
#button_url = params[:letter]['messages_attributes']['0']['buttons_attributes']['0']['button_url'] if params[:letter]['messages_attributes']['0']['buttons_attributes']['0']['button_url'] != ''
#cards = params[:letter]['cards_attributes'] if params[:letter]['cards_attributes'].present? == true
#test_segment = Segment.where(core_bot_id: #core_bot_active.id, name: "test").first
#recipients = BotUser.where(core_bot_id: #core_bot_active.id, source: #test_segment.token)
if #recipients.exists?
send_message_onboarding if #message_content != '' and #button_message.present? == false
send_message_button_onboarding if #message_content != '' and #button_message.present? == true and #button_url.present? == true
send_card_onboarding if #cards
respond_to do |format|
format.json { render json: {"value" => "Success"}}
end
else
respond_to do |format|
format.json { render json: {"value" => "No Recipient"}}
end
end
end
I get the following error in the Chrome console for the edit view:
POST http://localhost:3000/letters/ajax_send_test_botletter 404 (Not
Found)
And in my Rails logs:
ActiveRecord::RecordNotFound (Couldn't find Letter with
'id'=ajax_send_test_botletter):
It seems it calls the Update method instead of the send_test_botletter method...
Any idea what's wrong here?
I found the trick. The problem was the PATCH method in the edit form.
I found a plugin in this discussion in order to modify the serialized data and change the method to "post":
$('#send_test_letter').on('click', function(){
$('form').submit(function() {
var valuesToSubmit = $(this).awesomeFormSerializer({
_method: 'post',
});
$.ajax({
type: "POST",
url: "/letters/ajax_send_test_botletter",
data: valuesToSubmit,
dataType: "JSON" // you want a difference between normal and ajax-calls, and json is standard
}).success(function(json){
if(json['value'] == "No Recipient") {
$('#send_test_letter').css('display', 'none');
$('#save_test_user').css('display', 'block');
} else {
console.log("Success")
$('#confirmation_test_sent').html('Test successfully sent. Check your Messenger.')
}
$('form').unbind('submit');
});
return false; // prevents normal behaviour
});
});
(function ( $ ) {
// Pass an object of key/vals to override
$.fn.awesomeFormSerializer = function(overrides) {
// Get the parameters as an array
var newParams = this.serializeArray();
for(var key in overrides) {
var newVal = overrides[key]
// Find and replace `content` if there
for (index = 0; index < newParams.length; ++index) {
if (newParams[index].name == key) {
newParams[index].value = newVal;
break;
}
}
// Add it if it wasn't there
if (index >= newParams.length) {
newParams.push({
name: key,
value: newVal
});
}
}
// Convert to URL-encoded string
return $.param(newParams);
}
}( jQuery ));
form_for(letter... generates a different url and method depending whether or not the instance is persisted, defaulting to create and post or update and patch as appropriate.
When you hit submit, it's trying to hit this endpoint, before your listener kicks in. And in doing so, breaks the remaining js.
However, you can also provide url and method options to form_for. Try providing a blank url option and the correct method (form_for letter, ..., url: '', method: :post).
Alternatively, you could stop the default behaviour / propagation on form submission:
$('form').submit(function(e) {
e.stopPropagation() // Or could simply be `preventDefault()`, depending on your use case
...
// your AJAX
}
Able to test out these approaches?
Update
Your method is actually nesting a submit listener within the click one. Try the following:
$('#send_test_letter').on('click', function(e){
e.stopPropagation()
var $form = $(this).closest('form')
var valuesToSubmit = $form.serialize();
$.ajax({
type: "POST",
url: "/letters/ajax_send_test_botletter",
data: valuesToSubmit,
dataType: "JSON" // you want a difference between normal and ajax-calls, and json is standard
}).success(function(json){
if(json['value'] == "No Recipient") {
$('#send_test_letter').css('display', 'none');
$('#save_test_user').css('display', 'block');
} else {
console.log("Success")
$('#confirmation_test_sent').html('Test successfully sent. Check your Messenger.')
}
return false; // prevents normal behaviour
});
});

Ruby Devise sign up with AJAX not getting json response?

I have followed this tutorial: https://blog.andrewray.me/how-to-set-up-devise-ajax-authentication-with-rails-4-0/ And am using rails 5.1.
I have implemented the json response in the controller:
class RegistrationsController < Devise::RegistrationsController
respond_to :json
end
And when I call the ajax I only get an Html/Text response:
function createUser(callback) {
$.ajax({
type: "POST",
url: window.urls.createUser,
data: {
authenticity_token: $("meta[name=csrf-token]").attr("content"),
user: grabOrderFormUserData()
},
success: function(data) {
console.log("Data: " + data);
},
error: function (data) {
//console.log("error");
}
})
}
That call works fine, but returns the HTML page of the sign up.
The url I use is createUser: hostUrl + '/users/'
What did I miss?
And no, if I add .json to my url, it will respond with 500 error code.
I think you missed to mention dataType: "json" with your ajax call , try this
function createUser(callback) {
$.ajax({
url: window.urls.createUser,
type: "POST",
data: {`enter code here`
authenticity_token: $("meta[name=csrf-token]").attr("content"),
user: grabOrderFormUserData()
},
dataType: "json",
success: function(data) {
console.log("Data: " + data);
},
error: function (data) {
//console.log("error");
}
})
}
and also in your controller try to call this block in case if you are not getting json response,
respond_to do |format|
format.json {
render json: {.....}
}
end
thank you.

angular resource not invoking my error callback function

I've been at this for hours and I can't figure out why angular is not triggering my error call back when my rails back-end raises a proper error. I'm using angular 1.2.0rc1.
According to the documentation:
non-GET "class" actions: Resource.action([parameters], postData, [success], [error])
And I'm using it in my angular controller during a save product operation:
$scope.saveProduct = function(product){
if (product.id) {
Product.update({id: product.id},{product: product}, function(data){
console.log('handle success')
}, function(){
console.log('handle error') //THIS IS NEVER OUTPUT!
});
}
}
Here is the resource definition:
angular.module('sellbriteApp.resources').factory('Product', function ($resource) {
return $resource('/api/products/:id', { id: "#id" },
{
'create': { method: 'POST' },
'index': { method: 'GET', isArray: true },
'show': { method: 'GET', isArray: false },
'update': { method: 'PUT' },
'destroy': { method: 'DELETE' }
}
);
});
Here is my rails controller:
def update
respond_to do |format|
if #product.update(product_params)
format.html { redirect_to [:edit, #product], notice: 'Product was successfully updated.' }
format.json { render 'products/show.json.jbuilder', status: :accepted }
else
format.html { render action: 'edit' }
format.json { render json: #product.errors, status: :unprocessable_entity }
end
end
end
Rails returns a 422 status when attempting to save a product with a duplicate sku, and I want to display an error msg on the front end.
I would expect that angular should execute the error handling function provided in the update call, but I can't get that far. Instead in my console I see:
TypeError: Cannot read property 'data' of undefined with an unhelpful stacktrace:
TypeError: Cannot read property 'data' of undefined
at $http.then.value.$resolved (http://localhost:9000/bower_components/angular-resource/angular-resource.js:477:32)
at wrappedCallback (http://localhost:9000/bower_components/angular/angular.js:9042:59)
at wrappedCallback (http://localhost:9000/bower_components/angular/angular.js:9042:59)
at http://localhost:9000/bower_components/angular/angular.js:9128:26
at Object.Scope.$eval (http://localhost:9000/bower_components/angular/angular.js:9953:28)
at Object.Scope.$digest (http://localhost:9000/bower_components/angular/angular.js:9809:23)
at Object.$delegate.__proto__.$digest (<anonymous>:844:31)
at Object.Scope.$apply (http://localhost:9000/bower_components/angular/angular.js:10039:24)
at Object.$delegate.__proto__.$apply (<anonymous>:855:30)
at done (http://localhost:9000/bower_components/angular/angular.js:6542:45)
What am I missing?
UPDATE:
Apparently this http interceptor is related. If I comment this code out, the error function is called. I had copied this snippet from some where else and modified it in order to redirect a user to the sign_up page if they hit a rails api when they are not logged in. It must be interfering, but I'm not sure how I should fix it.
App.config(['$httpProvider', function ($httpProvider) {
$httpProvider.responseInterceptors.push('securityInterceptor');
}]);
App.factory('securityInterceptor', ['$injector', '$location', '$cookieStore', function ($injector,$location,$cookieStore) {
return function(promise) {
var $http = $injector.get('$http');
return promise.then(null, function(response){
if (response.status === 401) {
$cookieStore.remove('_angular_devise_merchant');
toastr.warning('You are logged out');
$location.path('/sign_in');
}
});
};
}]);
You need to reject the promise in your interceptor as well, otherwise its considered as if you've 'handled' the exception.
So:
App.factory('securityInterceptor', ['$injector', '$location', '$cookieStore', '$q', function ($injector,$location,$cookieStore, $q) {
return function(promise) {
var $http = $injector.get('$http');
return promise.then(null, function(response){
if (response.status === 401) {
$cookieStore.remove('_angular_devise_merchant');
toastr.warning('You are logged out');
$location.path('/sign_in');
}
return $q.reject(response);
});
};
}]);

Read cross domain JSON response

<script>
$.ajaxSetup( {contentType: 'application/json'} );
function submit_data(f){
alert('submitting')
var data_string = $(f).serialize();
$.ajax({
url: "http://localhost:3000/application/1/contact_us.json?jsonpcallback=?"+data_string,
dataType: "jsonp",
type : 'post',
processData: false,
crossDomain: true,
contentType: "application/json",
jsonp: false,
jsonpcallback: result()
});
}
function result(){
alert('back in')
alert(data)
}
function jsonp1300279694167(){
alert('dhoom')
}
</script>
I have script above querying across domain and posting data within a form.
Everything seems to work fine. JSON response can be seen in the firebug console. I want to process the response and display status messages accordingly to the user. How should I achieve it?
UPDATE
I have tried as suggested by T.J. Crowder but have no luck yet. The modified code is as below
function submit_data(f){
alert('submitting')
var data_string = $(f).serialize();
$.ajax({
url: "http://localhost:3000/application/1/contact_us.json?"+data_string,
dataType: "jsonp",
crossDomain: true,
success: handleSuccess()
});
}
function handleSuccess(data) {
alert("Call completed successfully");
alert(data);
}
This does not accesses data and alerts undefined. If I try to pass it from success: handleSuccess() it errors and redirects with a http request.
I am getting response from a Ruby on Rails application. Here is the method I am hitting
def create
errors = ContactUsForm.validate_fields(params)
logger.info errors.inspect
if errors.blank?
respond_to do |format|
format.json {render :json => {:status => 'success'}.to_json}
end
else
respond_to do |format|
format.json {render :json => {:status => 'failure', :errors => errors}.to_json}
end
end
end
Is there any thing that I need to configure in my rails app
You're close. You just use the success callback as usual (see the ajax docs), not a special one:
$.ajax({
url: "http://localhost:3000/application/1/contact_us.json?jsonpcallback=?"+data_string,
dataType: "jsonp",
type : 'post',
processData: false,
crossDomain: true,
contentType: "application/json",
jsonp: false,
success: function(data) {
// Use data here
}
});
Also, your code:
jsonpresponse: result()
...would call the result function and then use its return value for the jsonpresponse property of the ajax call. If you want to use a separate function, that's fine, but you don't include the (), so:
$.ajax({
url: "http://localhost:3000/application/1/contact_us.json?jsonpcallback=?"+data_string,
dataType: "jsonp",
type : 'post',
processData: false,
crossDomain: true,
contentType: "application/json",
jsonp: false,
success: result
});
function result(data) {
// use `data` here
}
Also, I'm pretty sure you don't need/want the jsonp parameter if you use success, so:
$.ajax({
url: "http://localhost:3000/application/1/contact_us.json?jsonpcallback=?"+data_string,
dataType: "jsonp",
type : 'post',
processData: false,
crossDomain: true,
contentType: "application/json",
success: result
});
function result(data) {
// use `data` here
}
Finally: Are you sure you want to set contentType? That relates to the content being sent to the server, not the content being received from it. If you're really posting JSON-encoded data to the server, great, you're fine; but it looks like you're using jQuery's serialize function, which will not produce JSON (it produces a URL-encoded data string). So you probably want to remove contentType as well, both from the call and from the ajaxSetup call.
I hope if you can try jQuery-JSONP
jQuery-JSONP How To
[Example]
$.getJSON('server-url/Handler.ashx/?Callback=DocumentReadStatus',
{
userID: vuserID,
documentID: vdocumentID
},
function(result) {
if (result.readStatus == '1') {
alert("ACCEPTED");
}
else if (result.readStatus == '0') {
alert("NOT ACCEPTED");
}
else {
alert(result.readStatus);
}
});
I tried many tutorials including the answers above but had no luck. So I implemented it something like below
Form
<form action="" onsubmit="submit_data(this, '1'); return false;">
// some form fields
</form>
Submit function for form
<script>
function submit_data(f, app_id){
var data_string = $(f).serialize();
$.ajax({
url: "http://www.example.com/"+app_id+"/contact_us.js?"+data_string,
dataType: "jsonp",
crossDomain: true,
});
}
function show_errors(jsonOb)
{
$("span.error").remove();
$.each(jsonOb, function(key,val){
$("#contact_us_form_"+key).after("<span class=error>"+val+"</span>")
});
}
</script>
In my controller
def create
#application = Application.find params[:application_code]
#errors = ContactUsForm.validate_fields(params, #application)
#application.save_contact_us_form(params[:contact_us_form]) if #errors.blank?
respond_to do |format|
format.js #{render :json => {:status => 'success'}.to_json}
end
end
And finally in create.js.erb
<% if #errors.blank? %>
window.location = "<%= #application.redirect_url %>"
<% else %>
var errors = replaceAll('<%= escape_javascript(#errors.to_json)%>', """, "'")
var errors_json = eval('(' + errors + ')')
show_errors(errors_json);
function replaceAll(txt, replace, with_this) {
return txt.replace(new RegExp(replace, 'g'),with_this);
}
<% end %>
This way I called submit_form on form submit and called show_errors javascript function from server it self. And it works..
But still I would like to have comments if this is a worst solution?

Resources