I want to show errors when they are in form and can not understand why this code does not work.
hotel.rb
class Hotel < ActiveRecord::Base
...
has_many :comments
...
end
comment.rb
class Comment < ActiveRecord::Base
belongs_to :hotel
belongs_to :user
delegate :email, to: :user, prefix: true
validates :body, presence: true, length: { minimum: 5, maximum:200 }
end
hotels/show.
...
%h2 Comments
#comments
.ui.piled.blue.segment
.ui.header
%i.icon.inverted.circular.blue.comment
Comments
.ui.comments
= render :partial => #hotel.comments
= render 'comments/form', comment: #hotel.comments
...
_form
-if user_signed_in?
= simple_form_for [#hotel, Comment.new] do |f|
=f.error_notification
%br
.ui.reply.form
.field
=f.label :body, "New comment"
=f.input :body, as: :text, label: false
=f.submit 'Add comment', class: "ui fluid blue labeled submit icon button"
-else
=link_to 'Sign in to add comment', new_user_session_path, class: 'ui blue button'
_comment
= div_for comment do
.comment
.content
%span.author= comment.user_email
.metadata
%span.date Posted #{time_ago_in_words(comment.created_at)} ago
.text
= comment.body
If you add too_short and too_long model that does not correct the.
UPDATE
comments_controller
class CommentsController < ApplicationController
def create
#hotel = Hotel.find(params[:hotel_id])
#comment = #hotel.comments.new(comment_params)
#comment.user_id = current_user.id
#comment.save
redirect_to #hotel
end
private
def comment_params
params.require(:comment).permit(:user_id, :body, :hotel_id)
end
end
I solved this problem.
comments_controller
def create
#hotel = Hotel.find(params[:hotel_id])
#comment = #hotel.comments.new(comment_params)
#comment.user_id = current_user.id
#comment.save
respond_to do |format|
if #comment.save
format.html { redirect_to #hotel }
else
format.html { render partial: 'comments/form' }
format.json { render json: #comment.errors, status: :unprocessable_entity }
end
end
end
_form
-if user_signed_in?
= simple_form_for [#hotel, #comment] do |f|
- if #comment.errors.any?
#error_explanation
%h2
= pluralize(#comment.errors.count, "error")
prohibited this comment from being saved:
%ul
- #comment.errors.full_messages.each do |msg|
%li= msg
%br
.ui.reply.form
=f.error_notification
.inputs
=f.label :body, "New comment"
=f.input :body, as: :text, label: false
.actions
=f.button :submit, 'Add comment', class: "ui fluid blue labeled submit icon button"
-else
=link_to 'Sign in to add comment', new_user_session_path, class: 'ui blue button'
%br
This code render comment form when the comment doesn't save, and write the errors.
Your issue is in your controller. Error's won't populate until you try and save the record. Well in your instance you don't have any flow control to see if it saved, you just have #comment.save. If it doesn't save or if it does save, you do the same redirect.
Try this:
if #comment.save
redirect_to #hotel, notice: 'Comment was successfully created.'
else
redirect_to #hotel, notice: 'There was an issue trying to save your comment.'
end
Now with render :new, #comment.errors will be populated (assuming you tried an invalid length) and now you should see the error messages displayed!
Related
hope your having a wonderful day drinking some coffee and responding to some forms.
Problem:
As my title states, I am trying to create 2 forms on one view. I am new to ruby on rails.
My controller functions:
Controller name is border_rotation:
def create
if params[:export_submit]
#border_rotation_export = BorderRotationExport.new(border_rotation_export_params)
respond_to do |format|
if #border_rotation_export.save
flash[:success] = "Export successfully created"
format.html { render :new }
else
flash[:error] = "Export was not created."
end
end
else
#border_rotation_import = BorderRotationImport.new(border_rotation_import_params)
respond_to do |format|
if #border_rotation_import.save
flash[:success] = "Export successfully created"
format.html { render :new }
else
flash[:error] = "Export was not created."
end
end
end
end
def new
#border_rotation_export = BorderRotationExport.new
#border_rotation_import = BorderRotationImport.new
end
private
def border_rotation_export_params
params.require(:border_rotation_export).permit(:exporter_name,:vehicle_color,:rot_num,:current_date,:current_time,:goods_description,:license_num,:entry)
end
def border_rotation_import_params
params.require(:border_rotation_import).permit(:importer_name,:vehicle_color,:rot_num,:current_date,:current_time,:goods_description,:license_num,:entry)
end
My new View form:
It has 2 forms and is enclosed in bootstrap tabs
<%= form_for #border_rotation_export, url: rotation_create_path, method: :post do |f|%>
<lable>Importer Name: </lable><%= f.text_field :importer_name, class: "form-control", placeholder: "Importer Name"%>
<lable>Vehicle Color: </lable><%= f.text_field :vehicle_color, class: "form-control", placeholder: "Vehicle Color"%>
**its fields**
<% end %>
and
<%= form_for #border_rotation_import, url: rotation_create_path, method: :post do |f|%>
<lable>Exporter Name: </lable><%= f.text_field :exporter_name, class: "form-control", placeholder: "Exporter Name"%>
<lable>Vehicle Color: </lable><%= f.text_field :vehicle_color, class: "form-control", placeholder: "Vehicle Color"%>
**its fields**
<% end %>
The error in my new.html.rb
First argument in form cannot contain nil or be empty
Displays this in red highlighted
<%= form_for #border_rotation_export, url: rotation_create_path, method: :post do |f|%>
My guess is that it submits both forms but only has the parameters for one form with the input data. Once I submit, it saves to the database but it gives me the error
**Routes **
get 'rotation/create', to: 'border_rotation#create'
post 'rotation/create', to: 'border_rotation#create'
Request
Parameters:
{"utf8"=>"β",
"authenticity_token"=>"Cu52CIDgrY0b7Yk6edkd7+RTl5yR4qSEqPPrqWtM0nIQVDvw7eYDF36zduJPLjI+vVNqCfgtLcMDUEkW6qDOdQ==",
"border_rotation_import"=>
{"importer_name"=>"john",
"vehicle_color"=>"red",
"rot_num"=>"11sssfeeea",
"current_date"=>"2021-09-22",
"current_time"=>"09:37",
"goods_description"=>"yogurt",
"license_num"=>"c-11223",
"entry"=>"c1223"},
"import_submit"=>"Submit"}
Thank you in advance
You can setup the controller with a lot less redundancy:
# config/routes.rb
resources :rotations, only: [:new, :create]
class BorderRotationsController < ApplicationController
# GET /rotations/new
def new
populate_forms
end
# POST /rotations
def create
resource = model_class.new(create_params)
set_ivar(resource) # sets #border_rotation_export or #border_rotation_import
if resource.save
flash[:success] = "#{humanized} successfully created"
redirect_to action: :new
else
populate_forms
flash[:error] = "#{humanized} could not be created - please try again."
render :new
end
end
private
# gets the model class via params[:subtype]
def model_class
#model_class ||= begin do
if params[:border_rotation_export].present?
BorderRotationExport
else
BorderRotationImport
end
end
end
def humanized
model_class == BorderRotationExport ? 'Export' : 'Import'
end
def set_ivar(value)
instance_variable_set(
"##{param_key}",
value
) β
βend
# just sets up the instance variables for the form
def populate_forms
#border_rotation_export ||= BorderRotationExport.new
#border_rotation_import ||= BorderRotationImport.new
end
# border_rotation_export or border_rotation_import
def param_key
model_class.model_name.param_key
end
def create_params
require(param_key).permit(
:exporter_name, :vehicle_color, :rot_num,
:current_date,:current_time, :goods_description,
:license_num, :entry
)
end
And then use partials so that you can resuse the same form:
# app/views/border_rotations/new.html.erb
<%= render partial: 'form',
locals: { border_rotation: #border_rotation_export } %>
<%= render partial: 'form',
locals: { border_rotation: #border_rotation_import } %>
# app/views/border_rotations/new.html.erb
<%= form_with model: border_rotation, url: rotations_path do |f| %>
<div class="field">
<%= f.label :importer_name %>
<%= f.text_field :importer_name, class: "form-control" %>
</div>
<div class="field">
<%= f.label :importer_name %>
<%= f.text_field :importer_name, class: "form-control" %>
</div>
# ...
<% end %>
If the requirements diverge use two separate routes/controllers and inheritance instead of blooming out in tons of new code paths.
I have a rails app with events and event_registrants, someone can essentially register for an event.
Here's my event model:
class Event < ActiveRecord::Base
has_many :event_registrants, :dependent => :destroy
accepts_nested_attributes_for :event_registrants, :reject_if => lambda { |a| a[:first_name].blank? }, :allow_destroy => true
Here's my event_registrant model:
class EventRegistrant < ActiveRecord::Base
belongs_to :event
attr_accessible :comment, :company, :email, :first_name, :last_name, :phone, :event_id
validates_presence_of :company, :email, :first_name, :last_name, :phone
end
On the show template, I'd like to include a form to allow someone to register directly from there.
<%= form_for #event do |f| %>
<%= f.fields_for :event_registrants do |builder| %>
<%= render "registration_form", :f => builder %>
<% end %>
<%= f.submit 'Send it', :class => "blue-btn", :id => "registrant_submit" %>
<% end %>
Partial:
<ul class="form" id="register_form">
<li><%= f.text_field :first_name, :placeholder => "First name *" %> <%= f.text_field :last_name, :placeholder => "Last name *" %></li>
<li><%= f.text_field :email, :placeholder => "Email address *", :class => 'wide' %></li>
<li><%= f.text_field :company, :placeholder => "Company name *", :class => 'wide' %></li>
<li><%= f.text_field :phone, :placeholder => "Phone number *", :maxlength => '14' %></li>
<li><%= f.text_area :comment, :placeholder => "Additional info", :class => 'wide', :rows => 4, :cols => 68 %></li>
</ul>
Finally my events controller'
def show
#event = Event.where(:event_hosting_type => "eureka_event").find_by_permalink(params[:id])
1.times {#event.event_registrants.build}
respond_to do |format|
format.html # show.html.erb
format.json { render json: #event }
end
end
def update
#event = Event.find_by_permalink(params[:id])
respond_to do |format|
if #event.update_attributes(params[:event])
format.html { redirect_to #event, notice: "#{#event.title} updated." }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
def create
#event = Event.new(params[:event])
#event_registrant = #event.build_event_registrant
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'New event created.'}
format.json { render json: #event, status: :created, location: #event }
else
format.html { render action: "new" }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
end
The issue I'm having is that the current form will display but will only update the current record, having 1.times {#event.event_registrants.build} will add a new, empty form but the previous entry will be there. I'd like someone to submit their info, have the form reset and allow someone else to submit new info.
You can update your code to say
1.times {#event.event_registrants.build} if #event.event_registrants.empty?
Your form will properly show the value of event_registrants if it exists, or build it if not there.
However, if you're looking to dynamically add or remove form fields, you may find the cocoon gem an easy way to handle the javascript/ruby involved with that.
I have a problem with a form with nested resource. The data model is easy:
class Event < ActiveRecord::Base
extend FriendlyId
friendly_id :name, use: [:slugged, :finders]
has_many :event_contents
end
class EventContent < ActiveRecord::Base
belongs_to :event
end
My form:
= simple_form_for([:admin, #event, #event.event_contents.new], remote: true) do |f|
.chat-form
.input-cont
= f.input :content, label: false, input_html: { class: 'form-control' }
.btn-cont
%span.arrow
= f.submit 'invia', class: 'btn blue icn-only'
The controller:
class Admin::EventContentsController < AdminController
def create
#event_content = EventContent.new event_content_params
#event_content.user_id = current_user.id if current_user
if #event_content.save
respond_to do |format|
format.js { render :nothing => true }
end
else
end
end
private
def event_content_params
params.require(:event_content).permit(
:content,
:event_id,
:user_id
)
end
end
When i submit the post in the params instead of event_id I have the event "slug"
pry(#<Admin::EventContentsController>)> params
=> {"utf8"=>"β", "event_content"=>{"content"=>"uhm"}, "commit"=>"invia", "action"=>"create", "controller"=>"admin/event_contents", "event_id"=>"test-test-test"}
The record is created in the db, but event_id is nil so the association is broken.
Why instead of the event_id I have the event slug???
Update
The issue was the controller:
def create
#event = Event.find params[:event_id]
#event_content = #event.event_contents.build event_content_params
#event_content.user_id = current_user.id if current_user
if #event_content.save
respond_to do |format|
format.js
end
else
end
end
Maybe try doing the following:
in your Event model add accepts_nested_attributes_for :event_contents
in your form, when you are collecting the :event_contents replace
f.input :content, label: false, input_html: { class: 'form-control' }
with the following:
f.fields_for :event_contents do |content|
content.input :content
end
update your strong params in your Events controller to include {:event_contents_attributes} which might look something like below depending on the other params you need to pass through:
params.require(:event).permit(:name, {:event_contents_attributes => [:content]})
In your Events controller, update def new to include this line item event_content = #event.event_contents.build
All that to say, I believe you would want this to be routed to your Events controller and not your EventContents controller because the :event_contents are nested in the :event. It looks like your form is submitting to the EventContents controller currently. Also, I don't believe this argument in your simple_form_for #event.event_contents.new is necessary.
Here's what I built off of your question. It's not admin namespaced but might be helpful.
class EventsController < ApplicationController
def new
#event = Event.new
event_content = #event.event_contents.build
end
def create
#event = Event.new(event_params)
respond_to do |format|
if #event.save
format.html { redirect_to #event, notice: 'Event was successfully created.' }
format.json { render :show, status: :created, location: #event }
format.js
else
format.html { render :new }
format.json { render json: #event.errors, status: :unprocessable_entity }
end
end
private
def event_params
params.require(:event).permit(:name, {:event_contents_attributes => [:content]})
end
end
Event model:
class Event < ActiveRecord::Base
has_many :event_contents
accepts_nested_attributes_for :event_contents
end
And finally the form:
<%= form_for(#event) do |event| %>
<div class="field">
<%= event.label :name %><br>
<%= event.text_field :name %>
</div>
<%= event.fields_for :event_contents do |content| %>
<div class="field">
<%= content.label :content %>
<%= content.text_field :content %>
</div>
<% end %>
<div class="actions">
<%= event.submit %>
</div>
<% end %>
I'm having a strange issue where an extra/random character ("0") is being displayed with my flash[:notice]. I can't figure out where it's coming from.
Controller:
def edit
#subject = Subject.find_by(id: params[:id])
end
def update
#subject = Subject.find_by(id: params[:id])
if #subject.update_attributes(strong_params)
flash[:notice] = 'Subject has been updated'
redirect_to action: 'show', id: #subject.id
else
render 'edit'
end
end
def delete
#subject = Subject.find_by(id: params[:id])
end
private
def strong_params
params.require(:subject).permit(:name, :position, :visible)
end
View:
= if !flash[:notice].blank?
.notice
= flash[:notice]
%h2 Update subject
= form_for :subject, :url => {action: 'update', id: #subject.id} do |f|
= f.label :name, 'Name:'
= f.text_field :name
= f.label :position, 'Position:'
= f.text_field :position
= f.label :visible, 'Visible:'
= f.text_field :visible
= f.submit 'Update Subject'
%h2
Are you sure you want to delete the subject - #{#subject.name}
= link_to 'Yes, delete!', action: 'destroy', id: #subject.id
So, as it turned out the answer is: change 'loud' haml = if !flash[:notice].blank? to 'silent' - if !flash[:notice].blank?.
I don't know what happened but my question form stopped working and now goes blank after submitting.
Here is the form view:
<%= simple_form_for [#comment, Question.new] do |f| %>
<p>
<div class="form">
<%= f.input :title, as: :text, input_html: { rows: "1" } %>
<%= f.input :body, as: :text, input_html: { rows: "10" } %>
<p><%= f.submit "Answer", class: "btn btn-primary" %></p>
</div>
<% end %>
The questions model:
class Question < ActiveRecord::Base
validates :body, presence: true
validates :title, presence: true
validates :user_id, presence: true
belongs_to :user
acts_as_votable
belongs_to :comment
has_many :pictures
end
and the controller:
def create
#comment = Comment.find(params[:comment_id])
#question = #comment.questions.create(question_params)
respond_to do |format|
if #question.save
format.html { redirect_to comment_questions_path, notice: 'Question was successfully created.' }
format.json { render action: 'show', status: :created, location: comment_questions_path }
else
format.html { render action: 'new' }
format.json { render json: #question.errors, status: :unprocessable_entity }
end
end
end
The main problem was in routing: you can't redirect_to #question for nested resources. Use
url_for([#comment, #question])