Rails + jQuery SPA Routing - ruby-on-rails

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.

Related

How to properly get an AJAX request to React from 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

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.

How do you respond using a one-line piece of javascript in a Ruby on Rails controller action?

I haven't been able to find any useful resources online on how to do this. Basically what I'm trying to do is run a simple jQuery $('#test-div').show(); when my def show_div controller action is complete.
I've tried the following and it doesn't work. It actually renders HTML which is confusing to me. when I explicitly state that the method respond with js.
users_controller.rb
def show_div
#user = User.first
respond_to do |format|
format.js {}
end
# also tried
# render :js => "$('#test-div').show();"
end
show_div.js.erb
$('#test-div').show();
render :text should do what you are asking for -- just return raw text (which in your case happens to be JavaScript code) without doing anything to it.

What options do you have in Rails in creating a Restful API?

I want people to be able to perform operations in ruby scripts using an API.
What options do you have in rails?
What if I want certain operations to be authenticated?
You can make any resource RESTful in rails by using resources in the routes.rb file. So if you have a table called items, then you could go resources :items. This will automagically make your controller accept any of the seven standard RESTful actions, new, create, index, etc.
Rails can deal with lots of different formats out of the box, html, json, xml, etc. So if you want someone to search your items, the code would look something like this when you go to a url that looks something like mydomain.com/items.json?search_term=HelloWorld:
class ItemsController < ApplicationController
def index
#item = Item.find_by_name(params[:search_term])
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #item}
format.json { render :json => #item } # this will get returned
end
end
end
If you want operations to be authenticated, then you could use something like the omniauth gem to do OAuth authentication via facebook, or something like that. You'll need some sort of library on your client side to manage the session as well, unless you recommend doing something icky like passing your user/pass in the URL.

Redirecting to a 500 page when an AJAX call fails in Ruby on Rails

I'm working with an application built in Ruby on Rails with very poor error handling right now. If a controller method is executed via ajax, and that method results in a 500 (or 404 or any other response) the 500.html page is rendered and returned as the result to the AJAX request. Obviously the javascript doesn't know what to do with that HTML and the web page looks like it's just waiting for a response.
Is there an easy way in rails to render an error.rjs template anytime an error occurs during an AJAX call?
You can use respond_to inside a controller's rescue_action or rescue_action_in_public method. Consider the following controller:
class DefaultController < ApplicationController
def index
raise "FAIL"
end
def rescue_action(exception)
respond_to do |format|
format.html { render :text => "Rescued HTML" }
format.js { render :action => "errors" }
end
end
end
I solved a similar problem with authorization. I created a simple authorization controller with this action:
def unauthorizedxhr
render :update do |page|
page.replace_html("notice", :partial=>"unauthorizedxhr")
page.show("notice")
end
end
Here's the template:
<% if flash[:notice] -%>
<div id="noticexhr"><%= flash[:notice] %></div>
<% end -%>
When the authorization failed in the controller, I'd redirect to the :controller=>"authorization", :action=>"unauthorizedxhr" after setting the flash[:notice] value. This allowed me to customize the message I sent to the user, and it handled the display of the message through the render :update code above.
You could adapt this to your problem by creating an errors controller, catching any raised errors in your other controllers, then simply redirecting to :controller=>"errors", :action=>"displayxhr" when the call fails. That way, you'll have standardized your error communication mechanism but allowed yourself the ability to customize error messages by each action.
You can still use cpm's idea above, but the error's display will be handled by separate and distinct controller logic. that should make it a little easier to maintain.
Hope that helps.
-Chris
This was my final solution:
def rescue_action_in_public(exception)
response_code = response_code_for_rescue(exception)
status = interpret_status(response_code)
respond_to do |format|
format.html { render_optional_error_file response_code}
format.js { render :update, :status => status do |page| page.redirect_to(:url => error_page_url(status)) end}
end
end
This basically forwards to the correct static HTML page no matter if the request was via AJAX or normal GET/POST.
This is NOT used for normal error handling like validation etc. It's only used when something really bad happens - like an unhandled exception.
You can do like below :
in allpication.js
$(document).ready(function(){
$(document).ajaxError( function(e, xhr, options){
if("500" == xhr.status)
{
$(location).attr('href','/users/sign_in');
}
});
})
Its working for me.....

Resources