Rails Simple Form Select from Array - polymorphic associations - ruby-on-rails

I am making an app in Rails 4. I use Simple Form for forms.
I have a profile model and an organisation model.
The associations are:
profile.rb
has_one :organisation, as: :orgable
organisation.rb
has_many :profiles
In my organisation table, I have an attribute called :org_type.
In my organisation form, I ask users to select from an array of types of organisation:
<%= f.input :org_type, :label => "Organisation type", collection: [ "University", "Research Organisation", "Company"] %>
In my profile form, I want to ask users which uni they study at.
I want to use the array of universities created within the organisation model.
I have a scope in my organisation model to filter out the universities:
scope :all_uni, -> { where(org_type: 'University') }
In my profile form I have:
<%= f.input :institution, :label => "Graduated from" %>
But this just has a text field.
I have tried to replace that line with an attempt at making a select function in my form which refers to my organisation model scope for all_uni. It looks like this:
<%= f.select(:institution, #organisation.all_uni.title.map { |value| [ value, value ] }) %>
It gives this error:
undefined method `all_uni' for nil:NilClass
I don't understand what this error message means, but I'm also not sure I'm on the right track with the form select field either. Any tips for where to look to get this working. I'm not sure how to go about setting up the select field in the first place?
ANOTHER ATTEMPT:
I have also tried using this in my profile form:
<%= f.select(:institution, #organisation.all_uni.title) %>
But I get the same error. I must be way off track - i've exhausted every option I can find.
ANOTHER ATTEMPT
I found this post
Rails Simple Form custom association select field
Taking the example in that solution, I tried:
<%= f.input :institution do %>
<%= f.select :institution, Profile.Organisation.all_uni.map{ |l| [l.title {:title => l.title.titlecase}] } %>
<% end %>
But, I get this syntax error. I've tried removing the => but keep getting more syntax errors.
syntax error, unexpected =>, expecting '}' ...i.map{ |l| [l.title {:title => l.title.titlecase}] } );#out... ... ^

Not a complete answer but according to what I know is, If you got 2 models then instead of using
profile.rb
has_one :organisation, as: :orgable
organisation.rb
has_many :profiles
You can simply use
profile.rb
belongs_to :organisation
organisation.rb
has_many :profiles

Related

How do I call the name of a user when they belong to a model

I have two models, Chasing and User, a chasing belongs_to :user and a user has_many :chasings.
I created a migration for linking the two models together:
class AddUsersToChasings < ActiveRecord::Migration
def change
add_reference :chasings, :user, index: true, foreign_key: true
end
end
I have a controller for creating new users which I then want to be able to assign to chasings. I currently have this code in my chasings form for selecting the user:
<%= f.select :user_id, options_for_select(User.all.map {|c| [c.name, c.id]}), { :include_blank => "Please select user"}, {:class => "form-control"} %>
This seems to do the trick, after calling Chasing.first in rails console I can see the chasing now has user_id relevant to the user I picked. I can also run Chasing.first.user.name to give me the name of the user who is associated with the chasing. I'm wanting to show this name in my index view, the code I currently have for this is:
ChasingsController:
def index
#chasing = Chasing.all
end
Index view:
<% #chasing.each do |chasing| %>
<%= chasing.user %>
<% end %>
This shows a random string (seems to change every time I update a chasing - #<User:0xf5b0ba8> for example). when I change this to chasing.user.name I get 'undefined method `name' for nil:NilClass'.
Is there a way I can call the name for my view?
EDIT:
As per NickM's comment below I had chasings without users assigned to them causing active record to throw the error.
Looks like you have some Chasing objects in your database without user_ids. You can test by doing <%= chasing.user.name if chasing.user %>

Money Rails Gem - null values

I have monetised two models of my Rails 4 app with Money-Rails gem.
One is called Participants, the other is called Funding. Each of these models is nested inside another model, called Scope. Scope belongs to Project.
The associations are:
Project has one Scope; Scope belongs to Project
Scope has one Participant and has one funding; each of Participant and Funding belong to Scope.
Project accepts nested attributes for Scope. Scope accepts nested attributes for Participant and Funding.
Params for each relevant attribute in Participant and Funding are permitted in the Scope and Project Controllers as well as the models themselves. Params for Scope are permitted in the Scope and Project controllers.
In my Project form, I ask several questions. That form also has nested forms for each of the models which belong to it. Inside the Scope form, I ask users two boolean questions, being: Do you want participants? Do you want funding? Each of these models has a follow up question about participation cost and funding (those attributes are monetised).
If the answer to those questions is true, then I reveal the participant or funding form partial and ask how much money they want.
I have two problems:
First problem: Not null violation
1. If a user says they do want participants, but there is no associated costs, so that the boolean question inside the participant model asking whether there is cost involved with participation, I get an error that says:
ERROR: null value in column "participation_cost_pennies" violates not-null constraint
If a user says they don't want participants in answer to the question asked in the Scope form, I get the same error as in 1 above
Second problem: If I save an amount in the monetised fields, and come back to edit the project form, the form does not show the saved amount in the monetised field - and if you don't reenter it, I get an error saying that it can't be blank.
Does anyone know how to:
make the first problem go away in all circumstances except those when participation costs are actually sought; and
Fix the second problem by displaying the original amount saved when you come back to edit the form? I have tried inserting :selected into my form element, but it doesn't do anything.
My code is as follows:
Inside my Scope form (nested inside my project form):
<%= f.simple_fields_for :scope do |s_all| %>
<%= s_all.input :if_participant, :as => :boolean, :label => false, inline_label: 'Public participants or volunteers' %>
<%= s_all.input :if_funding, :as => :boolean, :label => false, inline_label: 'Funding or expenses' %>
If the answer to these fields is true, then I reveal the partial forms for participant of funding (for whichever is true).
Inside my Participants partial form, I have:
<%= f.simple_fields_for :scope do |participants_s| %>
<%= participants_s.simple_fields_for :participant do |par| %>
<%= f.label 'Are participants reimbursed for their costs?', :class => 'question-project' %>
<%= par.collection_radio_buttons :costs, [[true, ' Yes'], [false, ' No']], :first, :last, {:item_wrapper_class => 'fixradio'}, {:class => "response-project"} %>
<%= f.label 'What amount will you pay for participation costs?', :class => 'question-project' %>
<%= par.select :participation_cost_currency,
options_for_select(major_currencies(Money::Currency.table)), selected: :participation_cost_currency,
label: false,
prompt: "Select your costs currency" %>
<%= par.input :participation_cost, label: false, placeholder: 'Whole numbers only', selected: :participation_cost_pennies, :input_html => {:style => 'width: 250px; margin-top: 20px', class: 'response-project'} %>
For the first problem, you'll want to set a default value for the participation_cost_cents column in a migration:
# in console
> rails g migration change_default_for_participation_cost_cents
# in migration file
class ChangeDefaultForParticipationCostCents < ActiveRecord::Migration
def change
change_column :participants, :participation_cost_cents, :integer, default: 0
end
end
I'm not sure I follow on the second problem though. Maybe you should split the question in two?
A meetup group for Rails has helped me answer this question. The answer is not obvious - especially for newcomers.
My problem was I had an attribute in my database called participation_cost. Monetise then tried to make a method with the same name and that was failing because of the attribute in my table. For others, you don't need the attribute in your database with the name of the field you want to monetise.
Removing that attribute (in my case, participation_cost) solved my problem.

select_check_boxes with an has many through relation and :checked option

I am using collection_check_boxes to create object in a has_many through relation;
here some models:
#emotion.rb
class Emotion < ActiveRecord::Base
has_many :emotional_states
has_many :reports, :through => :emotional_states
end
#report.rb
class Report < ActiveRecord::Base
has_many :emotional_states
has_many :emotions, :through => :emotional_states
[... other "irrelevant" stuff here ...]
end
#emotional_states.rb
class EmotionalState < ActiveRecord::Base
belongs_to :report
belongs_to :emotion
end
As you may understand when I create a Report I also select with a collection_check_box a list of Emotions I want to bind to that report (through the model EmotionalState); Everything works on create (I retrieve the hash values and if #report.save I also create EmotionalStates with the #report.id and #emotion.id.)
But when it cames to edit the Report I would like to edit also the associated EmotionalStates (this means creating new EmotionalStates or deleting old one).
How can I populate the select_check_boxes with ALL the available Emotions having checked that emotions that are alredy associated through the EmotionalStates bojects?
If I write something like:
<%= collection_check_boxes(:report, :emotion_id, #report.emotional_states.map{|e| e.emotion}, :id, :name) %>
I'll get a unchecked checkbox for every alredy associated Emotion.
<%= collection_check_boxes(:report, :emotion_id, Emotion.all, :id, :name, :checked => #report.emotional_states.map{|e| e.emotion}) %>
While this code will correctly returns Emotion.all, but will not check the emotions alredy associated to #report through #report.emotional_states.
I've searched all around the wheb for examples on the usage of :checked options for collection_select_boxes without any results...
any hint?
I did the same once in this way.you can also try :
Emotions:
<% Emotion.all.each do |emotion| %>
<%= check_box_tag 'report[emotion_ids][]' , emotion.id, #report.emotion.include?(emotion) %><%= emotion.name %><br/>
<% end %>
In Controller add :emotion_ids=>[] into strong parameters.
And one line into controller update method:
params[:report][:emotion_ids] ||= []
After coming back to this bug I discovered that the problem was an incorrect use of the .map method, mapping a whole object (e.emotion) instead its id (e.emotion.id).
This easily fixed my problem:
<%= collection_check_boxes(:report, :emotion_id, Emotion.all, :id, :name, :checked => #report.emotional_states.map{|e| e.emotion.id}) %>
Thank you for your help!

an example of a nested form in simple form?

I am still struggling both writing the controller and the actual form to be able to nest one form in another with an optional model?
I have Message which has many contacts
When submitting a message, I want to add a contact optionally.
I have this as an example:
= simple_form_for Message.new, :remote => true do |f|
#message_form
= f.error_messages
%p
= f.input :account_name, :url => autocomplete_account_name_messages_path, :size => 40, :as => :autocomplete
%p
= f.input :topic, :required => true,
:input_html => {:size => 30}
#add_contact_btn
= link_to "Add Contact"
#contact_form
= f.simple_fields_for :contactd do |fc|
= fc.input :email
= fc.input :first_name
= fc.input :last_name
= f.submit 'Give'
= f.submit 'Request'
For Message.rb model, I have the following:
has_many :contacts
accepts_nested_attributes_for :contacts, :reject_if =>:all_blank
Note: When I used :contacts in the simple_fields_for it didn't work, so it is singular. But the reverse for accepts_nested_attributess_for.
In my create controller for message, I included message.contacts.build
But right now I am still generating nil contacts.
Here is what I see passed as form data from google chrome:
message%5Baccount_name%5D:McKesson
message%5Btopic%5D:testing a contact
message%5Bbody%5D:testing this
sender_id:
receiver_id:23
message%5Bcontacts%5D%5Bemail%5D:888#gmail.com
message%5Bcontacts%5D%5Bfirst_name%5D:Ang
message%5Bcontacts%5D%5Blast_name%5D:Name
The correct method name is simple_fields_for (notice the plural)
Also, you need to keep the f. to call it on the simple_form object
I have a small project where I demonstrate how to use nested forms in simple-form, combined with cocoon (a gem I created to add/remove nested elements dynamically).
The project is on github.
Hope this helps.
In my create controller for message, I included message.contacts.build
But right now I am still generating nil contacts.
Make sure you put in your Message.rb model the ability for it to accept the attributes too.
class Message < ActiveRecord::Base
attr_accessible :contacts_attributes
has_many :contacts
accepts_nested_attributes_for :contacts
I know it doesn't answer your question fully but it may have just been this. When it comes to my project, it would return nil if i didn't include the :contacts_attributes, in my case it deals with products. Hope this helps even if I'm not using simple form as of now!
I faced similar issues working with nested forms. As suggested by JustinRoR you need to define
attr_accessible: contacts_attributes.
You should be able to test the hash in the ruby console ( I am not sure if you have tried this). I suggest you to print the params[:message] and use this to create message from console like Message.new(params[:message]). (Note params[:message] is what you get by printing the params[:message] hash).
Once it works in console it should work like charm

Modifying attributes on the join model with accepts_nested_attributes_for

Simply, a Contact can have various associated Time Windows, which may or may not be Active as a Schedule. To wit:
Models
class Contact < ActiveRecord::Base
has_many :schedules
has_many :time_windows, :through => :schedules
accepts_nested_attributes_for :schedules, :allow_destroy => true
end
class TimeWindow < ActiveRecord::Base
has_many :schedules
has_many :contacts, :through => :schedules
end
class Schedule < ActiveRecord::Base
belongs_to :contact
belongs_to :time_window
end
View
<% TimeWindow.all.each do |tw| %>
<% schedule = Schedule.find_by_contact_id_and_time_window_id(#contact.id, tw.id)
schedule ||= Schedule.new %>
<p>
<%= f.label tw.description %>
<%= hidden_field_tag "contact[schedules_attributes][][id]", schedule.id %>
<%= check_box_tag "contact[schedules_attributes][][time_window_id]",
tw.id, #contact.time_windows.include?(tw) %>
<%= check_box_tag "contact[schedules_attributes][][active]", nil,
schedule.active %>
</p>
<% end %>
This submits something like this:
Parameters: { "commit" => "Update", "contact" => {
"group_ids" => ["2"], "enabled" => "1",
"schedules_attributes" => [ { "time_window_id"=>"1", "id"=>"46"},
{ "time_window_id" => "2", "id" => "42", "active" => "on" },
{ "time_window_id" => "3", "id" => "43"},
{ "time_window_id" => "4", "id" => "44", "active" => "on"}],
"last_name" => ...
The update action in the controller is basically stock, except to handle another instance of another related model which I coded using the "Handling Multiple Models" example from the Advanced Rails Recipes book.
According to this API doc, I think the above ought to work. However, nothing about the Schedules is getting updated. This shows up in the server log:
[4;35;1mSchedule Update (0.2ms)[0m [0mUPDATE `schedules` SET `updated_at` = '2010-09-30 20:39:49', `active` = 0 WHERE `id` = 42[0m
[4;36;1mSchedule Update (0.1ms)[0m [0;1mUPDATE `schedules` SET `updated_at` = '2010-09-30 20:39:49', `active` = 0 WHERE `id` = 44[0m
(NetBeans is giving me those stupid "[0m"'s in the output. I don't know what's wrong there.)
The SQL shows that the "active" boolean field is getting set to 0 where checked. How do I get this to correctly set the active bit?
As a followup, how would I organize this to get rid of the Schedule "connection" at all? I'm thinking I need to submit a :_delete with the Schedule from the form, but how would I do that conditionally when a checkbox is involved?
Thanks for any help you can provide. Rails is turning out to be a vast subject for me, and I want to do it "right." I'm really close here, but there's got to be a way to make this -- not just correct -- but elegant. The view code just feels way too cumbersome to be proper Rails. ;-)
I've kept trying different approaches to this problem, and I've come up with this, which works. Mostly. The only problem is that it doesn't handle NOT having a "Schedule" for each "Time Window". The form will render, and I'll get a disabled check_box (to prevent me from trying to delete something that isn't there), but I don't have a way to add it back, and submitting without it throws off the params hash (and causes Rails to give me an "Expected Hash (got Array)" error)
<% TimeWindow.all.each do |tw| %>
<% schedule = Schedule.find_by_contact_id_and_time_window_id(#contact.id, tw.id)
schedule ||= Schedule.new %>
<% f.fields_for "schedules_attributes[]", schedule do |sf| %>
<p>
<%= sf.label tw.description %>
<%= sf.hidden_field :id %>
<%= sf.check_box :_destroy, :disabled => schedule.new_record? %>
<%= sf.check_box :active %>
</p>
<% end %>
<% end %>
Note that the "schedules_attributes[]" array will automatically give you an existing ID within the braces in your HTML (which is nice), but the _attributes hash is expecting an "id" alongside the other attributes in order to make sense of the sub-hashes.
One of the big lessons I've learned here is that the "check_box_tag" method doesn't (seem to) give me a paired-up hidden field for Rails to parse in the unchecked case. I would have expected this. Adding one in by hand made a mess, which led me to finally giving into the "fields_for" method, and trying many incarnations before finding the appropriate syntax to get what I wanted out of it.
I've realized that my model isn't quite appropriate in this setup, so I'm going to change it, but I was so close to this answer, I wanted to at least get to the point of being able to see the end before I moved on.

Resources