Radio buttons grouped over multiple nested form entries - ruby-on-rails

I'm using RoR with simple_form and cocoon to handle this structure : Node has_many :addresses
Each Address has a boolean :main field which will flag one of each Node address as the main one (actually the one used for SNMP monitoring).
I've a nested form to manage these addresses but I can't find a clean way to select the main one.
Logically, I should use a radio buttons group, each address being one radio group option, but, in a nested form, each radio button will belong to one single address and have a different name, meaning they're in different groups.
I could link the radio button input to a virtual attribute in Node, and set selected address':main field to true in a callback, but how will I link each button to its address (I could use address ID as button value, but newly added addresses have no ID yet).
Here's a simplified version of what my Node form should look like (parentheses represent radio buttons, brackets for text inputs) :
Name : [MyNode1 ]
| Address | Main |
|------------|------|
| [10.0.0.1] | (*) |
| [10.0.0.2] | ( ) |
| [10.0.0.3] | ( ) |
My only other solution would be a JS feeded selectbox (options updated when I change something in addresses list) but it's definitely less clean and logical than a simple radio group...
Edit
An "acceptable" workaround would be to group radio buttons on something else than name attribute (example : data-group attribute). Does someone know any good JS/jQuery plug-in that could do this ?

I had a similar problem, with a Title that had many Role objects (a HMT glue model, which said which Person had done what to that title -- author, editor, translator, whatever). Each title had a primary person, usually the author, but sometimes an editor or curator, as in an encyclopedia. Try as I might, I could not get Rails to use a radio button in a newly created nested model, as you have seen. Here's what I ended up doing:
In the nested Role partial:
<p>
<%= f.collection_select :name, person_roles, :to_s, :titleize, {:prompt => "Role"}, {:class => 'combo'} %>
<%= f.collection_select :person_id, Person.order(:sort_name), :id, :reverse_name, :prompt => "Person" %>
<%- if ! f.object.new_record? -%>
<%= radio_button_tag 'title[role_id]', f.object.id, (#title.role_id == f.object.id) %>
<%= label_tag :role_id, "Primary", :for => "title_role_id_#{f.object.id}" %>
<%- end -%>
<%- if ! f.object.new_record? -%>
<%= f.link_to_remove "Delete", :class => 'delete' %>
<%- end -%>
In the Title model:
has_many :roles, :dependent => :destroy
has_many :people, :through => :roles
belongs_to :role #the primary person, like the author or editor
accepts_nested_attributes_for :roles, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
after_create :set_primary
def set_primary
self.update_attributes( :role_id => self.roles.first.id ) if self.roles && self.roles.first
end
So the radio buttons don't even appear unless the nested models have been saved. Adding a new nested model will give you a form partial missing the radio button and delete link (since neither would work until that nested child had been saved).

Related

Maintain the order of an array in rails forms

I have 3 models (Workout, WorkoutSet, WorkoutStep) with the following associations:
A Workout has many sets
A WorkoutSet has many WorkoutSep
And a WorkoutStep has an array of videos (called video_usage[])
Using simple_form and cocoon I made the forms to edit such associations but, at the deepest level (the array of videos of WorkoutStep) by the time I edit a Workout, the order seem not to be constant, being, instead, ordered by (guessing) the last time the attribute has been modified (in this case, modified is changing a property, such as selecting another video).
I want the array of videos to remain constant between edits.
This is how my form looks like:
_workout_sets_fields.html.haml
.nested-fields
= f.simple_fields_for :main_video_usage do |mv|
= render 'main_video_usage_fields', f: mv
.steps{ :style => "margin-left: 680px" }
= link_to_remove_association 'remove step', f
= link_to_add_association 'add video', f, :main_video_usage
_main_video_usage_fields.html.haml
.nested-fields
= f.collection_select :video_id, Video.all.order(:title), :id, :title
You can give an explicit order, use id or created_at if you want to keep the order of creation:
has_many :main_video_usage, -> { order('id asc') }, class_name: 'VideoUsage::Main', as: :parent

Rails Simple Form Select from Array - polymorphic associations

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

Formtastic pre-check few checkboxes

I'm trying to manually tell formtastic to check a few checkboxes. #some_array currently has an element called checked which exists for each member.
= f.input :cboxes, label: "CBoxes", as: :check_boxes,
collection: #some_array.map { |a| [a[:name], a[:id]] }
I've tried to set the input_html to { checked: 'checked' } (How to pre-check checkboxes in formtastic) but this checks all checkboxes, not just the select few that I want.
The contents of #some_array are coming via an API, and I can't change the database structure (Ruby on Rails + Formtastic: Not checking checkboxes for multiple checked answers)
Suggestions?
If you are editing an ActiveModel, you don't need to "manually select checkboxes".
Let's consider a simple example with a single User model which has fields username and roles. Roles field is a string column, which Rails serializes as an Array. It might also be has_many relation to other ActiveModel, but we assume it's an Array for simplicity.
User is defined in user.rb:
class User < ActiveRecord::Base
serialize :roles, Array
end
Now you can "assign manually" desired roles to User in your controller:
#user = User.new(username: 'dimakura', roles: ['admin', 'editor'])
and define form in your view:
<%= semantic_form_for #user do |f| %>
<%= f.input :username %>
<%= f.input :roles, as: :check_boxes, collection: ['owner', 'admin', 'editor', 'viewer'] %>
<% end %>
In given example only "admin" and "editor" roles will be pre-selected in form. The "owner" and "viewer" role won't be selected.
Update Official documentation states:
Formtastic, much like Rails, is very ActiveRecord-centric.
But actually it's not a big challenge to create ActiveRecord-compatible model yourself. An example of doing this can be found in this blog post.

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.

ruby on rails how to use FormOptionHelpers to create dynamic drop down list

I have checked some tutorials but I got confused by the parameters in this method
collection_select (object, attribute, collection, value_method, text_method, options = {}, html_options ={})
I have a map model includes: :area, :system, :file
and I want to read :area from database to a drop down list, and let user choose one
I already did #map = Map.all in the view
what the method should be?
especially the parameter "attribute". In a lot tutorials, people put "id" here. But I don't know what "id" is, and in my situation I don't need any other value, just the "area".
Im not exactly sure what you are asking here but if you are trying to make a dropdown selection for use in an html form will this example help you at all?
<% nations = {'United States of America' => 'USA', 'Canada' => 'Canada', 'Mexico' => 'Mexico', 'United Kingdom'=> 'UK'} %>
<% list = nations.sort %>
<%= f.select :country, list, %>
Here nations is a hash of countries then list becomes the sorted copy of that hash. An html dropdown is then created as a part of the form "f". ":country" is the part of the model that the data is connected to while list is the options to populate the dropdown with
It's not clear from your question what the model is that's being populated with the area.
Typically, collection_select is used between related models.
eg.
class Category < ActiveRecord::Base
has_many :products
end
class Product < ActiveRecord::Base
belongs_to :category
end
When selecting the 'category' for a product, your view would have something like:
<%= f.collection_select(:category_id, :id, Category.all, :name, include_blank: true) %>
What this does is specify the Product.category_id as the attribute being populated with the value of Category.id. The values come from the Category.all collection, and with Category.name being the item displayed in the select. The last (optional) parameter says to include a blank entry.
Something like the following is probably what you need:
<%= f.collection_select(:map_id, :id, #map, :area) %>
However, if the model you're trying to populate has an area attribute (instead of an ID linking to the map), you might need to use:
<%= f.collection_select(:area, :area, #map, :area) %>
This specifies that the area attribute of the receiving table will be populated with Map's area attribute, which is also being used as the "description" in the select.

Resources