can't change text area style to field_with_error - ruby-on-rails

I'm dealing with this legacy form for creating a new conversation. It has two fields : Name and description (the first comment of a conversation)
Here are the fields :
_fields.haml
.conversation_title= f.label :name, t('.name')
.clear
= f.text_field :name, :style => 'width: 230px'
= errors_for f.object, :name
if f.object.new_record?
= f.fields_for :comments, f.object.comments.build do |comment_fields|
.conversation_title= comment_fields.label :description, t('.description')
= comment_fields.text_area :body, :placeholder => t("comments.new.conversation"), :style => 'width: 545px'
= errors_for f.object, :comments
from the new view for conversations
= form_for [#current_project, #conversation], :html => { 'data-project-id' => #current_project.id, :name => 'form_new_conversation', :multipart => true } do |f| #, :onsubmit => 'return validate_form_new_conversation(form_new_conversation)'
= render 'fields', :f => f, :project => #current_project
= render 'watcher_fields', :f => f, :project => #current_project
The associated validations are :
conversation.rb
validates_presence_of :name, :message => :no_title, :unless => :simple?
validates_presence_of :comments, :message => :must_have_one, :unless => :is_importing
comment.rb
validates_presence_of :body, :unless => lambda { |c| c.task_comment? or c.uploads.to_a.any? or c.google_docs.any? }
For some reason, the proc associated to fields with error from base.rb
##field_error_proc = Proc.new{ |html_tag, instance| "<div class=\"field_with_errors\">#{html_tag}</div>".html_safe }
doesn't get called for the text area, so it doesn't change its style to make it turn red. It does for the :name field. Error messages get displayed properly
What am I missing?
Thanks!

The validation would be for the Comment model (rather than the Conversation model) on the body field. Check to make sure that validation exists. You can debug this to make sure that comment_fields.object has an error set on the body field, too.

I failed to notice one important part of this line in your code:
= f.fields_for :comments, f.object.comments.build do |comment_fields|
You call f.object.comments.build which means that you will always end up with a new instance of Comment (rather than the instance that was validated in the controller).
To avoid this you can build a comment in the controller. If you are using the normal restful actions you probably have two places where you want to build a comment. First in the new action and second, in the create action.
def new
#conversation = Conversation.new
#conversation.comments.build # Create a blank comment so that the fields will be shown on the form
end
def create
#conversation = Conversation.new(params[:conversation])
respond_to do |format|
if #conversation.save
format.html { redirect_to conversations_path }
else
format.html {
#conversation.comments.build if #conversation.comments.blank? # Create a blank comment only if none exists
render :action => "new"
}
end
end
end

Related

Issues with Simple_form and accepts_nested_attributes_for

I'm quite new with RoR so sorry if I say something not correct.
I have these models.
class Course < ApplicationRecord
has_many :frequencies, inverse_of: :course
belongs_to :subject, optional: true
validates :start_date, presence: true
end
class Frequency < ApplicationRecord
belongs_to :user
belongs_to :course
validates :course, presence: true
validates_presence_of :course
accepts_nested_attributes_for :user, :course
has_many_attached :docs
end
The relationship between Course and Frequency is 1:N but, at the end, I use it as a 1:1 (things changed after defined the models).
This is the view app/views/frequencies/show.html.haml
= simple_form_for #frequency, :url => frequencies_update_path(:id => #frequency.id) do |f|
.panel.panel-primary
.panel-heading
%h4.panel-title= t 'frequencies.upd_frequency'
.panel-body
= f.simple_fields_for :user do |u|
.row
.col-md-4
= u.label t 'frequencies.first_name'
.col-md-4
= u.input :first_name, :label => false, :disabled => true, :input_html => {:id => 'first_name'}
.row
.col-md-4
= u.label t 'frequencies.last_name'
.col-md-4
= u.input :last_name, :label => false, :disabled => true, :input_html => {:id => 'last_name'}
-#= u.hidden_field :id, value: #user_id
.panel.panel-primary
.panel-heading
%h4.panel-title= t 'frequencies.course'
.panel-body
= f.simple_fields_for :course do |u|
.row
.col-md-4
= u.label t 'frequencies.course_start_date'
.col-md-4
= u.input :start_date, :label => false, :disabled => (#frequency.validated? ? true : false), :input_html => {:id => 'course_start_date'}
.
.
.
= f.submit t('button.save'), :class => 'btn btn-primary ' + (current_user.role == $admin_role && #frequency.validated? ? 'disabled' : '')
= link_to t('button.cancel'), request.referer.present? ? request.referer : frequencies_index_path, :class => 'btn btn-default'
This is part of the frequencies_controller.rb
def update
#frequency = Frequency.find params[:id]
#course = Course.find #frequency.course_id
if over_max_hours_in_a_day(frequency_params[:user_attributes][:id], #course)
flash[:danger] = t('flash.max_hours')
render :action => :show and return
end
if #course.update(frequency_params[:course_attributes])
#frequency.docs.attach(frequency_params[:attach][:docs]) if (frequency_params[:attach].present? && frequency_params[:attach][:docs].present?)
flash[:notice] = t('flash.upd')
redirect_to :action => 'index' and return
else
flash[:danger] = #course.errors.full_messages.to_sentence
render :action => :show and return
end
end
def show
#frequency = Frequency.find params[:id]
#subjects = Subject.all
end
I'm able to edit a course from the related frequency's view but I have some strange behaviours:
when I save the validation process occurs but I have the error message only as flash message and not under the involved field (in others simpler views I have the message also under the field)
when I edit some course's fields (from the frequency view) and after I click on the save button it calls the update action but, if it runs inside the over_max_hours_in_a_day if condition, I'm not able to stay on the same view with the modified fields precompiled (but I have the fields like it loads at the beginning show action)
when I press the cancel button after a previous failed edit I remain on the same page instead of come back to the previous view (index view)
I'm not sure if this is due to accepts_nested_attributes_for on a belongs_to model, because I usually see it in a has_many model.
Rails 5 5.2.2
simple_form 4.1.0
Please, can you help me?
Thanks.
separate logic depending on the view (from show / index pages). Create 2 update methods
in case of error just render view again render :show OR render :index
I think it's better move logic of over_max_hours_in_a_day to model
for cancel button don't use referrer because after update it won't work. Pass exact back url using form locals or other way
if you want to hightligh fields - you must call .update, .save method on it with form parameters

Rails 3: save method wont work

I'm building my first Rails Application and until now everything went fine but then I found the following scenario: One Presentation is supposed to have N Iterations. I'm NOT using REST. So, I was trying to make a simple form to create iterations.
These are the models:
class Presentation < ActiveRecord::Base
has_many :iterations
end
class Iteration < ActiveRecord::Base
belongs_to :presentation
attr_accessible :presentation_id, :description, :delivery_date, :file
validates :presentation_id, :presence => {:message => 'is required.'}
end
These are the actions in the controller:
#Shows Form
def add
#iteration = Iteration.new
#presentation = Presentation.find(params[:id])
end
#Saves Form
def save
#iteration = Iteration.new(params[:iteration])
#iteration.delivery_date = Time.now
if #iteration.save
flash[:notice] = "Saved succesfully!"
else
flash[:error] = "Changes were not saved."
end
redirect_to root_url
end
These would be the view in HAML:
= form_for #iteration, :url => { :action => "save", :method => "post" }, :html => { :multipart => true } do |f|
- if #iteration.errors.any?
There were some errors:
.notice-text.fg-color-white
%ul.notice
- for message in #iteration.errors.full_messages
%li= message
%br
.field
= f.label :description, "Description"
= f.text_area :description, :class=>"form-text-area", :rows=>5
.field
= f.label :file, "Upload File"
= f.file_field :file
.field
= hidden_field_tag :presentation_id, #presentation.id
%br
= f.submit "Save"
The problem is, save method wont save, but #iteration.errors.count's value on the view is 0.
I used then save! instead as I read in another post, that way it throw the following error:
Validation failed: Presentation is required.
I can't figure out what I'm doing wrong. Please notice that in the view I used to have "f.hidden_field" instead of "hidden_field_tag" but I changed it for some other reasons, however I was getting the same error before that.
Your HAML,
hidden_field_tag :presentation_id
needs to be,
f.hidden_field :presentation_id, :value => #presentation.id
Looking at the your model definition you can have,
Nested resource: Refer to Controller path for nested resource - undefined method `<controller>_path'
Use Virtual attributes: Extremely useful railcasts by Ryan on this -> http://railscasts.com/episodes/16-virtual-attributes-revised
Save the presentation id in session: (This is not a clean very clean method)
On your controller, you will need to instantiate iteration on presentation so that presentation id is correctly populated.

Nil object when you didn't expect it for contact form_for

I'm trying to add a contact form for my Rails 3.1.3 application using this tutorial. However at the end when I try to load my contact page, I get the error:
You have a nil object when you didn't expect it!
You might have expected an instance of Array.
The error occurred while evaluating nil.[]
It says it occurs on line 1 of this code block on the new.html.haml page:
= form_for #message, :url => { :action=>"new", :controller=>"contact"} do |form|
%fieldset.fields
.field
= form.label :name
= form.text_field :name
.field
= form.label :email
= form.text_field :email
.field
= form.label :body
= form.text_area :body
%fieldset.actions
= form.submit "Send"
My controller looks like this:
class ContactController < ApplicationController
def new
#message = Message.new
end
def create
#message = Message.new(params[:message])
if #message.valid?
NotificationsMailer.new_message(#message).deliver
redirect_to(root_path, :notice => "Message was successfully sent.")
else
flash.now.alert = "Please fill all fields."
render :new
end
end
end
Model looks like this:
class Message < ActiveRecord::Base
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :body
validates :name, :email, :body, :presence => true
validates :email, :format => { :with => %r{.+#.+\..+} }, :allow_blank => true
def initialize(attributes = {})
attributes.each do |name, value|
send("#{name}=", value)
end
end
def persisted?
false
end
end
Why would I be getting that error and how do I fix it? Thanks!
do you add the routes as mention in the tutorial ?
match 'contact' => 'contact#new', :as => 'contact', :via => :get
match 'contact' => 'contact#create', :as => 'contact', :via => :post
Beside you can just use in ur form as
<%= form_for #message, :url => contact_path do |form| %>
If you are using separate forms for new and edit actions, you can this in new.html.haml
= form_for :message, :url => { :action=>"new", :controller=>"contact"} do |form|
or
= form_for :message, #message, :url => { :action=>"new", :controller=>"contact"} do |form|

Rails 3.1: Polymorphic Association #commentable - how to do it right?

I am having troubles with a polymorphic association in Rails. I have an application where it should be possible to comment on different models, such as Posts, Images, Projects
Right now I just have Posts to comment on. On the start page there is an index view of the latest Posts and each Post has a small Comment form underneath to comment on via Ajax, very much like Facebook.
My models look like this:
class Post < ActiveRecord::Base
belongs_to :post_category
belongs_to :user
has_many :comments, :as => :commentable
validates_presence_of :user_id
validates_presence_of :post_category_id
validates_presence_of :title
validates_presence_of :body
end
class Comment < ActiveRecord::Base
belongs_to :user
belongs_to :commentable, :polymorphic => true
end
Now in my Comments controller I added the following method (I think I took it from railscasts or something), which I assume tries to find out the #commentable dynamically when creating an comment.
But this always returns the error undefined methodcomments' for nil:NilClass`
# find commentable (parent) item
def find_commentable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value) unless name == 'user_id'
end
end
nil
end
def create
#commentable = find_commentable
#comment = #commentable.comments.build(params[:comment])
if #comment.save
redirect_to #comment, :notice => 'Comment was successfully created.'
redirect_to :id => nil
else
render :action => "new"
end
end
The two things I tried in my partial were:
leaving the commentable info out of the form
= form_for [#commentable, Comment.new], :remote => true do |f|
#new_comment.add_comment
= f.hidden_field :user_id, :value => current_user.id
= f.text_field :content, :size => 55, :value => 'leave a comment...', :class => 'comment_form'
= f.submit "send"
and 2. passing the commentable_id and commentable_type
= form_for [#commentable, Comment.new], :remote => true do |f|
#new_comment.add_comment
= f.hidden_field :user_id, :value => current_user.id
= f.hidden_field :commentable_id, :value => post_id
= f.hidden_field :commentable_type, :value => 'Post'
= f.text_field :content, :size => 55, :value => 'leave a comment...', :onfocus => 'this.select()', :class => 'comment_form'
= f.submit "send"
both without luck. Any help would be highly appreciated.
the whole comments controller code is in this gist: https://gist.github.com/1334286
It seems like the commentable is not assigned correctly in the comments controller. This could have multiple reasons. Here is a setup that should work for you:
In the Posts controller, e.g. action "show":
#post = Post.find(params[:id])
In the posts/show view comments form:
= form_for [#post, #post.comments.new], :remote => true do |f|
You should be able to use your comments controller as it - but you should change the render to e.g. a redirect_to :back in the create action since the comments controller will most probably not have a "new" view on its own (it is dependent from the commentable)
Also, make sure that you have nested routes for all resources that can act as a commentable, like so:
resources :posts do
resources :comments do
end
resources :comments do
resources :comments # subomments
end
UPDATED to reflect information in the comments
Don't use #commentable in the post show view, since it's only defined in the comments controller.
Do this instead:
_comment.html.erb: (the comment partial in the post show view)
<%= form_for ([comments, #vote]), remote: true do |f| %>
posts/posts_controller.rb:
<%= form_for ([#post, #vote]), remote: true do |f| %>

How to create a parent model through a child controller in rails 3? (belongs_to association)

I have two resources: Topics and Posts.
I am trying to figure out how I can create a Topic through the Post controller.
The models look like this:
class Topic < ActiveRecord::Base
has_many :posts, :dependent => :destroy
validates :name, :presence => true,
:length => { :maximum => 32 }
attr_accessible :name
end
class Post < ActiveRecord::Base
belongs_to :topic, :touch => true
has_many :comments, :dependent => :destroy
accepts_nested_attributes_for :topic
attr_accessible :name, :title, :content, :topic
end
posts/_form.html.erb:
<%= simple_form_for #post do |f| %>
<h1>Create a Post</h1>
<%= f.input :name, :label => false, :placeholder => "Name" %>
<%= f.input :title, :label => false, :placeholder => "Title" %>
<%= f.input :content, :label => false, :placeholder => "Content" %>
<%= f.input :topic, :label => false, :placeholder => "Topic" %>
<%= f.button :submit, "Post" %>
<% end %>
posts_controller.rb#create:
def create
#post = Post.new(params[:topic])
respond_to do |format|
if #post.save
format.html { redirect_to(#post, :notice => 'Post was successfully created.') }
else
format.html { render :action => "new" }
end
end
end
With posts/_form.html.erb I can create posts, but an associated topic is not created along with it. Can anyone tell me why I get this behavior and possibly how to correct it? I'm using Ruby 1.9.2, Rails 3.0.7 and the simple_form gem.
Railscasts has several episodes about this problem.
episode 16(subscription needed)
Source code:
https://github.com/railscasts/016-virtual-attributes-revised
And this episode
http://railscasts.com/episodes/57-create-model-through-text-field
views/products/_form.rhtml
<p>
<label for="product_category_id">Category:</label><br />
<%= f.collection_select :category_id, Category.find(:all), :id, :name, :prompt => "Select a Category" %>
or create one:
<%= f.text_field :new_category_name %>
</p>
models/product.rb
belongs_to :category
attr_accessor :new_category_name
before_save :create_category_from_name
def create_category_from_name
create_category(:name => new_category_name) unless new_category_name.blank?
end
I think Ryan's solution on category is more elegant than #nathanvda 's option 1. As it deal the data in Model instead of Controller. If you need to do same work in different controllers/actions, you will see the benefits.
Depending on what you want to do, i can see two options.
Option 1: Use a text-box to create or find an existing topic (as you had). In your controller you would write something like:
def create
topic_name = params[:post].delete(:topic)
#topic = Topic.find_or_create_by_name(topic_name)
#post = Post.new(params[:post])
#post.topic = #topic
if #post.save
format.html { redirect_to(#post, :notice => 'Post was successfully created.') }
else
format.html { render :action => "new" }
end
end
That is the quick and dirty way. It will, for each topic you type, try to find that topic, by name, or create it and assign it. But, this is error-prone. If your sets of topics is limited, there is a much easier way.
Option 2: use a select-box, a list of available topics. In your view write:
<%= simple_form_for #post do |f| %>
<h1>Create a Post</h1>
<%= f.input :name, :label => false, :placeholder => "Name" %>
<%= f.input :title, :label => false, :placeholder => "Title" %>
<%= f.input :content, :label => false, :placeholder => "Content" %>
<%= f.association :topic %>
<%= f.button :submit, "Post" %>
<% end %>
That will render a select-box with the possible topics. And in your controller you just have to write:
def create
#post = Post.new(params[:post])
if #post.save
format.html { redirect_to(#post, :notice => 'Post was successfully created.') }
else
format.html { render :action => "new" }
end
end
While this second option is really easy, it is less easy to add topics on the fly. You could do something in between, using an autocomplete field, that will either allow looking up values if they exist, or add new values if they don exist.
Hope this helps.
Are you getting mass assignment errors in the server log? You may need to add :topic_attributes to your attr_accessible list.

Resources