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
Related
I have a rating form with 5 radios and a submit button in it. The problem is when I visit this page, for some reason it tries to submit the form (with 'zero' values, of course). Validations don't let to do this, so it renders error message, which is not pretty.
Rating form:
= simple_form_for #shop.ratings.find_or_create_by(user_id: user_id),
:html => {:id => form_id,
:class => "star_rating_form"} do |f|
= f.hidden_field :shop_id, :value => #shop.id
- if signed_in?
= f.hidden_field :user_id, :value => current_user.id
= f.input :stars,
:label => "",
:collection => [[1], [2], [3], [4], [5]],
:label_method => :last,
:value_method => :first,
:as => :radio_buttons,
:item_wrapper_class => 'inline',
:checked => true
= f.submit
Ratings controller:
class RatingsController < InheritedResources::Base
belongs_to :shop
actions :create, :update
def create
#shop = Shop.find(params[:rating][:shop_id])
super
end
def update
#shop = Shop.find(params[:rating][:shop_id])
super
end
private
def permitted_params
params.permit(:rating => [:stars])
end
I tried to do like super unless params[:rating][:stars] == 0 however, it didn't help.
PS For the rest the form works fine.
I am not sure but is this bcoz you are using find_or_create_by which is right away creating object, try find_or_initialize_by. Or look in javascript somewhere you specified function onload() { form.submit(); } is specified. Hope it may help
Maybe someone could tell me how to create or correctly pass params from partial form_for to custom controller? Here is what i got so far:
Basically there are two classes: Scribbles (polymorphic - connected to local feeds) and local feeds. Trough command line, I can create scribbles, assign them to local feeds and display them in html. But creating them in trough different controller seems to be a rather tricky task. Any help would be appreciated.
Scribble.rb
class Scribble < ActiveRecord::Base
attr_accessible :post, :comments_attributes, :user_id, :posted_by, :localfeed_attributes
belongs_to :scribbled, :polymorphic => true
has_many :comments, :as => :commentable
accepts_nested_attributes_for :comments
end
localfeed.rb
class Localfeed < ActiveRecord::Base
attr_accessible :city, :scribble_id, :location_id, :localfeed_id, :scribble_attributes
belongs_to :location
has_many :scribbles, :as => :scribbled
accepts_nested_attributes_for :scribbles
validates :city, :presence => true, :uniqueness => true
Here is the error
NoMethodError in LocalfeedsController#newlocalscribble
undefined method `scribbles' for nil:NilClass
Rails.root: c:/workspace/uu2
Application Trace | Framework Trace | Full Trace
app/controllers/localfeeds_controller.rb:80:in `newlocalscribble'
localfeeds/show.erb.html
<% #newlocalscribble = #localfeed.scribbles.new %>
<%=render :partial => 'newlocalscribble.html.erb', :locals => {:newlocalscribble => #newlocalscribble, :localfeed => #localfeed}%>
localfeeds/_newlocalscribble.html.erb
<%= form_for #newlocalscribble, :remote => true, :url => url_for(:controller => 'localfeeds', :action => 'newlocalscribble') do |f| %>
<div class="">
<div class="field">
<%= f.text_area :post,:rows=>3,:placeholder=>"What's on your mind,#{current_user.full_name}?", :class=>"sribble-status-text" %>
<%= f.hidden_field :localfeed, :value => #localfeed.id%>
</div>
<div class="sribble-status-actions" id="newlocalscribble">
<%= f.submit "Share",:class=>"btn btn-info" %>
</div>
</div>
<% end %>
localfeeds_controller.rb
def newlocalscribble
#localfeed = Localfeed.find_by_id(params[:localfeed])
#user = current_user
#newlocalscribble = #localfeed.scribbles.create(params[:localscribble])
##localscribble.scribbled = #user
##localscribble.scribbled = #localfeed
end
def new
#localfeed = Localfeed.new
#feed = Localfeed.find_by_id(params[:localfeed])
#newlocalscribble = #feed.scribbles.new
end
routes.rb
resources :localfeeds do
resource :scribbles
collection do
post 'localscribble', :action => :newlocalscribble
end
end
Variables passed.
{"utf8"=>"✓",
"authenticity_token"=>"1+/Qu/o4EeEbpiL/g07XFa3756IQDo6ldmKH196EkSQ=",
"scribble"=>{"post"=>"hfdfs",
"localfeed"=>"1"},
"commit"=>"Share"}
most likely your variable params[:localfeed] is not set right. It is either nil or the value that came in is not in the database.
Solved It by passing the :locals => {:scribble => #scribble, :localfeed => #localfeed} to pass the right feed ID params to partial, and deleted the hidden field which created a duplicate and cause mass assignment error
Thank you Guys
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.
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| %>
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