maintain `new` in url after validation failed - ruby-on-rails

from the default scaffold generator I have the following create action in my blogs controller:
# POST /blogs
# POST /blogs.json
def create
#blog = Blog.new(params[:blog])
respond_to do |format|
if #blog.save
format.html { redirect_to #blog, notice: 'Blog was successfully created.' }
format.json { render json: #blog, status: :created, location: #blog }
else
format.html { render action: "new" }
format.json { render json: #blog.errors, status: :unprocessable_entity }
end
end
end
When the sent form contains errors, my browser is redirected to /blogs URL but in the page the new action is rendered.
This is really ugly in my opinion and (also to simplify my javascript) I would like the browser to remain in the same blogs/new URL.
I tried with changing redirect_to :new instead of render action: "new", but this of course loses the #blog data.
any clue on how to do this?
thanks,

If you want to keep new in your path you could redirect with params like so:
redirect_to new_blog_path(blog: params[:blog])
and then check for these params in blog#new

Related

Rails errors resets form params

This seems simple, but I can't find anything on it.
I have a link to request a meeting with another user. This produces a url like this:
http://localhost:3000/meetings/new?requestee_id=5
The requestee id, and other information in the form are passed to the MeetingController:
def create
requestor = current_user
#meeting_with_params = meeting_params
#meeting = Meeting.new(meeting_params)
respond_to do |format|
if #meeting.save
format.html { redirect_to home_url, notice: 'Your lex was successfully requested! Click Plan Meeting ' }
format.json { render :show, status: :created, location: #meeting }
else
format.html { render :new, params: #meeting_with_params }
format.json { render json: #meeting.errors, status: :unprocessable_entity }
end
end
end
If there are errors, the url now looks like this:
http://localhost:3000/meetings
which means the form will never submit since the requestee_id is not present.
What is right way to have the user see errors, but the url params are never reset?
Thanks!
You can do this:
format.html { render "path/to/new?{#meeting_with_params.to_param}" }
OR
You can use rails path helpers. You canrake routes and find your url's helper path (it looks something like this: edit_user_path, new_user_path)
format.html { render new_meeting_path(#meeting_with_params) }
As you are ok to use ajax for this checkout [https://github.com/rails/jquery-ujs](jquery-ujs), your form tag has to look something like this:
form_for #meeting, data: {remote: true} ...

Render and/or redirect called multiple times in action

I'm using Devise and Pundit.
To create a new profile page, the user has to be authorized to do so.
This has been working fine since I first implemented it, but today it just started acting up with an error message:
Render and/or redirect were called multiple times in this action.
Please note that you may only call render OR redirect, and at most
once per action. Also note that neither redirect nor render terminate
execution of the action, so if you want to exit an action after
redirecting, you need to do something like "redirect_to(...) and
return".
Here's my code from my Application controller:
rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
...
private
def user_not_authorized(exception)
flash[:alert] = "Sorry, you are not authorized to perform this action."
redirect_to(request.referrer || root_path)
end
Here's my ProfilePage controller:
def new
#profile_page = ProfilePage.new
authorize #profile_page
end
def create
#profile_page = ProfilePage.new(profile_page_params)
respond_to do |format|
if #profile_page.save
format.html { redirect_to #profile_page, notice: 'Profile page was successfully created.' }
format.json { render :show, status: :created, location: #profile_page }
else
format.html { render :new }
format.json { render json: #profile_page.errors, status: :unprocessable_entity }
end
end
authorize #profile_page
end
Someone suggested that I add this line of code below flash[:alert]:
self.response_body = nil
But now my user is redirected to the 'new profile' page again, rather than the successful profile page. It also tells the user that they are not authorized to complete this action, despite the fact that it HAS authorized them to do so.
In the create action you have to put the authorization logic before saving the record:
You have to move
authorize #profile_page
at the top of your create action, after initializing the #profile_page, like so:
def create
#profile_page = ProfilePage.new(profile_page_params)
authorize #profile_page
respond_to do |format|
if #profile_page.save
format.html { redirect_to #profile_page, notice: 'Profile page was successfully created.' }
format.json { render :show, status: :created, location: #profile_page }
else
format.html { render :new }
format.json { render json: #profile_page.errors, status: :unprocessable_entity }
end
end
end

How do I display error messages in the same URL as the form URL in Rails forms?

(I've broken out the 2nd question that originally was part of this post into a separate post)
I am creating a product landing page with Rails in which users can enter their email address to be notified when the product launches. (Yes, there are services/gems etc that could do this for me, but I am new to programming and want to build it myself to learn rails.)
On submit of the form, if there are errors, the app currently redirects to '/invites' I would like to instead display error messages on the same page/URL as the original form? (In my case, the form is located at root while the error messages are displaying at '/invites')
I have read the Rails Guide on Routes and numerous stackoverflow posts on handling form errors nothing I've found seems to answer the question I have.
Update: Based on the reply from #rovermicrover I would like to clarify that, while I'm open to an Ajax solution, I'm fine with a page refresh that displays the error message. (I was not able to get the recommendation by #rovermicrover to function as desired - see my response to that solution below for more details.)
What I did:
Invite model:
class Invite < ActiveRecord::Base
attr_accessible :email
validates :email, :presence => {:message => "Please enter an email address."}
end
My routes file:
SuggestionBoxApp::Application.routes.draw do
root to: 'invites#new'
resources :invites
end
This is what I have in the Invites controller (I've only included the actions I'm referencing: new, create, show - it's basically the default of what Rails might generate):
class InvitesController < ApplicationController
def show
#invite = Invite.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #invite }
end
end
def new
#invite = Invite.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #invite }
end
end
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
format.html { redirect_to #invite }
format.json { render json: #invite, status: :created, location: #invite }
else
format.html { render action: "new" }
format.json { render json: #invite.errors, status: :unprocessable_entity }
end
end
end
end
Please let me know if there is any additional info I can provide in helping to answer this question. Thanks!
Make the form 'remote'
form_for #invite, :remote => true
....
Then in the controller
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
format.html { redirect_to #invite }
format.js { render :action => 'create_suc'}
else
format.html { render action: "new" }
format.js { render :action => 'create_fail' }
end
end
end
/invites/create_suc.js.erb
$('#errors').remove()
$('#new_invite').prepend("<div class='Thanks'>Thanks for signing up</div>")
$('#new_invite').hide("")
/invites/create_fail.js.erb
$('#new_invite').html('<%= escape_javascript render("form", :invite => #invite) %>');
Forms is a partial with your.... form in it, and also the handling of all errors on #invite.
There is a way to do this without resorting the making the form submit "remote", from a pure Ruby on Rails perspective. However, you can do this only if the browser has enabled cookies.
The idea is to save the form data in the session information in case of an error.
Just remember to delete the session data in case of success.
def new
#invite = Invite.new(session[:invite])
respond_to do |format|
format.html # new.html.erb
format.json { render json: #invite }
end
end
def create
#invite = Invite.new(params[:invite])
respond_to do |format|
if #invite.save
session.delete(:invite)
format.html { redirect_to #invite }
format.json { render json: #invite, status: :created, location: #invite }
else
session[:invite] = params[:invite]
format.html { render action: "new" }
format.json { render json: #invite.errors, status: :unprocessable_entity }
end
end
end

Rendering a partial from a controller in rails

I have a form that is adding rows to the DB via remote => true. I then want to append the new data to a table, but cannot get the correct view to render.
As of now, it is rendering the entire show.html.erb page for the new entry, but I want to layout a minimal version to be added as a . Is there a quick way to tell my controller what view to render after inserting into the db? I want to render my partial named _newly_added.html.erb
My Controller
def new
#task = Task.new
render :partial => "/tasks/newly_added", :locals => { :t => #task }
end
Thanks!!
EDIT
I think what I need is just an alternative "show" view.
I found that the method I needed to change was actually this:
def create
#task = Task.new(params[:task])
respond_to do |format|
if #task.save
format.html { redirect_to #task, notice: 'Task was successfully created.' }
format.json { render json: #task, status: :created, location: #task }
else
format.html { render action: "new" }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
I just need to make an alternative show view, and then tell this to redirect_to that view.
Edited per the changes in your question. However, nothing really changes. You're thinking about things wrong, and need to adjust how you're thinking. You don't need an alternative show, you need to handle the format.js request.
The partial should be rendered within a JavaScript response, not the controller. The controller looks more like this:
def create
#task = Task.new(params[:task])
respond_to do |format|
if #task.save
format.html { redirect_to #task, notice: 'Task was successfully created.' }
format.json { render json: #task, status: :created, location: #task }
format.js
else
format.html { render action: "new" }
format.json { render json: #task.errors, status: :unprocessable_entity }
format.js
end
end
end
Then, in views/tasks/create.js.coffee
($ '#mytable').append("<%= j render(partial: 'tasks/newly_added', locals: { t: #task }) %>")
What's going on here is that the browser makes a call to create.js. The controller responds with the create.js template, because of the respond_to block's format.js. The j escapes the contents of the _newly_added.html.erb file, and the contents of it are appended to the table. The controller doesn't interact with the existing view, instead, JavaScript is sent to the browser, and it interacts with the view.
This all changes somewhat if you're using a client-side MVC framework like Backbone or Ember, but you didn't specify that so I'm assuming you're going with stock Rails.

Ruby on rails: What's the difference between respond_to and respond_with?

What's the difference between respond_to and respond_with ?
What do they do?
Can anyone post example with the screenshot of output?
Thanks.
There is a pretty complete answer here. Essentially respond_with does the same thing as respond_to but makes your code a bit cleaner. It is only available in rails 3 I think
Both respond_to and respond_with does the same work, but respond_with tends to make code a bit simple,
Here in this example,
def create
#task = Task.new(task_params)
respond_to do |format|
if #task.save
format.html { redirect_to #task, notice: 'Task was successfully created.' }
format.json { render :show, status: :created, location: #task }
else
format.html { render :new }
format.json { render json: #task.errors, status: :unprocessable_entity }
end
end
end
The same code using respond_with ,
def create
#task = Task.new(task_params)
flash[:notice] = "Task was successfully created." if #task.save
respond_with(#task)
end
also you need to mention the formats in your controller as:
respond_to :html,:json,:xml
When we pass #taskto respond_with, it will actually check if the object is valid? first. If the object is not valid, then it will call render :new when in a create or render :edit when in an update.
If the object is valid, it will automatically redirect to the show action for that object.
Maybe you would rather redirect to the index after successful creation. You can override the redirect by adding the :location option to respond_with:
def create
#task = Task.new(task_params)
flash[:notice] = #task.save ? "Your task was created." : "Task failed to save."
respond_with #task, location: task_path
end
For more information visit this Blog

Resources