How to use ajax to replace text in a box? (Rails) - ruby-on-rails

I am making an application where each text box on a page has a "translate" button next to it. When the user clicks on this button, the text in the box would translate to english, via a function I would define in that page's controller. (If the text is already english, the button would not show up)
Where would I start going about how to do this? There are several rails/ajax tutorials on the web. Are there any that go about solving a use-case semi-similar to mine? I'm contacting the server for translating the text, so is this even an ajax request anymore?

I'm contacting the server for translating the text, so is this even an
ajax request anymore?
Yep.
Ajax is basically just a technology which allows Javascript to send requests on your behalf. It stands for Asynchronous Javascript And XML - basically meaning it can send data / requests "behind the scenes"
When you say your function needs to send a request to the server, you're going to be using ajax to do that, unless you want to reload the page.
--
Button
The way you'd handle the translation is as follows:
#app/assets/javascripts/application.js
$(document).on("click", ".translate", function(){
var text = $(".text_element).val();
$.ajax({
url: "pages/translate",
data: {text: text},
success: function(data){ }
});
});
This will allow you to send the request you require to the server, of which you can then process the response.
--
Controller
#config/routes.rb
resources :controller do
collection do
get :translate
end
end
To do this, you'll be best using the respond_to block in your controller:
#app/controllers/your_controller.rb
Class YourController < ApplicationController
respond_to :js, :json, only: :translate
def translate
respond_with ...
end
end

Related

Is this a good way to do AJAX request in Rails 4?

I am mostly familiar with a full JavaScript stack and ever since I have been working in Ruby on Rails, I was never fully confident with the way I make XHR requests, hence, I want to validate my understanding. I looked at the following questions
Ajax request throws 500 error, but the request works
Processing after the request was completed
Rails 3: How to "redirect_to" in Ajax call?
but didn't quite answer my issue.
Here is what is happening from my understanding. In Rails we have our MVC, and the Controller is essentially where we control what the route will render. It can also be designed as an API, for doing things like CRUD.
Next, on our front end, for the sake of simplicity let's use jQuery. We want to make an AJAX request and handle the response.
$.ajax({
url: '/new-sandwich/'+food_id,
type: 'post',
data: { food_id: food_id}
})
.done(function() {
// if it works, refresh the page
})
.fail(function(e) {
// if it works alert the user and refresh the page
alert(e.responseText)
return location.reload();
})
Suppose the Controller for this is very thick, so the response takes up to a couple seconds.
After going through about 100 lines or so... the problem then happens with the response.
If it fails, it alerts the user and after refresh it still processes as if everything was okay. To fix that, I believe I have to add better validation at the top of the controller action.
Next, the problem is that since this a post request, I don't can't figure out where to REDIRECT? If the request is succesful, I want it refresh, but do I do that from the client or the server? If I don't add a render :nothing => true at the end of the POST Controller Action I get...
ActionView::MissingTemplate - Missing template sandwich/create_sandwich, application/create_sandwich
but if I do, then is there a point of doing it in the JavaScript?
Well, in my controller, I typically do something along the lines of:
class FooController < ApplicationController
def create
... do some stuff
render partial: 'some_partial', locals: {if: :needed}
end
end
And then in the javascript, something like:
$.ajax({
url: '/new-sandwich/'+food_id,
type: 'post',
data: { food_id: food_id}
})
.success(function(data) {
$('#someSelector').html(data)
})
.fail(function(e) {
// if it works alert the user and refresh the page
alert(e.responseText)
return location.reload();
})
So, the controller returns some HTML and the js inserts that HTML into the page in the location defined by $('#someSelector').
If my controller method really takes a lot of time, then I would typically do something along the lines of:
class FooController < ApplicationController
def create
render json: {job_id: FooCreateService.call(some: params[:thing])}, status: :ok
end
end
And then my ajax call might look something like:
$.ajax({
url: '/new-sandwich/'+food_id,
type: 'post',
data: { food_id: food_id}
})
.done(function(data) {
#.$jobId = data['job_id']
#showAWorkingOnItNotification()
#pollForResults()
})
.fail(function(e) {
// if it works alert the user and refresh the page
alert(e.responseText)
return location.reload();
})
My #pollForResults function would use #.$jobId to poll a background job service for completion, and then respond appropriately when the job is done (successfully or unsuccessfully).
It is, naturally, a bit more complicated than all of that. But, that's the direction I usually head.

Add a "Thank you for your order" page that is not directly visitable

When the user purchases an item we want to display a thank you page.
Then again we want this page not to be directly visitable.
In other words, if the page was to be /orders/thankyou, the user could directly navigate to it which would be rather ugly and fail aas it would have nothing to show.
how can we show this page only when in context and forbid direct navigation to it?
You can create a partial form and append it to the DOM after the purchase event is fired. In the partial view, be sure to add a redirect action if it was accessed without the purchase event firing.
For just displaying short text (and not also e.g. the order data) you could just use a flash notice. For example:
redirect_to #page, notice: "Thank you for your order!"
Ajax
Sounds like you'll be best using ajax:
#app/views/cart/index.html.erb
<%= form_tag cart_order_path, remote: true do |f| %>
... (cart form)
<%= f.submit %>
<% end %>
This (obviously very simple) form will send a "remote" (Ajax) form submission to your controller. The reason why this is important is because you will then handle the response directly in that page you just sent the request from:
#app/assets/javascripts/application.js
$(document).on("ajax:success", "#your_form_id", function(status, data, xhr) {
$("your_page_div").html(data);
});
The trick here will be to render your thank you view without a layout -
#app/controllers/cart_controller.rb
class CartController < ApplicationController
respond_to :js, only: :create
def create
... business logic here
render "thank_you", layout: false
end
end
This will render the view you want without any of the supporting "layout" HTML - giving you the chance to append that to your current view. This means that if you wanted to show the "Thank You" view without letting the user browse to it directly - that's what you'll do
How It Works
Ajax (Asynchronous Javascript and XML) is a javascript technology which basically allows you to send "pseudo requests" to your Rails backend:
Basically the same as a standard HTTP request, except handled with Javascript, Ajax gives you the ability to create the appearance of "no refresh" functionality in your app. It does this by sending requests on your behalf, through Javascript.
Ajax is typically used for small pieces of functionality on web interfaces - whereby you'll have the ability to send simple requests to the server, gaining a comparable response that you can then work into the DOM.
This is the functionality I have been proposing - whereby you'll be able to send a request to your controller (albeit using the Rails UJS engine), to which you'll then receive a response. This response can then be worked into your page, thus providing you with the ability to show the "Thank You" page without the user refreshing.
You can use any solution from the following:
Using ActionDispatch::Flash:
flash[:notice] = "Thank you for your order!"
redirect_to #page
or
redirect_to #page, notice: "Thank you for your order!"
Using alert, in show.js.haml file (assuming you use action show in orders_controller.rb):
alert("Thank you for your order!");
and then add respond_to :js, only: :show, and format.js in action show for orders_controller.rb

ActionController::InvalidCrossOriginRequest exception due to bingbots

I have rails applications where I am loading comments using Ajax after page load.
class CommentsController < ApplicationController
respond_to :js
def index
#comments = Comments.all
respond_with #comments
end
end
It is working as expected. But bingbot is trying to access this url with which it leads to
An ActionController::InvalidCrossOriginRequest occurred in comments#index:
Security warning: an embedded tag on another site requested protected JavaScript. If you know what you're doing, go ahead and disable forgery protection on this action to permit cross-origin JavaScript embedding.
like that it is coming for all url's which are only responding to js format.
I know about rack-cors, but it is for allowing cross side script access, but here it is not.
app/views/comments/index.js.erb
$('.comments_container').html("<%=j render 'comments' %>");
comments.js
jQuery(function() {
return $.ajax({
url: $('.comments_container').data('url')({
dataType: "script"
})
});
});
Assuming you need some help with CORS(Cross-origin resource sharing), You are getting error because your CORS policy is default to "denying" every direct XHR access.
You can use the rack-cors gem https://github.com/cyu/rack-cors to avoid this. Hope this help!

How to implement redirect response with an AJAX request in Rails 3?

I have a simple scenario where I want to request a page. The request format is AJAX. If there is some error in the controller/action logic for that request, I want to redirect to an error page. The issue is that a redirect is not a JavaScript response type, so I am not sure whether it will work.
If there are no errors, then I want the page to be updated via the appropriate JavaScript response type.
What is best practice to achieve redirect responses given that the request format is AJAX?
This blog post enlightened me on what I think is the right way to do this, if your ajax response is ajax; at least, in the unobtrusive javascript paradigm. In essense, the ajax call always returns a standard json package, which can be parsed for information payload or a redirect url.
You can also put this in your ApplicationController to redirect properly for AJAX requests:
class ApplicationController < ActionController::Base
# Allows redirecting for AJAX calls as well as normal calls
def redirect_to(options = {}, response_status = {})
if request.xhr?
render(:update) {|page| page.redirect_to(options)}
else
super(options, response_status)
end
end
end
If you use jQuery, you can use .ajaxError()
$(document).ajaxError(function(event, request, settings){
location.href = '/error.html';
});
or assume you do a ajax post
var jqxhr = $.post("example.php", function() {
// Do what it completed will do
})
.error(function() {
location.href = '/error.html';
})

Is there a "rails way" to retry a POST after authenticating a user?

My user experience involves users submitting a form before they've authenticated (using omniauth). I started doing something like this:
def self.require_facebook_authentication!(options={})
before_filter :redirect_to_facebook_if_not_authenticated options
end
def redirect_to_facebook_if_not_authenticated
if !logged_in?
session[:param_cache] = params
session[:original_destination] = request.fullpath
redirect_to '/auth/facebook'
end
end
Then, on hitting the auth callback, redirect to a page that submits a form with the post params inline, for a total of 3 redirects (/stuff/new/ on POST -> auth/facebook -> facebook -> /auth/facebook/callback [ html template with POST form ] -> /stuff/create). I'd rather not create an authentication popup; instead, I'd like to navigate to a separate page, log in, and redirect to the completed action.
I'm fairly new to Rails, so I'm still learning - is this already built in to another framework? Am I missing something really basic? Thanks in advance!
if you are asking as to whether or not there is a "RAILS" way that will automatically post the data after a redirect, the answer is no (see https://stackoverflow.com/questions/985596/redirect-to-using-post-in-rails)
In my opinion the safest, easiest, and most RESTful way to achieve what you want would be to simply have the params you are eventually posting stored in session so that you can redirect back to the original 'new' page and have the form automatically prefilled with the post data. Sure this is one extra step for the user, but since REST doesn't allow for redirects to POSTs it is imo the cleanest way to go about it
There may be a better way, but if you render this after authenticating, then the client will Ajax post the form contents then redirect.
<script>
new Ajax.Request(<%= session[:original_destination] %>, {
method: 'post',
params: '<%= session[:param_cache].to_query %>',
onSuccess: function(){
window.location = '<%= session[:original_destination] %>';
}
});
</script>

Resources