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.
Related
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 :)
My questions is regarding section 5.10 of the official Rails guide
I have an articles model with fields Title and Text
article.rb :
class Article < ApplicationRecord
validates :title, presence: true, length: { minimum: 5 }
end
articles_controller.rb :
class ArticlesController < ApplicationController
def index
#articles = Article.all
end
def show
#article = Article.find(params[:id])
end
def new
#article = Article.new
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new'
end
end
private
def article_params
params.require(:article).permit(:title, :text)
end
end
The guide says that
#article = Article.new
needs to be added to the new action otherwise otherwise #article would be nil in our view, and calling #article.errors.any? would throw an error. Here is the new.html.erb :
<%= form_with scope: :article, url: articles_path, local: true do |form| %>
<% if #article.errors.any? %>
<div id="error_explanation">
<h2>
<%= pluralize(#article.errors.count, "error") %> prohibited
this article from being saved:
</h2>
<ul>
<% #article.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<p>
<%= form.label :title %><br>
<%= form.text_field :title %>
</p>
<p>
<%= form.label :text %><br>
<%= form.text_area :text %>
</p>
<p>
<%= form.submit %>
</p>
<% end %>
<%= link_to 'Back', articles_path %>
What I am able to understand is when there is an error, it is present in #articles so #article.errors.any? helps in showing the errors when the 'new' view is rendered. It indeed works as expected but what I am not able to understand is #article = Article.new in the new action should reset the #article and the errors should be lost after the user is redirected to new. But somehow, the errors are not lost and are indeed being displayed. How is this happening ?
Both render and redirect are different things.
render Renders the content that will be returned to the browser as the response body.
redirect or redirect_to - Redirect is concerned about telling the browser it needs to make a new request to a different location or to the same location as given in path.
It is clearly mentioned in artcle 5.10
Notice that inside the create action we use render instead of redirect_to when save returns false. The render method is used so that the #article object is passed back to the new template when it is rendered. This rendering is done within the same request as the form submission, whereas the redirect_to will tell the browser to issue another request.
Note: You can read render vs redirect in detail
As per your question
def new
#article = Article.new
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
render 'new' # this will add error (validations)
end
end
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
redirect_to 'new' # this will not add any error as this is new request and #article will initialise again.
new #same as redirect
end
end
Edit: Creating Form Objects with ActiveModel. A form object is an object designed specifically to be passed to form_for
We always check for errors #article.errors.any? it will execute if #article object contains any error messages
Please read form_for doc.
render doesn't run any code in the new method, it just uses the new.html.x view. As such, #article = Article.new is never executed.
If you wanted the code from new run, you would need to actually call that method:
def create
#article = Article.new(article_params)
if #article.save
redirect_to #article
else
new #actually runs the code in the 'new' method
render 'new' # doesn't go anywhere near the new method, just uses its view
end
end
I'm running into a problem when trying to create a new object using nested resources in Rails. My routing is set up as:
resources :coins do
resources :questions
end
When I attempt to create a new question, it does not save. I'm redirected to the 'questions' page and the form from the 'new' page including everything that was typed into it remains on the page (rather than the list of questions that are supposed to be there when it saves). My controller is as follows:
class QuestionsController < ApplicationController
before_action :find_question, only: [:show]
before_action :find_coin
before_action :authenticate_user!, except: [:index, :show]
def index
#questions = Question.where(coin_id: #coin.id).order("created_at DESC")
end
def show
end
def new
#coin
#question = current_user.questions.build
end
def create
#question = current_user.questions.build(question_params)
if #question.save
redirect_to coin_question(#question.coin_id, #question.id)
else
render 'new'
end
end
private
def find_question
#question = Question.find(params[:id])
end
def find_coin
#coin = Coin.find(params[:coin_id])
end
def question_params
params.require(:question).permit(:content, :ques_num, :coin_id)
end
end
My 'new' page then displays the following form:
<%= simple_form_for #question, url: coin_questions_path(#coin.id) do |f| %>
<%= f.input :ques_num %>
<%= f.input :content %>
<%= f.submit "Post", class: "btn btn-primary" %>
<% end %>
This is my first time using nested resources and its tripping me up a little bit. I really appreciate any assistance here.
Your create action is failing and so it's executing the else statement which is just rendering back your form with the data you entered. The easiest thing to do is to just check out the log file and see why the save it being blocked.
go to /log/development.log and if you're using a mac press Command and the down arrow which will bring you all the way to the bottom of the file.
Also you may want to check out your model validations. If you don't have flash setup or aren't outputting the errors to your view a validation may be causing the form not to save and you wouldn't see the errors.
you could add some error handling to your view like this
<%= form_with(model: question, local: true) do |form| %>
<% if question.errors.any? %>
<div id="error_explanation">
<h2 class="text-danger"><%= pluralize(question.errors.count, "error") %> prohibited this question from being saved:</h2>
<ul class="text-danger">
<% question.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
for your controller try
def create
#question = Question.new(question_params)
if #question.save
flash[:success] = "question created successfully!"
redirect_to question_url(#question.id)
else
render 'new'
end
end
I think there will be error to create question.
if #question.save
redirect_to coin_question(#question.coin_id, #question.id)
else
render 'new'
end
So if record have any error to save it will redirect to new form.
Just use following code to know what is the errors to creating question
def create
#question = current_user.questions.build(question_params)
if #question.save
flash[:notice] = 'Question created'
redirect_to coin_question(#question.coin_id, #question.id)
else
flash[:notice] = 'Some error here!'
render 'new'
end
end
You need to setup flash to show flash error.
My update.html.erb:
<% #uploads.each do |upload| %>
<p><%= upload.name %></p>
<p class="grey">
<%= best_in_place upload, :place, type: :select, collection: [ ["Home", "Home"],["Sauna", "Sauna"]]%>
</p>
<%end%>
controller:
def show
#uploads = Upload.all
end
def update
#uploads = Upload.all
#upload = Upload.find(params[:id])
respond_to do |format|
if #upload.update_attributes(params[:upload])
format.html { redirect_to #upload, notice: 'Upload was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #upload.errors, status: :unprocessable_entity }
end
end
end
The problem is that I get the error: undefined methodeach' for nil:NilClass` , meaning that my controllers do not pass my object to the variable #uploads. Why do not the do that and how can I fix it? Thanks in advance.
ps. I could write at the top of my view something like this:
<% #uploads=Uploads.all%>
But that is not the best idea.
Thanks in advance
<% #uploads.each do |upload| %>
<%end if #uploads.present? %>
IF your routes are RESTful routes, the update method is a HTTP PUT method and it shouldn't have a template. What you are looking for is a edit action:
def edit
#uploads = Upload.all
end
#katja: Referring to your comment.
update is a HTTP PUT request, you cant get a record in that but edit is a get request you can get all records in edit action.
You can add a before_filter callback to your edit action.
before_filter :get_all_uploads, only: [:edit]
def get_all_uploads
#uploads = Upload.all
end
And in your edit.html.erb you can do
<% #uploads.each do |upload| %>
<p><%= upload.name %></p>
<p class="grey">
<%= best_in_place upload, :place, type: :select, collection: [ ["Home", "Home"],["Sauna", "Sauna"]]%>
</p>
<% end %>
I have 2 controllers: DocumentsController and DashboardController
After the user login successful, he is redirected to dashboard_path, which has a form to create a 'fast document' like this
<%= form_for #document, :html => {:class => 'well'} do |f| %>
<% if #document.errors.any? %>
<div id="alert alert-block">
<div class="alert alert-error">
<h2>Couldn't create your doc. :(</h2>
<ul>
<% #document.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
<label>A lot of fields</label>
<%= f.text_field :fields %>
<div class="form-actions">
<%= f.submit 'Create document', :class => 'btn btn-large' %>
</div>
<% end %>
but when an exception happen (like the user forgot to fill a field), I would like to show these exceptions, not just an alert saying 'Error'...actually, I didn't found a way to do this
here's my DashboarController
class DashboardController < ApplicationController
before_filter :authenticate
def index
#document = Document.new
end
end
and my DocumentsController
class DocumentsController < ApplicationController
respond_to :json, :html
def show
end
def create
#document = Document.new(params[:document])
#document.user = current_user
if #document.save
redirect_to dashboard_path, notice: 'Created!'
else
flash[:error] = 'Error!'
redirect_to dashboard_path
end
end
end
any help is appreciated :)
You are correctly redirecting on success; on failure, though, should not redirect; you need to render the template where the form was filled.
if #document.save
redirect_to dashboard_path, notice: 'Created!'
else
render 'dashboard/index'
end
You'll have to make sure that any variables needed by the index template are available in the create action of the documents_controller (you're just rendering the index template; you're not running the code from the dashboard controller's index action). Here's a excerpt from the relevant Rails Guide to clarify:
Using render with :action is a frequent source of confusion for Rails newcomers. The specified action is used to determine which view to render, but Rails does not run any of the code for that action in the controller. Any instance variables that you require in the view must be set up in the current action before calling render.
More at http://guides.rubyonrails.org/layouts_and_rendering.html#using-render