ruby - populating combobox - ruby

I have two objects ingredient & origin.
each ingredient has an origin so in the ingredient I have origin_id
the view displays
<p>
<b>Name:</b>
<%= #ingredient.name %>
</p>
<p>
<b>Origin:</b>
<%= #ingredient.origin_id %>
</p>
class ingredient is declared as follows
class Ingredient < ActiveRecord::Base
has_and_belongs_to_many :recipes
belongs_to :origin
attr_accessible :name, :origin_id
end
class origin
class Origin < ActiveRecord::Base
attr_accessible :name
end
in the edit form I write and it works
<% originsArray = Origin.all.map { |origin| [origin.name, origin.id] } %>
<div class="field">
<%= f.label :origin_id %><br/>
<%= f.select(:origin_id, originsArray) %><br/>
</div>
but if I write instead (as in all tutorials)
<%= collection_select(:origin, :id, #origins, :id, :name, options ={:prompt => "-Select a payment"}, :class =>"origin") %>
i get
undefined method `map' for nil:NilClass
what should I fix ?
Edit
added :
def edit
#ingredient = Ingredient.find(params[:id])
#origins = Origin.all
end

I don't see here where #origins is declarated. It seems like your #origins is just nil and not collection.

Related

Rails 5: Is Nested forms set up correct

If I want a Nested Form for volunteer_shift
inside my assignment/_form
How should my model associations be set up?
My current associations look like this..but I am not sure they are correct if the volunteer_shift partial sits inside the assignment form....
When I try to open the "add new record" form...my assignment#new action errors out...
error message
Completed 500 Internal Server Error in 30ms (ActiveRecord: 2.7ms)
NoMethodError - undefined method `build' for nil:NilClass:
app/controllers/assignments_controller.rb:21:in `new'
Here is my new action...
# GET /assignments/new
def new
#assignment = Assignment.new
# binding.pry
#assignment.volunteer_event.build <----Line 21
#my_url = {:action => "create", :id => params[:id]}
end
Here are my current models associations...
class Assignment < ApplicationRecord
attr_accessor :volunteer_event
belongs_to :volunteer_shift
has_one :volunteer_task_type, :through => :volunteer_shift, :source => :volunteer_task_type
belongs_to :contact ,optional: true
validates_presence_of :volunteer_shift #belongs_to takes care of this now
validates_associated :volunteer_shift
accepts_nested_attributes_for :volunteer_shift, allow_destroy: true
...
class VolunteerShift < ApplicationRecord
has_many :assignments
belongs_to :volunteer_event
...
class VolunteerEvent < ApplicationRecord
belongs_to :volunteer_default_event
has_many :volunteer_shifts, :dependent => :destroy
has_many :resources_volunteer_events, :dependent => :destroy
validates_associated :volunteer_shifts
And inside my assignment/form
<%= form_for #assignment, :url => #my_url, remote: true do |f| %>
...
<!--VOLUNTEER SHIFT-->
<!--TODO: make this a partial under field_for-->
<%= f.fields_for :volunteer_shift do |builder| %>
<%= render 'volunteer_shift_fields', vs: builder %>
<% end %>
...
<% end %>
and if you want to see the volunteer_shift_fields partial
<div class="name large flex-row">
<%= vs.label :volunteer_shift %>
</div>
<div id="volunteer_shift" class="d-flex flex-row">
<div class="col-sm-12 p-2">
<div id="volunteer_shift" class="text-right">
<!-- old if: if class is assignment show volunteer shift else show default shift -->
<!-- we need default shift here...NO assignment is attached-->
<div class="field">
<%= vs.label :volunteer_task_type_id %>
<%= select_tag 'volunteer_task_type_id', options_from_collection_for_select([VolunteerTaskType.new(:description => ""), VolunteerTaskType.instantiables.effective_on(Date.today)].flatten, "id", "description") %>
</div>
<div class="field">
<%= vs.label :roster_id %>
<%= select_tag 'roster_id', options_from_collection_for_select([Roster.new(:name => ""), Roster.all].flatten, "id", "name") %>
</div>
<div class="field">
<%= vs.label :program_id %>
<%= select_tag 'program_id', options_from_collection_for_select([Program.new(:name => ""), Program.where(:volunteer => true)].flatten, "id", "name")%>
</div>
<div class="field">
<%= vs.label :set_description %>
<%= vs.text_field(:set_description, nil) %>
</div>
<div class="field">
<%= vs.label :set_date, "Date" %> <
<%= vs.text_field(:set_date, nil) %>
</div>
</div>
</div>
</div>
Are my model associations correct?
What is my "assignment#new" action missing?
Remove attr_accessor :volunteer_event, you need to set up the model association to volunteer_event through volunteer_shift
so
class Assignment < ApplicationRecord
attr_accessor :volunteer_event
needs to become
class Assignment < ApplicationRecord
has_one :volunteer_event, through: :volunteer_shift
Looking at your volunteer_shift associations I'm pretty sure that you only want to ever have a single volunteer_event record for a single assignment? if that's not what you want then you need to look at your associations again
Or...
You could change the attr_accessor to attr_reader and assign the relevant volunteer_event record in an initializer but I would not recommend that approach for this case

Inserting multiple rows in Rails for single form

There are three fields in this form:
employee
project
project
The field project comes up twice and so I want two records created in this case. If I put in the values:
employee: John Doe
project: Project_1
project: Project_2
I would like two records in the model:
employee: John Doe; project: Project_1
employee: John Doe; project: Project_2
This is the view:
<%= simple_form_for(#source) do |f| %>
<div class="form-group">
<%= f.label :employee %>
<%= f.text_field :employee, class: "form-control" %>
</div>
<div class="form-group">
<%= f.input :project, class: "form-control" %>
<%= f.input :project, class: "form-control" %>
</div>
<% end %>
Here is my code for the application controller:
def create
#source = Source.new(source_params)
if #source.save
redirect_to #source, notice: 'Source was successfully created.'
else
render action: 'new'
end
end
Any help will be greatly appreciated.
In your controller, you should build the model along with two projects, then use the form helper fields_for, which will render both project fields
your_controller.rb
class YourController
def your_action_new
#object = YourModel.new
2.times{ #object.projects.build }
end
end
For the view, I don't really know how simple_form behaves, but basically
views/your_views/new.html.erb
<!-- blabla -->
<%= f.fields_for :project do |project_f| %>
<div class="project">
<%= project_f.text_field(:name) %>
<%= project_f.text_field(:description) %>
...
</div>
<% end %>
ALso don't forget in to accept nested attributes
class YourModel
has_many :projects, dependent: :destroy
accept_nested_attributes_for :projects
end
class YourController
def your_model_params
params.require(:your_model).permit(blabla, projects_attributes: [:id, :name, :blabla, ...])
end
end
end

Unpermitted parameters for nested form many-to-many relationship Rails 4

I have a simple blog app, where I want to be able to create a Post and also create a new Tag for it in the same form, using a nested form.
Post and Tag have a many-to-many relationship, via a join table:
class PostTag < ActiveRecord::Base
belongs_to :post
belongs_to :tag
end
Here's the tag model:
class Tag < ActiveRecord::Base
has_many :post_tags
has_many :posts, :through => :post_tags
validates_presence_of :name
validates_uniqueness_of :name
end
Post model accepts nested attributes for tags:
class Post < ActiveRecord::Base
has_many :post_tags
has_many :tags, :through => :post_tags
accepts_nested_attributes_for :tags
validates_presence_of :name, :content
end
On the posts controller, I permit tags_attributes:
def post_params
params.require(:post).permit(:name, :content, :tag_ids => [], :tags_attributes => [:id, :name])
end
In my form for a new post, where I want to be able to either associate already existing tags (via checkboxes) or create a new one via a nested form using fields_for:
....
<div class="field">
<%= f.collection_check_boxes :tag_ids, Tag.all, :id, :name %><br>
<%= f.fields_for [#post, Tag.new] do |tag_form| %>
<p>Add a new tag:</p><br>
<%= tag_form.label :name %>
<%= tag_form.text_field :name %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
My error is "Unpermitted parameters: tag":
Parameters: {"utf8"=>"✓", "authenticity_token"=>"dZnCgFxrvuoY4bIUMMxI7kTLEr/R32pUX55wwHZsS4Q=", "post"=>{"name"=>"post title", "content"=>"post content", "tag_ids"=>[""], "tag"=>{"name"=>"new tag"}}, "commit"=>"Create Post"}
Unpermitted parameters: tag
Change:
<%= f.fields_for [#post, Tag.new] do |tag_form| %>
to
<%= f.fields_for(:tags, Tag.new) do |tag_form| %>

Nested attributes that do not belong to the current object using has_many :through

My end goal is to be able to add costumes to an agreement in the agreement view, regarless of whether or not they exist in the Costume database yet. My main difficulty is that a costume does not belong to an agreement, they exist independently but can be added to an agreement. If a new costume is added that isn't in the Costume database, it will add it to the database. Is there a way to do this? I can't find a tutorial about this anywhere. If I could get the controller from this post, I think that is all I need. I just need to create one costume every time the form is displayed. Thanks so much.
My models are as follows:
# app/models/agreement.rb
class Agreement < ActiveRecord::Base
has_and_belongs_to_many :costumes, join_table: :agreement_costumes
accepts_nested_attributes_for :costumes, :reject_if => :all_blank
end
# app/models/costume.rb
class Costume < ActiveRecord::Base
has_and_belongs_to_many :agreements, join_table: :agreement_costumes
end
# app/models/agreement_costume.rb
class AgreementCostume < ActiveRecord::Base
belongs_to :agreement
belongs_to :costume
end
My agreement controller is as follows:
class AgreementsController < ApplicationController
before_action :set_agreement, only: [:show, :edit, :update, :destroy]
# Some methods ommitted
# GET /agreements/new
def new
#agreement = Agreement.new
#agreement.costumes.build
#costumes = Costume.all
end
private
# Use callbacks to share common setup or constraints between actions.
def set_agreement
#agreement = Agreement.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def agreement_params
params.require(:agreement).permit(:name, :phone, :email, :mailbox, :wesid, :title, :start, :end, :due, :financer, :employee, :costumes_attributes => [:cid, :description, :wd, :back, :photo])
end
:end
And finally, the agreement view
<%= form_for(#agreement) do |f| %>
<% if #agreement.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#agreement.errors.count, "error") %> prohibited this agreement from being saved:</h2>
<ul>
<% #agreement.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
<!-- Agreement fields omitted -->
<%= f.fields_for :costumes do |c| %>
<div class="field">
<%= c.label :cid %><br>
<%= c.number_field :cid %>
</div>
<div class="field">
<%= c.label :description %><br>
<%= c.text_field :description %>
</div>
<!-- etc with costume fields -->
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
You need accepts_nested_attributes_for in your Agreement controller if you want to create new costumes there.
class Agreement < ActiveRecord::Base
has_many :agreement_costumes
has_many :costumes, through: :agreement_costumes
accepts_nested_attributes_for :costumes, :reject_if => :all_blank, :allow_destroy => :false,
end
and then in the agreements#new action in your Agreements controller you build a costume entry
def new
#agreement = Agreement.new
#agreement.costumes.build
#costumes = Costumes.all
end
#agreement.costumes.build creates a blank instance of a costume, related to this agreement. You then access the params of that costume in the form using :costumes Don't forget to whitelist your nested params in your Agreements controller:
def agreement_params
params.require(:agreement).permit(:name, :phone, :email, :mailbox, :wesid,
:title, :start, :end, :due, :financer, :employee, costumes_attributes[:name, :price, :id])
end
Now your form has to have a place to choose existing costumes from a list and/or add a new one.
<%= f.label :costumes, "Costumes" %>
<%= f.collection_select :costumes, :agreement_id, :id, :name, price, {}, {multiple: true} %>
<strong>Add a new costume</strong>
<%= f.fields_for :costumes do |c|
<%= c.label :name %>
<%= c.text_field :name %>
<br>
<%= c.label :price %>
<%= c.number_field :price %>
<br>
<% end %>
This should get you most of the way there. I've had to guess at some of your code, so this isn't going to be a cut and paste answer. You'll need to build what you can off of this and probably do a little more Googling here and there. If you wanted to have a popup form to add a costume to the list on the fly and then be able to choose it in the collection_select, you would have to turn on Turbo_links in your app, create a Javascript popup form. Then use AJAX to submit the form, save the costume to the database, run another .js.erb file that would then update the collection_select text list using a reload of just that list via your Javascript. It's actually probably easier than having a new costume form nested in this form.

Rails 3 Nested Form not being created

The models I'm working with look like this:
class ComplexAssertion < ActiveRecord::Base
has_many :expression_groups
has_many :expressions, :through => :expression_group
accepts_nested_attributes_for :expression_groups, :allow_destroy=>true
end
class ExpressionGroup < ActiveRecord::Base
belongs_to :complex_assertion
has_many :expressions
accepts_nested_attributes_for :expressions, :allow_destroy=>true
end
class Expression < ActiveRecord::Base
belongs_to :expression_group
end
My form looks like the following:
<%= form_for(#complex_assertion) do |f| %>
<div id="mainAssertionGroup" style="border:1px; border-style:solid; width:1000px; padding:5px">
<div class="field">
<%= f.label :title %>: <%= f.text_field :title, :size=>'10' %>
<%= f.label :description %>: <%= f.text_field :description, :size=>'25' %>
<%= f.label :scope %>: <%= f.text_field :scope, :size=>'1' %>
Test
Category: <%= collection_select(:complex_assertion, :assertion_category_id, AssertionCategory.all, :id, :name, {:include_blank=>"UNCATEGORIZED"}) %>
</div>
<div id="initialGroup" style="border:1px; margin-left:10px; margin-top:10px; border-style:solid; width:850px;">
<div class="childGroup1" style="padding:5px;">
<%= f.fields_for :expression_groups do |eg| %>
<%= eg.fields_for :expressions do |e| %>
Type: <%= e.collection_select :assertion_type_id, AssertionType.all, :id, :name %>
Attribute: <%= e.collection_select :attribute_name, Attribute.find_by_sql("select distinct a.name from attributes a "), :name, :name %>
<%= e.label :operator_type_id %>
: <%= e.collection_select :operator_type_id, OperatorType.all, :id, :value %>
Value: <%= e.text_field :value, :size=>'1' %>
<% end %>
<div id="innerOperator">
<%= eg.collection_select :logical_operator_type_id, LogicalOperatorType.all, :id, :value %>
</div>
<% end %>
</div>
</div>
</div>
<div id="createComplex" align="center">
<%= f.submit :value=>'Submit' %>
</div>
<% end %>
And my controller looks like:
def new_complex_assertion
#complex_assertion = ComplexAssertion.new
end
When I load the page, I only the ComplexAssertion portion of the form and get nothing back for the ExpressionGroups or the Expressions. It's as if there isn't anything available. But, if you see my controller, I did a ComplexAssertion.new which I though would create the dependent objects automagically; I assume I'm incorrect?
I'm debugging through RubyMine and when I evaluate the ComplexAssertion.new, I only see 5 attributes, the five that are defined for only that object, none of the relational objects. What am I doing incorrectly?
EDIT
Looks like if I do the following:
#complex_assertion = ComplexAssertion.new
#complex_assertion.expression_groups.build
#complex_assertion.expressions.build
And change my form to use:
<%= f.fields_for :expressions do |e| %>
instead of eg.fields_for, it shows the forms.
This DOES NOT give me the correct nesting. I thought I should be able to do:
#complex_assertion.expression_groups.expressions.build
but it tells me that expressions is an undefined method.
Yes, you have to explicitly instantiate the associated objects. It is not done for you.
#complex_assertion.expression_groups.expressions.build
Will not work because expression_groups is an array and not an individual expression group. So, after you create the expressions_groups do the following:
#complex_assertion.expressions_groups.each do |group|
group.expressions.build
end
Also, you could replace the 2nd line with the following as well to create multiple expressions
2.times do { group.expressions.build }
As for using fields_for with nested models, make your code in the form look like this:
<%= f.fields_for :expression_groups, #complex_assertions.expression groups do |eg| %>
<%= eg.fields_for :expressions, eg.object.expressions do |e| %>
I will try to explain what is going on. The :expressions_groups is telling fields_for what class of object it is going to render fields for, and the second part I added is telling fields_for where to find the object(s) to render fields for. If we are passing in an array, which we are in this case, it will automatically iterate over the array. On each iteration, it puts the current model object we are working with into a variable called object which is stored in the form builder instance returned by fields_for. So we use this to tell the second fields_for where to find the expression model objects it needed. This means eg.object points to an expression_group model object.
I hope this helps and makes sense. Also, I have not tested anything and am only pointing out what looks out of place.

Resources