Finding the render route if validation fails in Rails - ruby-on-rails

I am trying to create a basic survey tool as my first Rails project.
At the moment I am working on the validation for submitting a new answer to a survey question. The following is my answer model.
class Answer < ApplicationRecord
belongs_to :question
belongs_to :participant
validates :text, presence: true,
length: { minimum: 5, maximum: 100 }
end
I have set up an if statement that takes you to the next question if passes validation. My problem is I'm not sure what to render inside the else for this statement.
For similar validation in other controllers I have written the render statement to be the pages URL. For example: View all questions + add new question are rendered on the studies/id page. So if question validation fails then render will be 'studies/show'.
The URL to add a new answer looks like this.
http://localhost:3000/studies/20/questions/47/answers/new
For more context here is some of my code:
*Answers Controller*
class AnswersController < ApplicationController
def new
#study = Study.find(params[:study_id])
#question = #study.questions.find(params[:question_id])
#participant = find_participant
#answer = #question.answers.build(participant: #participant)
end
def create
#study = Study.find(params[:study_id])
#question = #study.questions.find(params[:question_id])
#answer = #question.answers.build(answer_params)
if #answer.save
next_question = #question.next_question
redirect_to next_question_path(next_question, #answer) if next_question.present?
else
#I want to render the current page the participant is on to display errors here.
end
end
***some private functions here***
end
* New Answer View *
<div class="wrap">
<h1 class="med-header"><%= #question.question %></h1>
<%= form_with model: #answer, url: study_question_answers_path(#study, #question), local: true do |form| %>
<%= form.hidden_field :participant_id %>
<% #question.answers.each do |answer| %>
<% if answer.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(answer.errors.count, "error") %> prohibited
this answer from being saved:
</h2>
<ul>
<% answer.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% end %>
<%= form.text_area :text %><br>
<%= form.submit %>
<% end %>
</div>
What do I render in the else?

create generates the object and saves. new only generates the object.
If #answer.save fails in this situation,
we need to show the form back to the user. format.html { render :new }
If use JSON you need show errors format.json { render json: #anwser.errors, status: :unprocessable_entity }
Reference : How CURD Work

The cheap way of doing this:
if #answer.save
next_question = #question.next_question
redirect_to next_question_path(next_question, #answer) if next_question.present?
else
flash[:notice] = #answer.errors.full_messages.first
render :new
end
render :new will render the new view as you can infer.
Anecdotally, your next_question_path(next_question, #answer) if next_question.present? is an unlocked gate if there's no next_question :)

Related

Can't get a custom edit form to save

I want to have a form to just edit one field for my user's model that is separate from the scaffold generated _form.erb.
The form will show but it will not save. When I modify the def in the controller with a respond_to block, the form is bypassed and I just get the record shown.
employee_supervisor_edit.html.erb has <%= render 'employee_supervisor_form' %>
routes.rb contains match '/employee_supervisor_edit/:id' => 'users#employee_supervisor_edit' , via: [:get, :post ]
the form is _employee_supervisor_form.erb
users_controller.rb has
def employee_supervisor_edit
#users = User.all
#user = User.find(params[:id])
respond_to do |format|
if #user.update(user_params)
format.html { redirect_to #user, notice: 'User was successfully updated.' }
format.json { render :show, status: :ok, location: #user }
else
format.html { render :edit }
format.json { render json: #user.errors, status: :unprocessable_entity }
end
end
end
I also have have the following in my users controller.
def user_params
# params.require(:user).permit(:login,
params.permit(:login,
:group_strings,
:name,
:ou_strings,
:email,
:active_employee,
:last_name,
:first_name,
:is_supervisor,
:#supervisor_id)
end
end
If I comment out the whole respond_to block, the form appears but no data is saved. If I put the respond_to block in, then the form is bypassed and it goes right to the show method.
I'm not sure if the problem is related to getting the following error if I use params.require(:user).permit(:login, instead of params.permit(:login,
ActionController::ParameterMissing in UsersController#employee_supervisor_edit
param is missing or the value is empty: user
Rails.root: C:/Users/cmendla/RubymineProjects/employee_observations
Application Trace | Framework Trace | Full Trace
app/controllers/users_controller.rb:134:in `user_params'
app/controllers/users_controller.rb:16:in `block in employee_supervisor_edit'
app/controllers/users_controller.rb:15:in `employee_supervisor_edit'
========== added ==============
I have the following associations in my user.rb
Class User < ActiveRecord::Base
has_many :subordinates, class_name: "User", foreign_key: "supervisor_id"
belongs_to :supervisor, class_name: "User"
======== added : =====================
<%= form_for(#user) do |f| %>
<% if #user.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#user.errors.count, "error") %> prohibited this user from being saved:</h2>
<ul>
<% #user.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
User Login: <%= #user.login %><br>
User Name: <%= #user.name %> <br>
<div class="field">
<%= f.label :active_employee %>
<%= f.check_box :active_employee %>
</div>
<div class="field">
<%= f.label :supervisor %>
<%= f.collection_select(:supervisor_id, User.order('name'), :id, :name, prompt: true)%>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The problem stemmed from my data. Since I am in the development process, I don't have all the error checking I need in place. Null fields or associations pointing to non existent records cause errors where it isn't always obvious (at least to me) that the problem is the data, not the code itself.
I went in with an sql editor and made sure that the contents causing the issues were not set to null and that the columns such as supervisor_id were pointing to actual existing records, not records that were deleted.
I changed params.permit(:login, back to params.require(:user).permit(:login, and now the form is saving as expected.
My next step will be to add validations for input and some error checking for the index and show methods. For the long term, I think that I need to become more proficient with testing as that might show areas that could cause these types of issues.

Errors within form not displaying in view

I am trying to display errors in my view when a form is not submitted correctly. I have a validation set in my model for location to be present and in my form I am using the errors method to try and display the errors in my view. Below is my code. The validation is working, because I get a rails error when location is nil, it's just not displaying the msg as html.
model
class Destination < ActiveRecord::Base
validates :location, presence: true
end
form new.html.erb
<%= form_for #destination do |f| %>
<% if #destination.errors.any? %>
<% #destination.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
<% end %>
<%= f.label :location %>
<%= f.text_field :location %><br>
<%= f.submit %>
<% end %>
controller
def create
#destination = Destination.new(destination_params)
if #destination.save!
redirect_to destinations_path
else
render new_path
end
end
private
def destination_params
params.require(:destination).permit(:location, :description)
end
end
#destination.save! will raise an error if not successful.
#destination.save will return true or false.
#destination.save! will throw error in case of unsuccessful saving. To get to the render new_path line you have to do just #destination.save.
#destination.save! will throw an error. You have to do something like;
if #destination.save # returns true if successfully saved else false
redirect_to destinations_path
else
flash[:errors] = #destination.error_messages # Display errors in view
render new_path
end
HTH.

Rails polymorphic comments ajax - 500 (Internal Server Error)

I used this tutorial for making polymorphic comments
https://gorails.com/episodes/comments-with-polymorphic-associations
It works fine but when I set remote true to my comments form I got 505 error(the new comment is not appended to the list) when trying to add a new comment.
But when I reload the page the new comment appears in the list.
What can be wrong here?
I got this files in views/comments folder:
create.js
<% unless #comment.errors.any? %>
$("ul.comments").append('<%= j render "comments/comment" %>')
$("#new_comment")[0].reset();
<% end %>
_form.html.erb
<%= simple_form_for([commentable, Comment.new], remote: true) do |f| %>
<%= f.input :body, label: false %>
<%= f.button :submit, "Submit" %>
<% end %>
comments_controller.rb
def create
#comment = #commentable.comments.new(comment_params)
#comment.user = current_user
#comment.save
respond_to do |format|
format.html { redirect_to #commentable, notice: "Your comment was successfully added."}
format.js
end
end
_comment.html.erb
<li class="comment">
<b><%= comment.user.try(:username) %>:</b>
<%= comment.body%>
</li>
console
UP
I got this routes for post
resources :posts do
resources :comments, module: :posts
end
+
controllers/posts/comments_controller
class Posts::CommentsController < CommentsController
before_action :set_commentable
private
def set_commentable
#commentable = Post.friendly.find(params[:post_id])
end
end
The folder structure looks almost the same as here
https://github.com/excid3/gorails-episode-36/tree/master/app/controllers
the response tab shows this
undefined local variable or method `comment' for #<#<Class:0x007f993d3c5450>:0x007f99450a6190>
Trace of template inclusion: app/views/comments/create.js.erb
and when I replace
<%= j render #comment %> with some text it appends to the list on submit
ok the problem is with _comment.html erb
updated create.js with instance variable for comment and it works now.
<% unless #comment.errors.any? %>
$("ul.comments").append('<%= j render partial: "comments/comment", locals:{comment: #comment} %>')
$("#new_comment")[0].reset();
<% end %>

Nested model validation - errors don't show

There have been many questions about this, but none of them seem to help. And yes, I have watched this rails cast.
I have an Author who has many Books, like so:
Author:
class Author < ActiveRecord::Base
attr_accessible :name
has_many :books, dependent: :destroy
accepts_nested_attributes_for :books, allow_destroy: true
validates :name, presence: true
validates :name, length: { minimum: 3 }
end
Book:
class Book < ActiveRecord::Base
attr_accessible :name, :year
belongs_to :author
validates :name, :year, presence: true
validates :year, numericality: { only_integer: true, less_than_or_equal_to: Time.now.year }
end
I created the following form to add a book to an author in authors#show:
<%= form_for([#author, #book], html: { class: "well" }) do |f| %>
<% if #book.errors.any? %>
<div class="alert alert-block">
<ul>
<% #author.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
#labels and buttons...
<% end %>
...with the following authors_controller method:
def show
#author = Author.find(params[:id])
#book = #author.books.build
end
...and the following books_controller method:
def create
#author = Author.find(params[:author_id])
if #author.books.create(params[:book])
redirect_to author_path(#author)
else
render action: :show
end
end
I cannot seem to figure out why the form does not display any error messages. I followed the example from railscasts where they say there should be an instance variable of books in the form instead of #author.books.build, so I put the latter in the controller and #book in the form - still to no avail.
Thanks for any help!
Let's step through it.
You submit the create, and that enters your create action
def create
#author = Author.find(params[:author_id])
if #author.books.create(params[:book])
redirect_to author_path(#author)
else
render action: :show
end
end
(Side note, what if #author is not found. You are not handling that case.)
Now, the Author is found, but #author.books.create fails (returns false), so you render the show action.
This uses the show template, but does not call the show action code. (Side note, maybe the new page would be a better choice, so the user can try to create again.)
At this point #author is instantiated with the Author you found, but not #book. So #book, if called will be nil.
Your show template does
if #book.errors.any?
which will not be true, so the rest of the template inside the if will be skipped. That's why there are no errors.
You don't need a form_for to display error messages. If you switch to using the new template, then there will be a form to try again.
So let's switch to rendering new.
Class BooksController < ApplicationController
def new
#author = Author.find(params[:author_id])
#book = #author.books.build
end
def create
#author = Author.find(params[:author_id])
#book = #author.books.build(params[:book])
if #author.save
redirect_to author_path(#author)
else
render action: :new
end
end
Your new template will be
<% if #author.errors.any? %>
<div class="alert alert-block">
<ul>
<% #author.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<% if #book.errors.any? %>
<div class="alert alert-block">
<ul>
<% #book.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<%= form_for([#author, #book], html: { class: "well" }) do |f| %>
#labels and buttons...
<% end %>
In Books controller
/books_controller.rb
def new
#author = Author.find_by_id(params[:author_id])
#book = #author.books.build
end
def create
#author = Author.find_by_id(params[:author_id])
if #author
#book = #author.books.build(params[:book])
if #book.save
flash[:notice] = "Book saved successfully"
redirect_to author_path(#author)
else
render :new
end
else
flash[:notice] = "Sorry no author found"
redirect_to author_path
end
end
If author is not present redirect to authors index page with error message dont render the new form as you'll not be able to build the books form as author is nil.
And in your books new form you can have the error listed for books
/books/new.html.erb
<% if #book.errors.any? %>
<div class="alert alert-block">
<ul>
<% #books.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>

undefined method `errors' for nil:NilClass

Here is my Edited details:
I have my controller like,
class Enr::Rds::SurvRdsapXrefsController < Enr::Controller
def index
#enr_rds_surv_rdsap_xrefs = Enr::Rds::SurvRdsapXref.paginate(page: params[:page])
end
def show
end
def new
#enr_rds_surv_rdsap_xref = Enr::Rds::SurvRdsapXref.new
end
def edit
#enr_rds_surv_rdsap_xref = Enr::Rds::SurvRdsapXref.find(params[:id])
end
def create
#enr_rds_surv_rdsap_xref = Enr::Rds::SurvRdsapXref.new(params[:enr_rds_surv_rdsap_xref])
respond_to do |format|
if #enr_rds_surv_rdsap_xref.save
format.html { redirect_to :enr_rds_surv_rdsap_xrefs, notice: "Survey link was successfully created." }
format.js
format.json { render json: #enr_rds_surv_rdsap_xref, status: :created, location: #enr_rds_surv_rdsap_xref }
else
format.html { render action: "new" }
format.js
format.json { render json: #enr_rds_surv_rdsap_xref.errors, status: :unprocessable_entity }
end
end
end
def update
end
def destroy
end
end
Here is my view form like
<%= form_for(#enr_rds_surv_rdsap_xref, :remote => true) do |f| %>
<% if #enr_rds_surv_rdsap_xref.errors.any? %>
<div id="error_explanation">
<div class="validate">
The form contains <%= pluralize#enr_rds_surv_rdsap_xref.errors.count, "error") %>.
</div>
<ul>
<% #enr_rds_surv_rdsap_xref.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="control-group">
<%= f.label :section %><br />
<%= f.text_field :section %>
</div>
<%= f.submit %>
<% end %>
When i click the index page to create a new link, the index page showing error like
NoMethodError in Enr/rds/surv_rdsap_xrefs#index
undefined method `errors' for nil:NilClass
Thanks for the suppport and please suggest me to rectify the error. I am new to ROR. Thanks
Your error reveals that the rendering of the index template is causing the error, which means you're rendering the form for the new survey (the code snippet above) in the index template. This is fine, but if you're going to do that, you'll have to instantiate a new survey in index, as well as in new.
At the simplest, you could just copy the code in new to index:
def index
#enr_rds_surv_rdsap_xrefs = Enr::Rds::SurvRdsapXref.paginate(page: params[:page])
#enr_rds_surv_rdsap_xref = Enr::Rds::SurvRdsapXref.new
end
def new
#enr_rds_surv_rdsap_xref = Enr::Rds::SurvRdsapXref.new
end
To keep your code a bit DRYer you might change where the new instance is created. A pattern you'll often see is something similar to:
before_filter :build_record, :only => [:new, :index]
protected
def build_record
#survey = YourSurvey.new
end
This way you don't even need to write the new/index methods if you don't have any other logic.
Do you also set #survey in the new action in your controller? The error means that when the view is rendered #survey is nil, so there must be a problem with setting that instance variable.
Do you get the error when you go to the 'new' view or when you try to submit the form (create)?
The form contains <%= pluralize#enr_rds_surv_rdsap_xref.errors.count, "error") %>
This line of the code is the problem. You are lacking a "(" between the pluralize and the #enr...
Explained: RoR thinks that the object is: pluralize#enr... instead of the # All alone, and he has no errors for this kind of object.

Resources