Render a new view upon json - ruby-on-rails

I am trying to build something really simple but I am bit confused. I have a customer controller with 2 actions, index and identification. The views of these actions simply display an image each. What I want to do is be able to change from the index view to the identification view when an ios app sends a simple json get request (it could be post, doesn"t really matter as I only want to change the view). My customer_controller.rb is
def index
respond_to do |format|
format.html # index.html.erb
format.json {render 'identification'}
end
end
def identification
#date = Time.now.strftime("%A, %d %B %Y")
#time = Time.now.strftime("%H:%M %p")
end
My routes.rb is
get "customer/index"
post "customer/index"
get "customer/identification"
Can anyone point me to the right direction as to what I am doing wrong? Whenever the ios app sends a json nothing happens.

When you call render 'identification' that only tells rails to use the 'identification' template, it does not actually call the #identification method.

Related

Ruby on Rails 6. Using Ajax after redirection

Say, I have users list on the '/users' page and 2 actions for the 'user' entity: 'index' (with using of Ajax) and 'destroy'.
def index
...
respond_to do |format|
format.html
format.js
end
end
def destroy
...
redirect_to users_url
end
I want to destroy a user (right from the '/users' page) and use Ajax of the 'index' action after that ('index.js.erb' file) in order to render only a part of the opened '/users' page.
Is it possible to do that?
My current solution right now is to use Ajax for 'destroy' action (a separate 'destroy.js.erb' file) and duplicate needed changes for 'index' page there. But, first of all, it's a code duplication, and second, in this case my pagination links are broken (I use 'Kaminari' gem and looks like it works fine only with 'get' requests, at least by default).
There is a 'view' part of updating with Ajax, if necessary:
<div id="users_table">
<table class="table table-hover table-borderless">
...
<tbody>
<%= render #users %>
</tbody>
</table>
<div><%= paginate #users, remote: true %></div>
</div>
If you want the destroy action to render the index.js.erb:
def destroy
...
respond_to do |format|
format.js { render action: :index}
format.html { redirect_to users_url}
end
end
But, to render index.js you will need to, in your destroy action, rebuild the #users object and ensure you're rebuilding it for the correct page. So, when you call the destroy action you'll need to pass the ID(s) of the user(s) you want to destroy, as well as the page you are on.
Your destroy.js.erb should (on successful destruction) remove the destroyed element from the index by deleting a part of the HTML. I don’t expect that the code to do that duplicates the code you have in the index view.
Post your current destroy.js.erb as well as the relevant part of index.html.erb for more help though.
You can also use redirect within a respond_to so your HTML call will redirect while the Ajax uses destroy.js.erb
respond_to do |format|
format.js
format.html { redirect_to users_url}
You could also hack your way to your answer by calling render :index for the js response. But, if you were to try to render the index view here you’ll definitely get duplication of code, along with an extra DB call and probably some broken pagination. So, I’d recommend that you take the approach I first suggested (use destroy.js.erb to remove that user from the HTML)
Finally, more generally, when you’re trying to avoid duplication of view code; a partial might be the answer

React + Rails routes configuration

I'm trying to build a little todo app with rails 4 and react. Nothing really hard, it's just a beginning to learn how to use react with rails, but I have difficulties on how to code my rails controllers.
The two urls reachable at the moment are "mysite.local" to display all the todos, and to "mysite.local/todos/1" to display a specific todo with more details.
I have a Todos controller that I use to respond to the AJAX calls related to the todos.
The view that contains the react app is not a "Todos" view because I will add more than just todos in the futur and the react app will have to handle everything.
What I want to do is simple: I want rails to always render the view with the react app regardless of the url used to reach the website. All the controllers in my app (like the Todos controller) are just used to retrieve JSON datas that will be manipulated by react.
The solution I found is to put the react app in the application layout. That way, it will always be present when someone go on the website for the first time. Since it's in the layout it will not be rendered again, the react app can do its job.
My controllers then looks like this:
class TodosController < ApplicationController
def index
respond_to do |format|
format.html
format.json { Todo.all }
end
end
def create
respond_to do |format|
format.html
format.json { Todo.create(todo_params) }
end
end
def show
respond_to do |format|
format.html
format.json { Todo.find(params[:id]) }
end
end
private
def todo_params
params.require(:todo).permit(:content)
end
end
With a controller like this, I can make my AJAX calls and get a JSON, which is the only datas my controller will send, and at the same time I can still reach "mysite.local/todos/1" and rails will do nothing except rendering an empty view.
This method works, but I don't like it because the controller render a view for each action. When someone reach the website for the first time, an empty view will be rendered. Even if the view is empty and it will not display anything on the browser, rails still have to do all the process to render the view. I don't know if this process really cost something, but I don't like the fact that my application is doing something useless.
Is there any way to tell rails to literally do nothing if the format action is html? (= not rendering the "index", or "show" view?)
Or is there a better way to do what I want to do?
Thanks
EDIT
Thanks to gobluego, I modified my application a little. I created a Front controller to handle the client part. Then I moved my Todos controller in an api folder.
Here is my routes.rb file now:
root "front#index"
namespace :api, constaints: { format: 'json' } do
resources :todos, only: [:index, :create, :delete, :show]
end
get '*path' => "front#index", via: :all
and my new Todos controller:
class Api::TodosController < ApplicationController
respond_to :json
before_action :ensure_json_request
def index
respond_with Todo.all
end
def create
respond_with Todo.create(todo_params)
end
def show
respond_with Todo.find(params[:id])
end
private
def todo_params
params.require(:todo).permit(:content)
end
def ensure_json_request
return if request.format == :json
render :nothing => true, :status => 406
end
end
That way, any url is handled by front#index, except all the apis urls, which is what I want. To ensure that nothing is rendered if, for example, someone tries to reach mysite.local/api/todos in the browser, a before_action is used and it render nothing if the format is not json.
Why not use pages#index instead as the root path? That way, TodosController is only responsible for being the back end service for your app.
As an additional measure you can enforce that both requests and responses are only of JSON format. As for the implementation of that, this is a a good starting point.

Allow method to accept an :id param

I have created a method called verify in a controller (events_controller.rb), and I want to allow that page (verify.html.erb) to accept an object (#event), and show of that objects parameters. I'm creating a show page in essence, but I need to build some special logic into this page that I don't want to build into the show page. I have created the route, but I still get an error when I tell it to find an Event by params[:id]. The actual url it is going to is /verify.(event :id) and I believe it should be routing to events/verify/(event :id).
My error
Couldn't find Event without an ID.
routes.rb
get "verify", to: 'events#verify'
resources :events
events_controller.rb
def verify
#event = Event.find(params[:id])
respond_to do |format|
format.html # verify.html.erb
format.json { render json: #event }
end
end
Thanks Stack!
get "verify/:id", to: 'events#verify', as: "verify"
in browser go to url for example:
localhost:3000/verify/1

Redirect to after successful ajax form

I've got a form with remote => true.
And right now my controller looks like:
# POST /items
# POST /items.json
def create
#item = #store.items.build(params[:item])
respond_to do |format|
if #item.save
format.html { redirect_to edit_admin_item_path(#item), :flash => {:success => "#{#item.name} was successfully created."} }
format.js { render :js => "window.location.href = ('#{edit_admin_item_path(#item)}');"}
format.json { render json: #item, status: :created, location: #item }
else
format.html { render action: "new" }
format.js { render :partial => 'fail_create.js.erb', :locals => { :ajax_errors => #item.errors.full_messages } }
format.json { render json: #item.errors, status: :unprocessable_entity }
end
end
end
Which works but feels very clumsy. It also doesn't allow me to use a flash notice, which is sad time indeed.
Ideally I feel like I should be able to simply use "format.js { redirect_to...} or check against the request headers and redirect_to. Sheesh!
I'm not sure what the best solution is. Any advice would be super awesome, thanks in advance!
-- PS -- I know this has been asked somewhat before but to no avail: How to redirect after a successful AJAX form submission. There seems to many questions similar floating around, but no real solutions.
I think it might be impossible. The response to a Ajax request is processed by XMLHttpRequest. If a 3xx response is returned, XMLHttpRequest will follow the redirect itself, if the URL is of same origin. No matter how you set the headers, the browser cannot be aware of that. So the only way could be changing window.location with some Javascript.
I use a combination of Rails responders to generate my response messages and some content in my <action>.js file.
The content of — say update.js would look something like this:
// Checks if the article slug has changed.
// If it has the entire page should be reloaded at that new location.
<%= reload_if_slug_changed #article, params[:id] %>
// Displays the flash notices
// See ApplicationHelper#js_flash_response
<%= js_flash_response %>
Where the different methods are defined in some helper (in my case my ApplicationHelper). The content of the different methods are as follows:
def js_flash_response
if flash.now[:notice].present?
js = "$('#notice').html('#{flash.now[:notice]}').change();"
elsif flash.now[:alert].present?
js = "$('#alert').html('#{flash.now[:alert]}').change();"
end
end
def reload_if_slug_changed object, expected_value
"window.location.href = '#{url_for [:edit, object]}';" if object.slug != expected_value
end
The content of the flash messages are generated automatically by Rails responders and displayed with the now scope that deletes the from the flash hash, ensuring that if the user reloads (after the flash has been displayed) they will not reappear.
I don't believe that you should ever make a form pointing to a restful create action a remote one, because you would always expect critical redirect, so in my case I only need to redirect if the url slug has changed.
I hope that this helps. It's not a solution, but simply the way that I handled some of the same problems.
Best regards.
Under your scenario, here's how I would inject javascript into the page from a controller action. After you've completed the logic section of your action insert something like this:
render :update do |page|
page << "javascript_here"
end
This should allow you to insert you window.location or create a javascript flash method and call it when your create method executes correctly.
If you're looking to DRY up your controller actions, I would recommend looking into this Railscast about make_resourceful. Make_resourceful automagically performs each core activity for each action. It also allows you to tap into the hooks that they've created such as before :create, after :create, response_for :create, and after :create_fails. By using this gem, you can run code based on the success or failure of your methods and have finer grained control over them.
In addition to this, you should be able to initialize a create.js.erb and create_fails.js.erb in your view file, include a format.js without anything passed to it in your controller, and Rails will automagically run that file that contains javascript depending on if the controller action executed successfully.

Showing Post + Poster's Info on the "Index" Page

I want to show a post along with the poster's info on my rails app. Right now I'm able to show the post and user association on the "show" page (the page for a single post), but when I want to show it on the "index" page (where all of my posts are), I get this error: undefined method `username' for nil:NilClass
I added #post.user = current_user to my post_controller under "show" (That allowed me to show the poster's info. But i dont know what to add to the "def index".
This is what I have there:
def index
#posts = Post.all
respond_to do |format|
format.html # index.html.erb
format.xml { render :xml => #posts }
end
end
This is what I have under "show"
def show
#post = Post.find(params[:id])
#post.user = current_user
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #post }
end
end
I want to know what to add to my def index to be able to display the poster's info. Thanks in advance.
Your set up is wrong. The user needs to be added to the post when it is created and updated.
As you have it now, all posts will always belong to the user that is currently viewing the post because you are assigning the current_user to the post in the show action (and would be doing the same in the index view if you continued with this approach) meaning that when post 1 is viewed by user 1 it would show as belonging to user 1. When post 1 is shown by user 2 it would belong to user 2.
The solution is to assign the current user to the post in the update and create actions of the posts controller exactly as you have it now in the show action but before the post gets physically updated or created.
Then remove the #post.user = current_user from the show action.
This may mean you are left with legacy data in your app that doesn't have a user assigned. But that's o.k., just edit and save each post and it will have you attached automatically.
Then in the views/posts/index_html.erb just add the user details that you want to see in new table row columns before the show/edit links. This is assuming you have a standard scaffolded index.html.erb file. If you don't have a standard index view then put it wherever you want it but then if you had a customised index view you probably wouldn't be asking this question in the first place so forget about that and you'd know where it goes
Update
I did explain how to show the user in the views in my response above but possibly I need to be a little clearer so I'll show you the code.
To show the user name in the show view use
<%= #post.user.name unless #product.user.blank? %>
To show the user in the index action for the post use
<% #posts.each do |post| %> <!-- This is already in your index action. -->
<%=post.user.name unless post.user.blank?%><!-- This is the code you need -->
<!-- You might want to add a new th in the header for the css table then you can add a <td></td> tags round the above line so your table all matches up nicely for display purposes-->
<%end%> <!-- This is already in your index action -->
Hope that helps

Resources