In my app I have a quiz in a partial because I need the same (lengthy) quiz for my new and for my edit view. In the header of the application.html.erb and on my users#show page I display the results from this quiz, once it is taken, using an if/else statement. For some reason, after a user takes the quiz in the new view it is registering that the quiz has been completed (you can tell by the if/else statement) but not displaying the variables. However, using the edit view (that calls the same partial _quiz.html.erb) the variables display perfectly.
My new view calls the partial like this:
<%= render partial: "quiz", locals: { url: swing_books_path, method: :post } %>
While my edit view calls it like this:
<%= render partial: "quiz", locals: { url: edit_swing_book_path(#swing_book), method: :put } %>
The partial looks like this:
<%= form_for #swing_book do |f| %>
<div class="clearfix">
<h1 class="text-center">
<%= f.text_field :swing01, class: 'form-control input-1' %>
<%= f.text_field :swing02, class: 'form-control input-1' %>
<%= f.text_field :swing03, class: 'form-control input-1' %>
-
<%= f.text_field :swing04, class: 'form-control input-1' %>
<%= f.text_field :swing05, class: 'form-control input-1' %>
<%= f.text_field :swing06, class: 'form-control input-1' %>
-
<%= f.text_field :swing07, class: 'form-control input-1' %>
<%= f.text_field :swing08, class: 'form-control input-1' %>
<%= f.text_field :swing09, class: 'form-control input-1' %>
<%= f.text_field :swing10, class: 'form-control input-1' %>
</h1>
<div class="form-group text-center">
<%= f.submit "Submit Swing Code", class: 'btn btn-success' %>
</div>
</div>
<% end %>
However, I use these exact same details (with just the variable names changed) on another quiz that is not having the same issue. (I have three other similar quizzes all of which display perfectly from both new and edit views.)
My model looks like this:
class SwingBook < ActiveRecord::Base
before_save :set_swing_code
def set_swing_code
self.swing_code = "#{self.swing01}#{self.swing02}#{self.swing03}-#{self.swing04}#{self.swing05}#{self.swing06}-#{self.swing07}#{self.swing08}#{self.swing09}#{self.swing10}"
end
belongs_to :user
validates :user, presence: true
end
My controller looks like this:
class SwingBooksController < ApplicationController
before_action :require_sign_in
def show
#swing_book = SwingBook.find(params[:id])
end
def new
#swing_book = current_user.build_swing_book
end
def create
#swing_book = SwingBook.new
#swing_book.swing_code = params[:swing_book][:swing01]
#swing_book.swing_code = params[:swing_book][:swing02]
#swing_book.swing_code = params[:swing_book][:swing03]
#swing_book.swing_code = params[:swing_book][:swing04]
#swing_book.swing_code = params[:swing_book][:swing05]
#swing_book.swing_code = params[:swing_book][:swing06]
#swing_book.swing_code = params[:swing_book][:swing07]
#swing_book.swing_code = params[:swing_book][:swing08]
#swing_book.swing_code = params[:swing_book][:swing09]
#swing_book.swing_code = params[:swing_book][:swing10]
#swing_book.swing_code = params[:swing_book][:swing_code]
#swing_book.user = current_user
if #swing_book.save
flash[:notice] = "Your swing code was saved successfully."
redirect_to user_path(current_user)
else
flash[:alert] = "Sorry, your results failed to save."
redirect_to welcome_index_path
end
end
def edit
#swing_book = SwingBook.find(params[:id])
end
def update
#swing_book = SwingBook.find(params[:id])
#swing_book.assign_attributes(swing_book_params)
if #swing_book.save
flash[:notice] = "Results were updated successfully."
redirect_to user_path(current_user)
else
flash.now[:alert] = "There was an error saving your results. Please try again."
redirect_to welcome_index_path
end
end
private
def swing_book_params
params.require(:swing_book).permit(:swing01, :swing02, :swing03, :swing04, :swing05, :swing06, :swing07, :swing08, :swing09, :swing10, :swing_code)
end
end
Finally, here's the logic I'm using to display these variables in the application.html.erb file:
<% if current_user.swing_book != nil %>
<p>ID Code:
<%= current_user.swing_book.swing01 %>
<%= current_user.swing_book.swing02 %>
<%= current_user.swing_book.swing03 %>
-
<%= current_user.swing_book.swing04 %>
<%= current_user.swing_book.swing05 %>
<%= current_user.swing_book.swing06 %>
-
<%= current_user.swing_book.swing07 %>
<%= current_user.swing_book.swing08 %>
<%= current_user.swing_book.swing09 %>
<%= current_user.swing_book.swing10 %></p>
<% end %>
And on the user#show page:
<% if #user.swing_book == nil %>
<h3>ID Code: ???-???-????<%= %></h3>
<p><%= link_to "Get Your ID Code Now!", new_swing_book_path %></p>
<% else %>
<h3>ID Code:
<%= #user.swing_book.swing01 %>
<%= #user.swing_book.swing02 %>
<%= #user.swing_book.swing03 %>
-
<%= #user.swing_book.swing04 %>
<%= #user.swing_book.swing05 %>
<%= #user.swing_book.swing06 %>
-
<%= #user.swing_book.swing07 %>
<%= #user.swing_book.swing08 %>
<%= #user.swing_book.swing09 %>
<%= #user.swing_book.swing10 %></h3>
<p><%= link_to "Update Your Code", edit_swing_book_path(#user.swing_book) %></p>
<% end %>
I originally tried displaying just the swing_code variable (as established in the method, but it was displaying a single integer so I switched to the code seen above. Ideally, I would like to display it as just the swing_code variable, but the (somewhat repetitive) workaround I'm currently using works fine-ish if I could only get the new/edit display issue worked out.
Any ideas as to what could be going wrong? I can't find any differences between this and the other quizzes that could be causing a problem!
In your create method, you have a block of code that explicitly sets the #swing_book values. This code is setting all of the values from the form into just the swing_code member. Since the last assignment is also from the swing_code param to the swing_code member, it's hiding the mistake. Here's the code with the correct member fields assigned:
#swing_book.swing01 = params[:swing_book][:swing01]
#swing_book.swing02 = params[:swing_book][:swing02]
#swing_book.swing03 = params[:swing_book][:swing03]
#swing_book.swing04 = params[:swing_book][:swing04]
#swing_book.swing05 = params[:swing_book][:swing05]
#swing_book.swing06 = params[:swing_book][:swing06]
#swing_book.swing07 = params[:swing_book][:swing07]
#swing_book.swing08 = params[:swing_book][:swing08]
#swing_book.swing09 = params[:swing_book][:swing09]
#swing_book.swing10 = params[:swing_book][:swing10]
#swing_book.swing_code = params[:swing_book][:swing_code]
Now, while that will fix the immediate problem, you probably just want to use the same approach as the update method. Try this for your create method, instead:
def create
#swing_book = SwingBook.new
#swing_book.assign_attributes(swing_book_params)
#swing_book.user = current_user
if #swing_book.save
flash[:notice] = "Your swing code was saved successfully."
redirect_to user_path(current_user)
else
flash[:alert] = "Sorry, your results failed to save."
redirect_to welcome_index_path
end
end
However, you have an opportunity to minimize the overall code to help eliminate errors like this. Consider something like this for your controller code:
class SwingBooksController < ApplicationController
before_action :require_sign_in
def show
#swing_book = SwingBook.find(params[:id])
end
def new
#swing_book = current_user.build_swing_book
end
def edit
#swing_book = SwingBook.find(params[:id])
end
def create
#swing_book = SwingBook.new
apply_form_values
end
def update
#swing_book = SwingBook.find(params[:id])
apply_form_values
end
private
def apply_form_values
#swing_book.assign_attributes(swing_book_params)
if #swing_book.save
flash[:notice] = "Results were updated successfully."
redirect_to user_path(current_user)
else
flash.now[:alert] = "There was an error saving your results. Please try again."
redirect_to welcome_index_path
end
end
def swing_book_params
params.require(:swing_book).permit(:swing01, :swing02, :swing03, :swing04, :swing05, :swing06, :swing07, :swing08, :swing09, :swing10, :swing_code)
end
end
You can use techniques like this to make it easier to write your initial code, and make it far easier to maintain.
Related
i have no idea why this error come out, can anyone help me? thanks
It shown when i comment out the product controller( def add) there.
And also if possible is that can help me check the add.html.erb there is that my option_for_select to get all the outlets correct?
Errors show in website
Errors show in console
ActionView::Template::Error (undefined method `errors' for nil:NilClass
#virtual_path = "shared/_error_messages";object = local_assign
s[:object]; object = object;; if object.errors.any?
^^^^^^^):
1: <% if object.errors.any? %>
2: <div id="error_explanation">
3: <div class="alert alert-danger">
4: The form contains <%= pluralize(object.errors.count, "error")
%>.
app/views/shared/_error_messages.html.erb:1
app/views/products/add.html.erb:5
app/views/products/add.html.erb:4
Add.html.erb
<h1>Add to outlet</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: #product, local: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :quantity %>
<%= f.number_field :quantity, class: 'form-control' %>
<%= f.label :price %>
<%= f.number_field :price, class: 'form-control' %>
<%= f.label :outlet %>
<%= f.select :outlet, options_for_select(#outlets), :include_blank => true %>
<%= f.hidden_field :category_id, value: 1 %>
<%= f.submit "Save changes", class: "btn btn-primary" %>
<% end %>
_error_messages.html.erb
<% if object.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(object.errors.count, "error") %>.
</div>
<ul>
<% object.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Product Controller
class ProductsController < ApplicationController
def category
#category = Category.find(params[:id])
end
def index
#products = Product.all
end
def show
#product = Product.find(params[:id])
end
def new
#product = Product.new
#product.category_id = params[:category_id]
end
def create
#product = Product.new(product_params)
#category_id = Category.find(params[:product] [:category_id])
if #product.save
flash[:success] = "Succesful create!"
redirect_to #product
else
render 'new'
end
end
def outlet
#outlet = Outlet.find(params[:id])
end
def add
##product = Product.find(params[:id])
##outlet = Outlet.find(params[:outlet_id])
end
def update
#product = Product.find(params[:id])
#outlet = Outlet.find(params[:outlet][:name])
if #product.update(product_params)
flash[:success] = "Product updated"
redirect_to #product
else
render 'add'
end
end
private
def product_params
params.require(:product).permit(:name, :quantity, :price,
:category_id)
end
end
The :new view when redirected if validations are not matched and where I'd like to see the error messages:
<%= simple_form_for ([ #recipe, #recipe.comments.build]), class:"comment-form" do |f| %>
<%= f.error_notification %>
<%= f.object.errors.full_messages.join(", ") if f.object.errors.any? %>
<%= f.input :name, label: false, placeholder: "Your name", input_html: { value: #comment.name } %>
<%= f.input :comment, label: false, placeholder: "Tell us about your experience", input_html: { value: #comment.comment } %>
<%= f.submit "Submit", class: "btn-comment-submit" %>
<% end %>
This is my controller:
def new
#recipe = Recipe.find(params[:recipe_id])
#comment = Comment.new
#comment = #recipe.comments.build
end
def create
#comment = Comment.new(comment_params)
#recipe = Recipe.find(params[:recipe_id])
#comment.recipe = #recipe
if #comment.save
redirect_to recipe_path(#recipe)
else
render :new
end
end
You're not binding the #comment instance you have created in your controller to the form. Instead #recipe.comments.build always creates a new instance of Comment.
You can set the model with a conditional:
<%= simple_form_for([#recipe, #comment || #recipe.comments.build]) do |form| %>
<%= f.error_notification %>
<%= f.object.errors.full_messages.join(", ") if f.object.errors.any? %>
<%= f.input :name, label: false, placeholder: "Your name" %>
<%= f.input :comment, label: false, placeholder: "Tell us about your experience" %>
<%= f.submit "Submit", class: "btn-comment-submit" %>
<% end %>
Note that you don't need to set the values for the inputs. The form builder will do that for you. Thats kind of the whole point of it.
Or you can preferably ensure that you're setting #comment in the controller to keep the view as simple as possible:
class RecipiesController < ApplicationController
before_action :set_recipe
# ...
def show
#comment = #recipe.comments.new
end
# ...
end
<%= simple_form_for([#recipe, #comment]) do |form| %>
# ...
<% end %>
And you can clean up your create action and just create the comment off the recipe:
def create
#recipe = Recipe.find(params[:recipe_id])
#comment = #recipe.comments.new(comment_params)
if #comment.save
redirect_to #recipe
else
render :new
end
end
I have a simple_form_for in my project. My goal is to alert people when their email is already in database thanks to an alert and ajax to reload the form if there is an error. When I send my form for the first time, I get the good respond from create.js.erb. But if I send the form a second time without reloading the page with the browser, my controller create method code is taken into account but not my create.js.erb code. For example, if I put an alert in the create.js.erb file, it doesn't pop the second time I send the form by clicking on the button again. Can you help me to figure why ?
pages_controller.rb
def create
#email = waiting_list_params[:email]
#waiting_user = WaitingList.new(waiting_list_params)
respond_to do |format|
if #waiting_user.save
UserMailer.welcome(#email).deliver_now
format.html { redirect_to concours_path }
format.js
else
format.html { render 'pages/concours' }
format.js { flash[:error] = #waiting_user.errors.full_messages[0] }
end
end
end
create.js.erb
function refreshForm(innerHTML) {
const newForm = document.getElementById("concours-form");
newForm.innerHTML = innerHTML;
}
<% if #waiting_user.errors.any? %>
refreshForm('<%= j render "pages/concours_form" %>');
confPopup.style.display = "none";
<% else %>
confPopup.style.display = "flex";
<% end %>
_form.html.erb
<%= simple_form_for #waiting_user, {id: 'concours-form', url: concours_path, remote: true} do |f| %>
<%= f.error_notification %>
<%= f.text_field :email,
class: "landing-form-input",
autocomplete: "off",
required: true,
pattern: '[^#]+#[^#]+\.[a-zA-Z]{2,6}',
placeholder: "Renseignez ton email ici"
%>
<i class="fa fa-envelope"></i>
<%= f.hidden_field :concours_city,
value:'',
id: 'c-choosen-city'
%>
<%= f.hidden_field :source,
value: params[:source]
%>
<%= f.button :submit,
class: "landing-form-button",
value: "Je tente ma chance !"
%>
<% end %>
routes.rb
get 'concours', to: 'pages#concours'
post 'concours', to: 'pages#create'
In my task show page, I have form for creating a response and the responses are displayed here as well. (Task has many responses)
views\tasks\show.html.erb
<%= simple_form_for([#task, Response.new]) do |f| %>
<%= f.input :is_comment, as: :hidden %>
<%= f.input :negotiate_pay, label: false %>
<%= f.input :comment_text, as: :text, input_html: {rows: 3}, label: false %>
<div class="row">
<div class="col-md-4 col-md-offset-8">
<%= f.button :submit %>
</div>
</div>
</div>
<% end %>
<br>
<div id="comments">
<%= render #responses %>
</div>
controllers\tasks_controller
def show
#task = Task.find(params[:id])
#responses = #task.responses.all
end
controllers\responses_controller
def create
#task = Task.find(params[:task_id])
#response = #task.responses.create(response_params)
#response.user_id = current_user.id
#response.is_comment = params[:is_comment]
#response.save
respond_to do |format|
format.html { redirect_to #task }
format.js
end
end
It is working alright but it doesn't show the errors in the form after validation and submit. How can I show the error message in response form which is in the show page of task. I am using a simple_form btw.
Help please
EDIT:
Models:
models\response.rb
belongs_to :user
belongs_to :task
has_many :subcomments
default_scope -> { order('created_at DESC') }
#VALIDATIONS
validates :comment_text, presence: true
validates :negotiate_pay, presence: true
You need to add the code that will display the errors to your form :
<%= simple_form_for([#task, Response.new]) do |f| %>
<% if #response.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#response.errors.count, "error") %> prohibited this response from being saved:</h2>
<ul>
<% #response.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
and you also need to tweak your create action in the responsesController:
def create
#task = Task.find(params[:task_id])
#response = #task.responses.build(response_params)
#response.user_id = current_user.id
#response.is_comment = params[:is_comment]
respond_to do |format|
if #response.save
format.html { redirect_to #task, notice: "response was createad" }
format.js
else
format.html { render :show }
end
end
also make sure you are displaying the errors inside your application.html.erb :
<% if flash[:error] -%>
<p class='alert alert-danger'><%=h flash[:error] %></p>
<% end -%>
<% if flash[:notice] -%>
<p class='alert alert-success'><%=h flash[:notice] %></p>
<% end -%>
The following code does the following: If the user, voted up show him a delete vote form and a vote down form. If the user voted down, show him a vote up form and the delete vote form. Otherwise just show the vote up and vote down form (I omitted some of the code so the question doesn't get too long).
(scenario: user voted down):
posts_controller.rb:
def show
#post = Post.find(params[:id])
#replies = #post.replies.paginate(page: params[:page])
#reply = #post.replies.build
#vote = Vote.new
store_location
end
votes_controller.rb:
class VotesController < ApplicationController
before_filter :signed_in_user
def create
#votable = find_votable
# Destroy the vote first in case the user already voted
if already_voted?
#vote = #votable.votes.find_by_user_id(current_user.id)
#vote.destroy
end
#vote = #votable.votes.build(params[:vote])
#vote.user_id = current_user.id
#votable.save
respond_to do |format|
format.html { redirect_back }
format.js
end
end
def destroy
#votable = find_votable
#vote = #votable.votes.find_by_user_id(current_user.id)
#vote.destroy
#votable.reload
respond_to do |format|
format.html { redirect_back }
format.js
end
end
private
def find_votable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
def already_voted?
#votable.votes.exists?(:user_id => current_user.id)
end
end
posts/vote_form
<div class="vote-form">
<% if #post.votes.exists?(:user_id => current_user.id) %>
<% if #post.votes.find_by_user_id(current_user.id).polarity == -1 %>
<%= form_for ([#post, #vote]), remote: true do |f| %>
<%= f.hidden_field :polarity, value: 1 %>
<div class="form-actions">
<%= button_tag type: :submit, class: "btn btn-small vote" do %>
<i class="icon-thumbs-down"></i> Vote up
<% end %>
</div>
<% end %>
<%= form_for ([#post, #post.votes.find_by_user_id(current_user.id)]),
method: :delete,
remote: true do |f| %>
<div class="form-actions">
<%= button_tag type: :submit, class: "btn btn-small btn-primary unvote" do %>
<i class="icon-thumbs-up"></i> Vote down
<% end %>
</div>
<% end %>
<% end %>
votes/create.js:
$('#<%= #votable.class.name.downcase %>-<%= #votable.id %> .vote-form').html(
"<%= escape_javascript(render('shared/delete_vote')) %>"
);
shared/_delete_vote.html.erb:
<% if #votable.votes.find_by_user_id(current_user.id).polarity == -1 %>
<%= form_for ([#votable, #votable.votes.new]), remote: true do |f| %>
<div class="form-actions">
<%= button_tag type: :submit, class: "vote-down btn btn-small" do %>
<i class="icon-thumbs-up"></i> Vote up
<% end %>
</div>
<% end %>
<%= form_for ([#votable, #vote]), method: :delete, remote: true do |f| %>
<div class="form-actions">
<%= button_tag type: :submit, class: "vote-up btn btn-small btn-primary" do %>
<i class="icon-thumbs-up"></i> Vote down
<% end %>
</div>
<% end %>
<% end %>
So, now everything works fine, except that I get this error when I click vote down, and then vote up right after (without refreshing the page):
ActionView::Template::Error (undefined method `polarity' for nil:NilClass):
1: <% if #votable.votes.find_by_user_id(current_user.id).polarity == 1 %>
2: <%= form_for ([#votable, #vote]), method: :delete, remote: true do |f| %>
What could be the problem?
EDIT:
I realized that the line in the error message is the problem (not #votable):
<% if #votable.votes.find_by_user_id(current_user.id).polarity == 1 %>
Strange, I thought it was the same as
<% if #post.votes.find_by_user_id(current_user.id).polarity == 1 %>
I think the problem is here:
def destroy
#votable = find_votable
#vote = #votable.votes.find_by_user_id(current_user.id)
#vote.destroy
#votable.reload
respond_to do |format|
format.html { redirect_back }
format.js
end
end
You destroy the vote for current user, so it can not be found inside view:
#votable.votes.find_by_user_id(current_user.id).polarity
That's why polarity is called on nil. You should reword the logic of your vote manipulations.
EDIT:
As the easiest solution, you may replace
if #votable.votes.find_by_user_id(current_user.id).polarity == -1
with
if #votable.votes.find_by_user_id(current_user.id).blank?