Problem with multi loop create method in rails controller - ruby-on-rails

I am having an issue looping through nested form parameters using Stimulus Rails Nested Form. What I have is a nested form where people can select one or many schedules they want to apply the event to, and also create one or many sets of schedule time ranges. So I could say Building A and B on Mon-Wed are going to have an event from 7Am to 12PM. Thurs/Fri an event from 8:00AM to 5:00PM. Any help is appreciated!
I need to keep this info on one table for various reasons so I am trying to loop through the schedules, then loop through the days and times in the time ranges and create separate events for them.
In my create method I have this:
def create
scheduleevent_params[:schedule_ids].each do |schedule_id|
scheduleevent_params[:schedtimeranges_attributes].each do |key, value|
value[:days].each do |day|
#scheduleevent = Scheduleevent.new(schedule_id: schedule_id, day: day, title: scheduleevent_params[:title],
description: scheduleevent_params[:description], start_date: scheduleevent_params[:start_date], end_date: scheduleevent_params[:end_date],
start_time: value[:start_time], end_time: value[:end_time] )
#scheduleevent.user_id = current_user.id
end
end
end
respond_to do |format|
if #scheduleevent.save
params[:start_date] = #scheduleevent.start_date.to_date
format.turbo_stream
format.html { redirect_to scheduleevent_url(#scheduleevent), notice: "Scheduleevent was successfully created." }
format.json { render :show, status: :created, location: #scheduleevent }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: #scheduleevent.errors, status: :unprocessable_entity }
end
end
end
I am getting the error undefined method `each' for nil:NilClass on the [:days].each secondary loop.
params:
def scheduleevent_params
params.require(:scheduleevent).permit( :title, :day, :start_time, :end_time, :description, :start_date, :end_date, :repeattypeid, :hexcolor, :scheduleinstid, :locked, schedule_ids: [],
schedtimeranges_attributes: [ :id, :start_time, :end_time, :repeattype, :_destroy, days: [] ])
end
Here is what is coming through:
{"title"=>"DW1",
"description"=>"DW1",
"start_time"=>"2022-10-24 00:00",
"end_time"=>"2022-10-28 00:00",
"schedtimeranges_attributes"=>{"1666122012736"=>{"days"=>["1", "2", "3"], "start_time"=>"12:00 PM", "end_time"=>"6:00 PM", "_destroy"=>"false"}},
"schedule_ids"=>["", "1", "2"]},
form:
<%= form_with(model: scheduleevent, class: "contents", data: { controller: 'nested-form', nested_form_wrapper_selector_value: '.nested-form-wrapper' }) do |form| %>
<template data-nested-form-target="template">
<%= form.fields_for :schedtimeranges, Schedtimerange.new, child_index: 'NEW_RECORD' do |schedtimeranges| %>
<%= render "schedtimerange_form", form: schedtimeranges %>
<% end %>
</template>
<%= form.fields_for :schedtimeranges do |schedtimeranges| %>
<%= render "schedtimerange_form", form: schedtimeranges %>
<% end %>
<% end %>

{"title"=>"DW1",
"description"=>"DW1",
"start_time"=>"2022-10-24 00:00",
"end_time"=>"2022-10-28 00:00",
"schedtimeranges_attributes"=>{"1666122012736"=>{"days"=>["1", "2", "3"], "start_time"=>"12:00 PM", "end_time"=>"6:00 PM", "_destroy"=>"false"}},
"schedule_ids"=>["", "1", "2"]},
Based on your params, you will need an extra loop to iterate over days
def create
scheduleevent_params[:schedule_ids].each do |schedule_id|
scheduleevent_params[:schedtimeranges_attributes].each do |key, value|
value[:days].each do |day|
#scheduleevent = Scheduleevent.new(scheduleinstid: schedule_id, day: day, title: scheduleevent_params[:title],
description: scheduleevent_params[:description])
#scheduleevent.user_id = current_user.id
#scheduleevent.save
end
end
end
end

Related

Unable to access Multiselect Values in Ruby on Rails Controller

I'm trying to implement a multi-select for a many to many relationship in Ruby on Rails, however I'm unable to access the array of strings representing the selected User Ids in the user_ids field.
The following is my project_params after clicking update
"project"=>{"name"=>"Project Name",
"client"=>"Client X", "project_url"=>""},
"user_ids"=>["2", "3", "4", "5"], "commit"=>"Update Project", "id"=>"1"}
The following is my ProjectsController code
def update
puts project_params[:user_ids=>[]]
respond_to do |format|
if #project.update(project_params)
format.html { redirect_to #project, notice: 'Project was successfully updated.' }
format.json { render :show, status: :ok, location: #project }
else
format.html { render :edit }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
def project_params
params.require(:project).permit(:id, :name, :client, :project_url, :user_ids => [])
end
The line puts project_params[:user_ids=>[]] prints a blank line. I've also tried puts project_params[:user_ids] and puts project_params[:user_ids[]] - none of which work
The following is my _form.html.erb Code
<%= select_tag "user_ids",
options_from_collection_for_select(User.all, "id", "name", #project.user),
{ :multiple => true, :size =>10}
%>
The select tag will use the first argument as the key. Hence the values is located on:
params[:user_ids]
or if you are using form builder
<%= form_for #project do |f| %>
<%= f.select :user_ids,
options_from_collection_for_select(User.all, "id", "name", #project.user),
{ :multiple => true, :size =>10}
%>
<% end %>
then it will be available in project_params

Rails calendar displays duplicate information

I have a calendar which I would like to display details of "visits" which belong to "users" and "branches". This works, in that each visit is displayed on the correct day with the correct information and only displays to the correct user. But for some reason each calendar entry is also showing an array of everything for its database entry like so:
[#<Visit id: 2, location: nil, date_from: "2015-08-12", time_from: "2000-01-01 10:30:00", date_to: "2015-08-20", time_to: "2000-01-01 09:15:00", comment: "trfghytrfg", branch_id: 2, user_id: 2>]
Obviously this doesn't look nice and I don't want it to appear, but I can't see where in my code that its coming from. If anyone can see where I'm going wrong that would be great.
visits_controller.rb:
class VisitsController < ApplicationController
before_filter :authenticate_user!, except: [ :new]
before_action :set_visit, only: [:show, :edit, :update, :destroy]
def index
#visits = Visit.where(user_id: current_user.id)
#visits_by_date = #visits.group_by(&:date_from)
#date = params[:date] ? Date.parse(params[:date]) : Date.today
#users = User.all
end
def show
end
def new
#visit = Visit.new
#branch = Branch.all
end
def edit
end
def create
#visit = current_user.visits.new(visit_params)
respond_to do |format|
if #visit.save
format.html { redirect_to #visit, notice: 'Visit was successfully created.' }
format.json { render :show, status: :created, location: #visit }
else
format.html { render :new }
format.json { render json: #visit.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if #visit.update(visit_params)
format.html { redirect_to #visit, notice: 'Visit was successfully updated.' }
format.json { render :show, status: :ok, location: #visit }
else
format.html { render :edit }
format.json { render json: #visit.errors, status: :unprocessable_entity }
end
end
end
def destroy
#visit.destroy
respond_to do |format|
format.html { redirect_to visits_url, notice: 'Visit was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_visit
#visit = Visit.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def visit_params
params.require(:visit).permit(:location, :date_from, :time_from, :date_to, :time_to, :comment, :branch_id, :user_id)
end
end
visit.rb:
class Visit < ActiveRecord::Base
belongs_to :branch
belongs_to :user #:as => 'created_by'
validates_uniqueness_of :time_from, :scope => [:date_from, :location], :message=>"slot is already taken on selected date"
end
calendar_helper.rb:
module CalendarHelper
def calendar(date = Date.today, &block)
Calendar.new(self, date, block).table
end
class Calendar < Struct.new(:view, :date, :callback)
HEADER = %w[Sunday Monday Tuesday Wednesday Thursday Friday Saturday]
START_DAY = :sunday
delegate :content_tag, to: :view
def table
content_tag :table, class: "calendar table table-bordered table-striped" do
header + week_rows
end
end
def header
content_tag :tr do
HEADER.map { |day| content_tag :th, day }.join.html_safe
end
end
def week_rows
weeks.map do |week|
content_tag :tr do
week.map { |day| day_cell(day) }.join.html_safe
end
end.join.html_safe
end
def day_cell(day)
content_tag :td, view.capture(day, &callback), class: day_classes(day)
end
def day_classes(day)
classes = []
classes << "today" if day == Date.today
classes << "not-month" if day.month != date.month
classes.empty? ? nil : classes.join(" ")
end
def weeks
first = date.beginning_of_month.beginning_of_week(START_DAY)
last = date.end_of_month.end_of_week(START_DAY)
(first..last).to_a.in_groups_of(7)
end
end
end
index.html.erb:
<div id="visits">
<h2 id="month">
<%= link_to "<-", date: #date.prev_month %>
<%= #date.strftime("%B %Y") %>
<%= link_to "->", date: #date.next_month %>
</h2>
<%= calendar #date do |date| %>
<%= date.day %>
<% if #visits_by_date[date] %>
<ul>
<%= #visits_by_date[date].each do |visit| %>
<%= visit.branch.branch_name %>
<% end %>
</ul>
<% end %>
<% end %>
</div>
<%= #visits_by_date[date].each do |visit| %>
Should be:
<% #visits_by_date[date].each do |visit| %>

Why is the check_box value not posting to the db

When I submit the form below the "published" check_box value is not posting.
params3 = {"utf8"=>"✓",
"authenticity_token"=>"i4SbblLJKIwba9yD30sDQCsir28/xdUxQZ90qYTNn0A=",
"story"=>{"name"=>"asdsaddsad", "post"=>"asdasdasdasd",
"user_id"=>"13", "image_id"=>"1", "published"=>"1"}, "commit"=>"Save
Story", "action"=>"create", "controller"=>"stories"}
def create
#story = Story.new(story_params)
respond_to do |format|
if #story.save
format.html { redirect_to #story, notice: 'Story was successfully created.' }
format.json { render action: 'show', status: :created, location: #story }
else
format.html { render action: 'new' }
format.json { render json: #story.errors, status: :unprocessable_entity }
end
end
end
<%= form_for #story do |f| %>
<%= f.text_field :name, placeholder: "Enter Title" %>
<%= f.text_area :post, placeholder: "Enter Story" %>
<br/>
<%= f.hidden_field :user_id, value: current_user.id %>
<%= f.hidden_field :image_id, value: #image.id %>
<%= f.label "Publish this" %>
<%= f.check_box :published %>
<%= f.submit "Save Story" %>
<% end %>
The data that is being passed to the action is story as seen in the params3 hash. Also, the published check box is being posted. Check boxes by default will pass a 1 or 0 to denote true / false. Rails will update the value accordingly and accept 1 or 0 for the value of a checkbox:
params3 = {"utf8"=>"✓", "authenticity_token"=>"i4SbblLJKIwba9yD30sDQCsir28/xdUxQZ90qYTNn0A=", "**story**"=>{"name"=>"asdsaddsad", "post"=>"asdasdasdasd", "user_id"=>"13", "image_id"=>"1", "published"=>"1"}, "commit"=>"Save Story", "action"=>"create", "controller"=>"stories"}
Therefore your object creation will need to use those params. Since you are using Rails 4, you will need to use strong_parameters which seems that you are. You will need to verify that published is an allowed value in your params hash.
def story_params
params.require(:story).permit(...., :published, ...)
end

Rails form with nested attributes (accepts_nested_attributes_for)

I have this one to many relationship:
class Programa < ActiveRecord::Base
attr_accessible :descripcion, :nombre, :roles_attributes
has_many :roles, :dependent => :restrict
accepts_nested_attributes_for :roles
...
end
class Role < ActiveRecord::Base
attr_accessible :description, :name, :programa_id
belongs_to :programa
...
end
It works in rails console:
> params = { programa: { nombre: 'nuevo', roles_attributes: [ {name: 'role1'}, {name: 'role2'}] }}
> p = Programa.create(params[:programa])
> p
=> #<Programa id: 7, nombre: "nuevo", descripcion: nil, created_at: "2013-10-09 14:07:46", updated_at: "2013-10-09 14:07:46">
> p.roles
=> [#<Role id: 15, name: "role1", description: nil, created_at: "2013-10-09 14:07:46", updated_at: "2013-10-09 14:07:46", programa_id: 7>, #<Role id: 16, name: "role2", description: nil, created_at: "2013-10-09 14:07:46", updated_at: "2013-10-09 14:07:46", programa_id: 7>]
But I can not make it work in the app/views/programas/_form:
<%= form_for(#programa) do |f| %>
<%= render 'shared/form_error_messages', object: f.object %>
<div class="field">
<%= f.label :nombre %>
<%= f.text_field :nombre %>
</div>
<div class="field">
<%= f.label :descripcion %>
<%= f.text_field :descripcion %>
</div>
<% f.fields_for :roles do |builder| %>
<div class="field">
<%= builder.label :name %>
<%= builder.text_field :name %>
</div>
<div class="field">
<%= builder.label :description %>
<%= builder.text_field :description %>
</div>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Is there anything else that I have to add or remove to make my form shows the roles nested attributes?
This is my the controller for programas:
class ProgramasController < ApplicationController
# GET /programas
# GET /programas.json
def index
#programas = Programa.all
respond_to do |format|
format.html # index.html.erb
format.json { render json: #programas }
end
end
# GET /programas/1
# GET /programas/1.json
def show
#programa = Programa.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #programa }
end
end
# GET /programas/new
# GET /programas/new.json
def new
#programa = Programa.new
respond_to do |format|
format.html # new.html.erb
format.json { render json: #programa }
end
end
# GET /programas/1/edit
def edit
#programa = Programa.find(params[:id])
end
# POST /programas
# POST /programas.json
def create
#programa = Programa.new(params[:programa])
respond_to do |format|
if #programa.save
format.html { redirect_to #programa, notice: 'Programa was successfully created.' }
format.json { render json: #programa, status: :created, location: #programa }
else
format.html { render action: "new" }
format.json { render json: #programa.errors, status: :unprocessable_entity }
end
end
end
# PUT /programas/1
# PUT /programas/1.json
def update
#programa = Programa.find(params[:id])
respond_to do |format|
if #programa.update_attributes(params[:programa])
format.html { redirect_to #programa, notice: 'Programa was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #programa.errors, status: :unprocessable_entity }
end
end
end
# DELETE /programas/1
# DELETE /programas/1.json
def destroy
#programa = Programa.find(params[:id])
#programa.destroy
respond_to do |format|
format.html { redirect_to programas_url }
format.json { head :no_content }
end
end
end
I just want the nested attributes to be shown in the edit and show actions only.
The form for the nested attributes will only show if there is actually data to show, ie. if your Programa instance has one or more Roles associated with it.
This can be as simple as #programa.roles.build in your controller, before rendering the form, to add a new Role. Any existing roles will be rendered.
edit: You also need to actually render the form, ie. <%= f.fields_for (note missing =).

multiple select

I am trying to create a form that allows to assign multiple projectmembers to a project.
(User and Project model being related through UserProject )
here is my view:
<div class="field">
<%= fields_for :projectmember do |u| %>
<%= u.label :projectmember %><br />
<%= select_tag :projectmember, options_for_select(User.all.collect {|u| [u.id, u.lastname]}, :projectmember),:multiple => true, :prompt => 'Select Person' %>
<% end %>
</div>
i have put the projectmember tag all over the place but i can't figure it out how to save this field projectmember into my db projects and user_projects !!??
my projects_controller :
def new
#project = Project.new
#user_project=UserProject.new
#user=User.all
#user_lastnames = User.all.collect do |u|
u.lastname
end
respond_to do |format|
format.html # new.html.erb
format.json { render json: #project }
end
end
and
def create
#project = Project.new(params[:project])
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render json: #project, status: :created, location: #project }
#user_project=UserProject.create(:project_id => #project.id, :user_id => #project.projectmember)
else
format.html { render action: "new" }
format.json { render json: #project.errors, status: :unprocessable_entity }
end
end
end
After creating one instance, the command using the console : #project=Project.all gives :
Project id: 55, projectname: "fdfd", projectdescription: "fdfd", projectheader: "5", projectmember: nil, projecttypename: "dffd">]
Assuming your associations are something like this:
class Project < ActiveRecord::Base
has_many :project_users
has_many :users, :through=>:project_users
end
...for your project form, use the following helper with the "name" option (:name=>'project[user_ids][]') to pass the selected ids to your controller:
<%= select_tag :user_ids, options_for_select(User.all.collect {|u| [u.lastname, u.id]}),:multiple => true, :prompt => 'Select Person', :name=>'project[user_ids][]' %>
That will give you the selected ids in the params and then you'll have to hook up your controller actions to assign them. For example:
def create
#project = Project.new(params[:project])
users = User.find(params[:project][:user_ids]) rescue []
#project.users = users
if #project.save
...
else
...
end
end
def update
#project = Project.find(params[:id])
users = User.find(params[:project][:user_ids]) rescue []
#project.users = users
if #project.update_attributes(params[:project])
...
else
...
end
end
In order to get the selected items to re-select when you render the edit form, I'm assuming you'd have to add that as an argument to the options_for_select method. Not exactly sure about that. An alternative to doing all of the above would be to use checkboxes to select the users since the checked could be set for each assigned user when the form renders. Good luck.
my model was indeed like that :
class Project < ActiveRecord::Base
has_many :project_users
has_many :users, :through=>:project_users
attr_accessible :colourcode, :projectdescription, :user_id, :projectname, :projectheader, :projectmember, :projecttypename
end
i have needed to change in my view ( else Can't mass-assign protected attributes):
<%= select_tag :user_ids, options_for_select(User.all.collect {|u| [u.lastname, u.id]}),:multiple => true, :prompt => 'Select Person', :name=>'project[user_ids][]' %>
by
<%= select_tag :projectmember, options_for_select(User.all.collect {|u| [u.lastname, u.id]}),:multiple => true, :prompt => 'Select Person', :name=>'project[projectmember][]' %>
and in my controller:
def create
#project = Project.new(params[:project])
users = User.find(params[:project][:projectmember]) rescue []
#project.users = users
respond_to do |format|
if #project.save
format.html { redirect_to #project, notice: 'Project was successfully created.' }
format.json { render json: #project, status: :created, location: #project }
#user_project=UserProject.create(:project_id => #project.id, :user_id => #project.projectmember)
#user_project=UserProject.create(:project_id => #project.id, :user_id => #project.projectheader)
i got now after creation :
#p=Project.all
Project id: 69, projectname: "test", projectdescription: "blabla", colourcode: "blue", projectheader: "5", projectmember: "---\n- '5'\n- '6'\n", projecttypename: "caucasian">]
#up=UserProject.all
UserProject id: 86, project_id: 69, user_id: 5, created_at: "2012-06-07 13:04:51", updated_at: "2012-06-07 13:04:51">
UserProject id: 87, project_id: 69, user_id: 6, created_at: "2012-06-07 13:04:51", updated_at: "2012-06-07 13:04:51">,
which i am happy about :)
still need to :
delete automatically the project/user couple if the project is deleted
show on the screen the corresponding member names rather than an id number...

Resources