How to properly get an AJAX request to React from Rails - ruby-on-rails

I'm fairly new to Rails, although I generally understand the structure/how it works. Side-note, my goal here is to have 1 javascript import tag in a views/home/application.html.erb that will import a single React file, which will then import the rest of my components in a tree-like structure. I'm used to this method because of my experience with Express so please let me know if this is not the standard way of doing things with Rails.
The current issue I'm facing however is that I can't/don't know how to get my Rails controller to send my instance variable as a response to my axios request. I'm expecting my browser to console.log('TESTING MY RAILS ROUTE'), but rather it's logging the entire HTML response.
Index.jsx (in javascript/components)
import React from "react"
import ReactDOM from 'react-dom'
import axios from 'axios'
class Index extends React.Component {
componentDidMount() {
axios.get('/login')
.then(res => {
console.log(res.data)
})
}
render() {
return (
<div>
<h1>Testing Axios</h1>
</div>
)
}
}
export default Index
config/routes.rb
Rails.application.routes.draw do
get 'welcome/index'
resources :login
root 'welcome#index'
end
controllers/login_controller.rb
class LoginController < ApplicationController
def index
#test = 'TESTING MY RAILS ROUTE'
end
end
index.html.erb (in views/welcome)
<%= react_component('Index') %>

You need to configure your Rails controller to respond with json as it is currently responding with HTML type response.
Since you are using React with Rails, I'm assuming that you want your Rails API to always return JSON for React to render.
Using the responders gem can help simplify this implementation, and have less code in your controllers.
Add gem 'responders' to your Gemfile.
Also, identify the login resource in your routes.db to be json by default, like so:
resources :login, defaults: {format: :json}
Now apply this change at the controller level to always respond with JSON. Add respond_to :json at the top of your controller. Then use respond_with in your controller actions. respond_with will render json in all actions of your controller, as long as respond_to :json is defined at the top.
class LoginController < ApplicationController
respond_to :json
def index
#test = 'TESTING MY RAILS ROUTE'
respond_with #test
end
end
Alternatively, you can also respond with json for an individual controller action only, by using respond_to within the controller action.

Rails has a convention that you need to choose the format to respond to if it's not the default. By default, Rails controller actions respond to html requests.
You should be able to configure this controller action to respond to both html and Ajax requests by adding the following code.
def index
#test = 'TESTING MY RAILS ROUTE'
respond_to do |format|
format.js json: #test, status: :ok
format.html
end
end

Related

Organizing and routing AJAX calls in Ruby on Rails

I am using Rails 4 and I have played with AJAX. I have successfully got it to work. The page that have Ajax links on it is the show.html.erb. It has a feed which have posts, and the posts has comments. To avoid loading everything I use Ajax for loading pieces of the post list and the comment list. Much like Facebook does.
I have organised it the following way: the pages controller checks if there is an ajax_action query parameter present. If it does it renders the JavaScript-response. It checks with a before_action. The value of the ajax_action are load_posts, load_comments and load_replies. They map to private methods in the pages controller.
Does this sound like a good approach when I need different Ajax methods on the same page? Or do you do it another way?
code from the pages controller:
before_action :delegate_ajax, only: :show
def delegate_ajax
ajax_action = params[:ajax_action]
unless ajax_action.nil?
ajax_handlers = {'load_posts' => 'load_posts', 'load_comments' => 'load_comments'}
send(ajax_handlers[ajax_action])
end
end
def load_posts
# Some logic (hidden for now)
respond_to do |format|
format.js { render 'load_posts', layout: false }
end
end
def load_comments
# Some logic (hidden for now)
respond_to do |format|
format.js { render 'load_comments', layout: false }
end
end
At least doing it like this keeps the routes.rb oblivious to all the AJAX routes.

Rails + jQuery SPA Routing

To avoid diving into a front-end framework for the time being, I'm building a single page application using only Rails and jQuery.
Here's the basic structure:
controller
class MainController < ApplicationController
def index
respond_to do |format|
format.html
end
end
def complex_query_1
#items = # complex query
respond_to do |format|
format.js { render 'refresh.js.erb', :locals => { :items => #items } }
end
end
def complex_query_2
#items = # a different complex query
respond_to do |format|
format.js { render 'refresh.js.erb', :locals => { :items => #items } }
end
end
end
application.js
$(document).ready(function() {
showActivityIndicator();
$.get(Routes.complex_query_1_path());
});
When a user first visits the app, they are shown #items from complex_query_1. To avoid a long time for the query to run, I've implemented a more "SPA" style of loading:
When the user visits the app, the index action is fired and serves the base layout.
Then a loading indicator and POST request for complex_query_1 are fired via js.
The action responds to js and refreshes the view via jQuery.
All internal links are AJAX so the base layout is never reloaded, preserving the "SPA" feel.
I've hit a roadblock with routing from outside the app. For example, if I navigate to /complex_query_2 from outside the app, no html layout is served. I could specify the format.html layout, but then the long query has to load before the html is served.
Is there an alternative/better way to structure the app for an SPA feel?
One solution some apps use is using fragments in the url to determine how the application should respond.
So your external link should be
/index#complex_query_2
The index path would provide the html layout, and you can parse the url in javascript to get the complex_query_2 part. Then use this to determine what action should be taken.

rails and ajax request

I have started to learn rails and javascript.
How i canmake properly ajax request to rails controller with jquery?
I write in js file
$.get('http://localhost:3000/take/show',{},function(s){
alert(s);
},'text');
in controller:
class FirstController < ApplicationController
def show
render :json => "Hi!"
end
end
but instead i see only blank alert dialog. What i have done wrong? In all tutorials i see thatthe URL in $.get should be like this "take/show" but it would not work in my case, so why?

Ruby - writing a basic controller

I am trying to learn Ruby on Rails and trying to write some of the code by hand so that I learn how it works.
I made this tiny controller:
class TestsController < ApplicationController
def test
def show
render :text => "Hi from TestsController!"
end
end
end
and this is what is left of my view:
<h3> Hello test </h3>
and this is my routes.rb snippet:
resource :test
but it gives an error that: The action 'show' could not be found for TestsController
Thanks!
This is the output of rake routes:
home_index GET /home/index(.:format) home#index
root / home#index
test POST /test(.:format) tests#create
new_test GET /test/new(.:format) tests#new
edit_test GET /test/edit(.:format) tests#edit
GET /test(.:format) tests#show
PUT /test(.:format) tests#update
DELETE /test(.:format) tests#destroy
A basic controller looks like this:
class TestsController < ApplicationController
def show
end
end
You do not need the respond_to block if you only want to render the default view (in this case: app/views/tests/show.html.erb). The respond_to block is when you have some more advanced needs.
What #klump said is correct. Try running a basic scaffold. This will generate a controller, model and views for you. This generator is great when you are learning rails.
rails g scaffold Test
Also check out http://www.railsforzombies.com as it is a great way to learn rails.
You use respond_to when you want your action to respond to multiple formats. Client sets it's desired format in HTTP Accept header.
You can then specify different action for each format.
Example
def show
respond_to do |format|
format.html { Rails.logger.debug "rendering show.html" }
format.xml { Rails.logger.debug "rendering show.xml" }
format.js { Rails.logger.debug "rendering show.js" }
end
end
Refer to the API for more examples.

How can I debug/test a json response from my rails controller?

I have written a simple jquery response in my rails 3 controller, I just want to test to see the result this returns (not automated testing).
My code looks like this:
class AppointmentsController < ApplicationController
def jsonlist
#appointments = Appointment.find(:all)
render :json => #appointments
end
Can I access this from a URL like: http://localhost:3000/appointments/jsonlist?
When I try, I get this error:
Couldn't find Appointment with
ID=jsonlist
The error you're getting does not appear to come from that action, it appears to be a conflict in your routes for whatever you have defined as your show action. Try defining your route for jsonlist before you define your route for show.
to debug a json response from your controller
render json: JSON.pretty_generate(JSON.parse(#appointments.to_json))

Resources