I have a parent model that accepts child attributes.
class User < ActiveRecord::Base
accepts_nested_attributes_for :spec
attr_accessible :name, :spec_attributes
In the view I have a form that gets information for 3 models. I use a generic form_tag.
<% form_tag(action) do %>
.
.
.
<% fields_for "user[spec_attributes]" do |spec_form|%>
<%= spec_form.check_box :alert_greeting %>
<%= spec_form.label :alert_greeting, "Email me when new greetings are posted" %>
<% end %>
<% end %>
In the Controller
#user = User.find(session[:user_id])
if #user.update_attributes(params[:user])
do something.
end
The database is getting updated and the all seems to be working.
However when I go back to the form to edit again, even though the value for the checkbox is showing 1 the check box is not checked.
Any ideas as to how to show the checkbox as being checked when it is supposed to be?
Thanks a lot in advance.
You need to reference the specific spec record that is within the user you're calling. Try changing
<% fields_for "user[spec_attributes]" do |spec_form|%>
to
<% fields_for #user.spec do |spec_form|%>
You'll need to make sure that you have a non-nil spec object built for the user (but not necessarily saved) in your edit controller action.
You can do attributes and nested attributes using two fields_for calls like this:
<%= form_tag(action) do %>
... other form tags ...
<%= fields_for :user, #user do |f| %>
<%= f.text_field :first_name %>
<%= f.text_field :last_name %>
<%= f.fields_for :spec do |s| %>
<%= s.check_box :alert_greeting %>
<%= s.label :alert_greeting, "Email me when new greetings are posted" %>
<% end %>
<% end %>
<% end %>
Related
I'm new to RoR. Wanted to try if my next web app should be RoR based. Started out following this trail: https://guides.rubyonrails.org/getting_started.html. Worked like a charm in the beginning, but i'm unable to get the darn thing to create new records. Any hint as to what i'm missing is appreciated.
The error i'm getting is this:
D, [2020-12-18T09:59:56.917197 #132399] DEBUG -- : Createevent
F, [2020-12-18T09:59:56.917893 #132399] FATAL -- :
ActionController::ParameterMissing (param is missing or the value is empty: Event):
app/controllers/event_controller.rb:33:in `event_params'
My routing looks like this:
Rails.application.routes.draw do
post 'event/new', to: 'event#create'
resources :event
end
(I'm baffled by the need for specifying the POST above, but without it the create is never fired. ).
The eventcontroller looks like this:
class EventController < ApplicationController
def index
#events = Event.all
end
def show
#event = Event.find(params[:id])
end
def new
logger = Rails.logger
logger.info 'NewEvent'
#event = Event.new
end
def create
logger = Rails.logger
logger.debug 'Createevent'
#event = Event.new(event_params)
logger.debug 'Eventcreated'
if #event.save
redirect_to event_path
else
render :new
end
end
private
def event_params
params.require(:Event).permit(:EventName, :Description, :EventStart, :EventEnd, :Maxparticipants, :Waitlist )
end
end
Index and show works fine.
The new.html.erb looks like this:
<h1>New Event</h1>
dsfsdfds
<%= form_with model: #Event do |form| %>
<div>
<%= form.label :eventname %><br>
<%= form.text_field :EventName %>
<%= #event.errors.full_messages_for(:EventName).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :Description %><br>
<%= form.text_field :Description %>
<%= #event.errors.full_messages_for(:description).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :EventStart %><br>
<%= form.text_field :EventStart %>
<%= #event.errors.full_messages_for(:eventstart).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :Eventend %><br>
<%= form.text_field :Eventend %>
<%= #event.errors.full_messages_for(:eventend).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :Maxparticipants %><br>
<%= form.text_field :Maxparticipants %>
<%= #event.errors.full_messages_for(:Maxparticipants).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.label :Waitlist %><br>
<%= form.text_field :Waitlist %>
<%= #event.errors.full_messages_for(:waitlist).each do |message| %>
<div><%= message %></div>
<% end %>
</div>
<div>
<%= form.submit %>
</div>
<%= submit_tag "Create" %>
<% end %>
Routes:
Prefix Verb URI Pattern Controller#Action
event_new POST /event/new(.:format) event#create
event_index GET /event(.:format) event#index
POST /event(.:format) event#create
new_event GET /event/new(.:format) event#new
edit_event GET /event/:id/edit(.:format) event#edit
event GET /event/:id(.:format) event#show
PATCH /event/:id(.:format) event#update
PUT /event/:id(.:format) event#update
DELETE /event/:id(.:format) event#destroy
Things created using:
bin/rails generate model Event EventName:string Description:string EventStart:datetime EventEnd:datetime Maxparticipants:integer WaitList:integer
bin/rails generate controller Event index
Version:
About your application's environment
Rails version 6.0.3.4
Ruby version ruby 2.7.2p137 (2020-10-01 revision 5445e04352) [x86_64-linux]
RubyGems version 3.1.4
Rack version 2.2.3
Thanks to Hackman & nathanvda for clarifying. I scratched everything and started over and i finally got it working. Still way too much woodo and black magic for my taste though. The error message part got me baffled for three consecutive hours.
As stated i followed the guide and therefore ended up using (in new)
<%= form_with model: #event do |form| %>
<% if #event.errors.any? %>
<h2>Errors</h2>
<ul>
<% #event.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
<% end %>
...
Which works (well, sort of). The record gets saved if ok, the validations rules gets fired
class Event < ApplicationRecord
validates :eventname, presence: true
validates :description, presence: true, length: { minimum: 20 }
end
and if violated no record gets written to the database, but no error messages either. Nil. Nothing. After poking around on the internet i ended up changing to
<%= form_for #event do |form| %>
and then error reporting works. Only problem with this solution is that the use of form_for is discouraged as it is being rendered obsolete.
Final version ended up being:
<%= form_with model: #event, local: true do |form| %>
Which does the trick.
Next step in my evaluation will be the use of natural keys as the use of surrogate keys is not an option for some of the data structures needed in this project. (during my poking around i got the impression that natural keys are some kind of a sore tooth in RoR, but time will show.
To start out, your resources in the routes should be pluralized. So resources :event should be resources :events
Also the controller name should be pluralized. So EventController would become EventsController.
Now the needed routes should work fine and you can get rid of the specified POST in your routes.rb
Now inside your controller you have the event_params method. There it is preferred to downcase/snake_case the names like this:
def event_params
params.require(:event).permit(:event_name #etc)
end
If your column names in DB are EventName etc, I would advice to rename them.
Last thing: In your form you got #Event with uppercase while in the controller#new action you defined #event with lowercase. Use lowercase everywhere.
So if you had started as follows:
bin/rails generate model Event event_name:string description:string event\-start:datetime event_end:datetime max_participants:integer wait_list:integer
bin/rails generate controller events index
Then the generated code would work a lot better.
A few tips to clarify:
in ruby we only write classes with a capital, for variables we use snake case (everything lowercase and words connected with underscores). So by extension when generating a model all attributes should be snake cased
a controller in general uses the plural form, since it "controls" all the events (not just one).
I have an edit form that is not a devise form (i have a devise edit on a different view) to edit a users details. However the form inputs only appear if the data is already there. So a user is unable to add new details to the form, as the inputs don't appear at all.
Is this happening as i'm not using a devise form view?
This is the code in my own edit file:
<%= render "devise/registrations/details", f: f, resource: #resource, addressable: #resource, default_location: nil %>
Then in the devise/registration/details i have this:
<%= f.simple_fields_for :address do |fields| %>
<%= render "address/fields", fields: fields, addressable: addressable, resource: resource %>
<% end %>
However, i think the inputs are not showing up as fields are blank in the iteration. However these fields are showing up in the actual devise/edit file, even if they are blank, just not in my new one.
When using fields_for and simple_fields_for (which is basically just a pimped up version of the former) you have to "seed" the inputs in order for them to appear.
Consider this simplified plain rails example:
class UsersController < ApplicationController
def new
#user = User.new
#user.build_address
end
# ...
end
<%= simple_form_for(#user) do |f| %>
<%= f.simple_fields_for(:address) do |af| %>
<%= af.input :street %>
<% end %>
<% end %>
If we remove the line #user.build_address there will be no nested inputs. Thats because simple_fields_for calls the method #address on #user and creates inputs for the record. If the association is one or many to many it would iterate through the association. If the method returns nil or empty there are no inputs to create - it would be like calling form_for with nil.
You can also pass a second argument to fields_for to manually specify the record object:
<%= simple_form_for(#user) do |f| %>
<%= f.simple_fields_for(:address, #user.address || #user.build_address) do |af| %>
<%= af.input :street %>
<% end %>
<% end %>
In Devise you can do this by passing a block to super or by overriding #build_resource.
class MyRegistationsController < Devise::RegistrationsController
def new
super { |resource| resource.build_address }
end
end
I'm new to Rails and I'm doing my first project. Also, English is not my native language so bear with me, please.
The problem I'm having is that I have a form with multiple instances of the same model, the data is being created correctly but when I try to edit it the form is populated in the wrong way.
I'm making an app to check if everything goes according to the rules.
The items to be checked are in a nested association Chapters->Subchapters->Checks
Every time the checks are submitted a CheckRound is created and the information of every check is stored separately in CheckResults.
CheckRounds
has_many :check_results, inverse_of: :check_round, dependent: :destroy
accepts_nested_attributes_for :check_results, reject_if: proc { |att| att['observation'].blank? }
CheckResults
belongs_to :check_round, optional: true, inverse_of: :check_results
belongs_to :check
Chapters
has_many :subchapters
Subchapters
belongs_to: chapter
has_many: checks
Checks
belongs_to :subchapter
has_many :check_results
The form displays all the Chapters and the nested Subchapters and Checks.
Every Check displays its name and has a text_area as an input.
The user can fill none or many Checks.
<%= form_for(#check_round, :url => {:action => 'update', :client_id => #client.id, :project_id => #project.id}) do |f| %>
<% #chapters.each do |chapter| %>
<%= chapter.name %>
<% chapter.subchapters.each do |subchapter| %>
<%= subchapter.name %>
<% subchapter.checks.each do |check| %>
<%= f.fields_for :check_results do |result| %>
<%= check.name %>
<%= result.hidden_field(:check_id, :value => check.id) %>
<%= result.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s) %>
<% end %>
<% end %>
<% end %>
<% end %>
<% end %>
The controller is
def edit
#check_round = CheckRound.includes(:check_results).find(params[:id])
#chapters = Chapter.includes(subchapters: :checks).where("segment_id = ?", #project.segment_id).sorted
end
If for example, I submit that check.id = 3 has the observation = "bad" when I go to edit every check has "bad" in its observation regardless of its id.
I want to know how can I show in edit all the checks with a blank observation but the ones that were created.
Thanks in advance for your time!
Ok, From what i see 2 things that needs to fixed.
1st, your f.fields_for :check_results do |result|
needs an extra parameter to specify which check_results it exactly has to modify... somethings like this:
f.fields_for :check_results, #check_round.check_results.where(check_id: check.id) do |result|
in the exact same place so the check variable is specify the right way.
2de, you need to permit your nested parameters in your controller so they can be saved when u submit. Normally you should see a method called check_round_params in your check_round controller.
this one have to like this for everything to work:
def check_round_params
params.require(:check_round_params).permit(
/*your needed params*/,
check_results_attributes: [:id, :check_id, :observation, /*all your nested params*/]
)
end
In short, your update and your create actions work according to those permitted params, so you need define them there. check_results_attributes: is the way that rails understands those params are for nested models.
Here is some documentation you might find interesting:Nested attributes example
Here is the solution i've promised.
Sinds you have already defined that check results with blank observations had to be rejected and there will to much logic involved in your erb for its own sake, i would put it all in an helper method so your erb will be cleaner. Something like this:
#helpers/check_rounds_helper.rb
def edit_or_instantiate_nested_check_results(f, check_round, check, new_check_result)
if check.check_results
f.fields_for :check_results, check_round.check_results.where(check_id: check.id) do |result|
result.hidden_field(:check_id, :value => check.id)
result.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s)
end #end for the already present check results
# if u want to add a new check result event if the check is populated
f.fields_for :check_results, new_check_result do |new|
new.hidden_field(:check_id, :value => check.id)
new.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s)
end #end for the new check result
else #if there is no existing check result nest a form for a new one
f.fields_for :check_results, new_check_result do |new|
new.hidden_field(:check_id, :value => check.id)
new.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s)
end #end for the new check result
end #end if statement
end
Then in your view:
<%= form_for(#check_round, :url => {:action => 'update', :client_id => #client.id, :project_id => #project.id}) do |f| %>
<% #chapters.each do |chapter| %>
<%= chapter.name %>
<% chapter.subchapters.each do |subchapter| %>
<%= subchapter.name %>
<% subchapter.checks.each do |check| %>
<%= check.name %>
<% new_check_result = CheckResult.new(check_round_id: #check_round.id, check_id = check.id) %>
<%= edit_or_instantiate_nested_check_results(f, #check_round, check, new_check_result) %>
<% end %>
<% end %>
<% end %>
<% end %>
And that shoud be it ;). Let me know if it did the trick :D!
KR,
I believe it works like you want with this (code with some simplifications):
Check
class Check < ApplicationRecord
belongs_to :subchapter
has_many :check_results
def check_results_for_form check_round_id
results = check_results.where(check_round_id: check_round_id)
results.any? ? results : check_results.build
end
end
CheckRoundsController
def edit
#check_round = CheckRound.find(params[:id])
#chapters = Chapter.includes(subchapters: :checks).all
end
edit.html.erb
<%= form_for(#check_round, :url => {:action => 'update'}) do |f| %>
<ul>
<% #chapters.each do |chapter| %>
<li>
<%= chapter.name %>
chapter
<ul>
<% chapter.subchapters.each do |subchapter| %>
<li>
<%= subchapter.name %>
subchapter
<ul>
<% subchapter.checks.each do |check| %>
<li>
<%= check.name %>
check
<br>
<%= f.fields_for :check_results, check.check_results_for_form(#check_round.id) do |result| %>
<%= result.hidden_field(:check_id, :value => check.id) %>
<%= result.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s) %>
<% end %>
</li>
<% end %>
</ul>
</li>
<% end %>
</ul>
</li>
<% end %>
<ul>
<%= f.submit %>
<% end %>
Your problem is that you are repeating the display of the form fields for check_results. Look at line 7 of your view code:
<%= f.fields_for :check_results do |result| %>
This is displaying the fields for each check result on f.object (which is #check_round). However, this code gets repeated for each check in subchapter. That surrounding block gets repeated for each subchapter in chapter, and the block surrounding that gets repeated for each chapter in #chapters.
When the form is submitted, the params for check_results all have the same names, they are not distinguished by chapter, subchapter, or check. As a result, the only value that gets saved for observation is the last one submitted.
I think a solution for your case would be to only show the check_result form fields associated with the current check in the loop. One way to do that is to put a conditional in the loop starting on line 7 of your view code:
<%= f.fields_for :check_results do |result| %>
<% if result.object.check == check %>
<%= result.hidden_field(:check_id, :value => check.id) %>
<%= result.text_area(:observation, rows: 4, :id =>'obs' + check.id.to_s) %>
<% end %>
<% end %>
<% end %>
You could also just loop through the check_results independently of the loops for checks, subchapters, and chapters, but I'm assuming that you want to keep that order and context for the UI.
I want to update a model - only lines where the checkox is clicked and insert a remark
View:
<%= form_tag update_fb_instruction_users_path, :method => :put do %>
<% #user_wishes.each do |u| %>
<%= u.user.name %>
<%= fields_for "instruction[]", u do |f| %>
<%= f.text_field :remark_tl %>
<% end %>
<%= check_box_tag "instruction_user_ids[]", u.id %>
<% end %>
Controller:
def update_fb
params[:instruction_user_ids].each do
#check = InstructionUser.update(params[:instruction].keys, params[:instruction].values).reject { |p| p.errors.empty? }
end
The issue there is that they all have the same name. So whatever value the last one is, that's what it will be in the request params.
It's a bit old, but you might want to check out the railscast here: http://railscasts.com/episodes/73-complex-forms-part-1. The basic idea is to use fields_for on top of each user object. I haven't done it myself before, otherwise i'd write a full solution :).
How do i get checkbox values in the form from the database? I want the form to bring the existing sub category name,and when i check the checkbox to select that particular category name and not create a new one.I have tried ryan bate's railscast but was no help to me. The realationship here is Category has_many SubCategories and SubCategory belongs_to Category.Thank you.
<%= form_for #category ,:url=>{:action =>"create"} do |f| %>
<%=f.text_field :category_name %>
<%= f.fields_for :sub_categories do |s| %>
<% #category.sub_categories.each do |sub|%>
<%=s.check_box "name",{},sub.id %> <!--need help here-->
<%end%>
<%end%>
<%=f.submit "submit"%>
<%end%>
Based on the exchange in the comments, it appears that you want to use the checkboxes to assign SubCategory objects to a Category object. If that's the case, you're association should be that a Category has_and_belongs_to_many :sub_categories. Then your form would look something like:
<%= form_for #category ,:url=>{:action =>"create"} do |f| %>
<%=f.text_field :category_name %>
<% SubCategories.each do |sc| %>
<div>
<%= check_box_tag :sub_category_ids, sub_category_id, #category.sub_categories.include?(sc), :name => 'category[sub_category_ids][]' -%>
<%= label_tag :sub_category_ids, sc.name -%>
</div>
<% end -%>
<% end %>
Which will show a category form and then list all of the sub_categories that can be assigned or unassigned by checking the checkboxes.
You will also need a join table "categories_sub_categories" for this new association and logic (likely in your controller) to handle the actual assignment.
example for your category_controller.rb
def create
#category = Category.find(params[:id])
#use the checked sub_category_ids from the form to find and assign the sub_categories.
assigned_sub_categories = SubCategory.find(params[:category][:sub_category_ids]) rescue []
#category.sub_categories = assigned_sub_categories
if #category.save
…
else
…
end
end