How to make a custom collection select input with Simple Form - ruby-on-rails

I would like to build a custom collection select input that takes a collection of text fields as input rather than a collection of check boxes. I have three basic models:
class Signature
has_many :signature_terms
has_many :signatures, :through => :signature_terms
...
class SignatureTerm
attr_accessible :initial # user provides initial as verification
belongs_to :signature
belongs_to :term
...
class Term
has_many :signature_terms
has_many :signatures, :through => :signature_terms
...
I need to present every term for the user to initial. With radio buttons or check boxes, this is fairly easy to accomplish:
= simple_form_for #signature do |f|
...
= f.collection_check_boxes :policy_term_signature_ids, #signature.terms, :id, :name
Which allows me to establish the relationship. However, I would like to present the user with every term for them to initial/not initial.
I've read the documentation on customizing simple form field inputs and have had success following the examples out of the code. What I haven't been able to do is implement a custom collection_select input. If someone could point me to an tutorial or functioning example, I'd sure appreciate it.

I do not know if it is the best approach but I would use the checkbox solution you currently have and then show a list of the choosen terms and textfields besides them for the initials.
Creating the list and textfields with JS.

Related

Ruby on rails Model User investor_or_startup:?

Hello I am new to rails and I'm creating an application to practice and learn. I have a issue which I don't know how to solve.
If I generate a model User and I want to include a section in the model where I name it investor_or_startup. When I create the form for a user to fill it out I want the user to only be able to fill in the blank with the word Investor or Startup. How would I go about this?
You could create a selector field in your form.
I would share some code but I think as a new rails developers, you should get used to reading and understanding the docs, as that is invaluable.
http://guides.rubyonrails.org/form_helpers.html#making-select-boxes-with-ease
Add a validation in your model for that specific attribute, if it's called investor_or_startup, then using validates specify this attribute, use the format option, and optionally you can add a message for the user, like:
class User < ApplicationRecord
validates :investor_or_startup,
format: {
with: /Investor|Startup/,
message: 'Two valid options are Investor or Startup'
}
end
The with option inside format accepts a regular expression, in this case to check for the specific word Investor or (|) Startup, it's case sensitive, so lowercase letters would make an invalid input. If you want to change to case insenstive then consider adding i at the end of your expression, like /investor|startup/i.
The message options accepts an string which you can use to display a message for the user.
You can keep reading about this here.
The most common way of doing thin in rails would be to add a role attribute to your User model. In this case, role replaces start_up_or_investor. You would start by using a migration to add a field called role of type :integer to your users table.
Next you would add the following line to your User model:
enum role: [:investor, :start_up]
You can read more about enum here.
Finally, you can use a select_tag in a Rails form helper to select one of the enum options. There are many ways to use a select_tag with an enum. A quick internet search will reveal many blogs and SO posts on the subject.
Bottom line: I recommend using an enum for this sort of thing.
So you have a User model and that user could possibly be an investor or a startup. To start off you could add some kind of "type" attribute that will query whether the user is a startup or an investor. User.type #=> "startup" or User.type #=> "investor". From there you could set up your form_for user and use a select field that would allow the user to select their value for the type attribute <%= f.select(:type, ...) %>
Like Sam suggested, I encourage you to study the rails guides,(particularly form helpers) to help you understand how a form works.
Adding roles to uses is a common programming problem. There are two decent ways to solve this (and many bad ways).
1. Use an ActiveRecord::Enum
class User < ApplicationRecord
enum role: [:investor, :start_up]
end
An enum is a integer column that maps to specific values. In this case investor = 0, start_up = 1.
To create an input based on this you can use the enum mapping:
<%= f.select(:role, User.roles.keys.map {|role| [role.titleize,role]}) %>
While simple the major limitation is that a user can only ever have one role.
2. Setup a many-to-many association
class User < ApplicationRecord
has_many :user_roles
has_many :roles, through: :user_roles
scope :with_role ->{|*names| joins(:roles).where(name: names) }
def has_role?(name)
roles.where(name: name).exist?
end
end
class UserRole < ApplicationRecord
belongs_to :user
belongs_to :role
end
class Role < ApplicationRecord
has_many :user_roles
has_many :users, through: :user_roles
end
This lets you add and remove roles from a user seperately and often is a better fit for real world use cases. The Rolify gem is an excellent example of this pattern.
To allow a user to select roles you can use a checkbox:
<%= f.collection_check_boxes(:role_ids, Role.all, :id, :name) %>

Ruby on Rails Polymorphic - Controller and View solution

I have been trying to wrap my mind on a polymorphic structure for my app for a while now, but I can't get it, and I don't know how to solve it.
I have a Supplier model, and a supplier's profile can by a Company OR a Person as listed below.
Supplier Model
class Supplier < ActiveRecord::Base
belongs_to :profile, :polymorphic => true
accepts_nested_attributes_for :personable, :allow_destroy => true, :reject_if => :all_blank
end
Company Model
class Company < ActiveRecord::Base
has_one :supplier, as: :profile
end
Person Model
class Person < ActiveRecord::Base
has_one :supplier, as: :profile
end
On the Model level (on console) my associations works fine, but I have to those between the two model up front (supplier.profile = Company.new, for example).
What I want is, when a user get to the NEW Supplier action he can select on the form if the Supplier is a Person or a Company, and then something must be done to show the correct fields for. And back on the Create action, handle everything.
I have read many stackoverflow questions and watch Ryan Bates cast #197 and #394.
I'm a Rails enthusiastic, not a programmer.
If someone can point me out to the right direction, I would be thankful.
Regards,
David
This largely depends on the variety of fields you need, and how much data is shared. But if they are very different, I would actually have two separate divs or section on the page that a radio button toggles showing one & hiding the other with javascript. Can do this pretty easily with jquery at the bottom of the view, given it's a default in rails:
<input type="radio" name="supplier_type" value="person" />
<input type="radio" name="supplier_type" value="company" />
<script>
$('input[name="supplier_type"]').on('change', function () {
if ($(this).val() === "person") {
$('.person-form').show();
$('.company-form').hide();
}
else {
$('.person-form').hide();
$('.company-form').show();
}
});
</script>
Then inside each of those divs have an entirely separate form that post to different actions. If the data is different enough to require two forms, then it's worth having two different controllers for this.
Now if it's just a couple fields different, or an additional field for one or something. I would have the radio button same as above inside the form_for. Again attach some javascript to show or hide the values. In the controller, use a check against the radio button value to get the params you need:
supplier_params = params.slice :list, :of, :generic, :params
if params[:supplier_type] == "company"
supplier_params.merge! params.slice :company_name
elsif params[:supplier_type] == "person"
supplier_params.merge! params.slice :person_name
end
Supplier.new supplier_params

Populating a drop down list in ruby on rails and saving the result

I'm fairly new to RoR and having trouble wrapping my head around how to do this.
Basically I want to design a drop down menu that will dynamically populate a drop down of newspapers from the database. Once the paper is selected, I want the user to be able to select an issue category (ex: billing), then a specific issue_type (ex: credit card charge), then the contact type (email or phone) (total of 4 drop downs).
The issue category, issue_type, and contact_type all belong to Issuelog. Each Issuelog should belong to a specific newspaper, as per my model code. I want the user to be able to record the volume of each kind of contact for each type of issue for each paper, with a very standard set of selections available. The newspaper table will not change after the submission, it will simply create an Issuelog that will correlate to that particular paper (the id created by default - not sure if I need to create any additional keys in this scenario).
Issuelog
class Issuelog < ActiveRecord::Base
belongs_to :newspaper
attr_accessible :category, :contact_type, :issue_type
end
Newspaper
class Newspaper < ActiveRecord::Base
has_many :issuelogs
attr_accessible :affiliate_group, :name
end
I'm having trouble understanding how I will need to structure this overall to achieve what I want. Will I need to use JavaScript in my view, and does my model design make sense for what I'm trying to achieve?
In controller's action
#newspapers = Newspaper.find(:all)
In model there are many that you can use, You can use something like this.
<%= select("newspaper", "ids", #newspapers.collect {|p| [ p.name, p.id ] }, { :prompt => 'Select' }, :onChange => 'do_your_thing()') %>
I hope this helps, But tell if you need any clarification

Custom Active Admin form inputs for has_and_belongs_to_many relationship

I have a very simple model
class Lifestyle < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :profiles
end
that has a has_and_belongs_to_many relationship with Profile
class Profile < ActiveRecord::Base
attr_accessible ...
belongs_to :occupation
has_and_belongs_to_many :lifestyles
accepts_nested_attributes_for :lifestyles
end
I want to use ActiveAdmin to edit the Profile object, but also assign Lifestyles to a profile. It should be similar to dealing with belongs_to :occupation, as this is sorted out automatically by ActiveAdmin to a dropbox with the options pre-filled with available occupations.
I've tried to use the has_many form builder method, but that only got me to show a form to type in the name of the Lifestyle and on submission, it returned an error.
f.object.lifestyles.build
f.has_many :lifestyles do |l|
l.input :name
end
Error I get:
Can't mass-assign protected attributes: lifestyles_attributes
The perfect way for me would be to build several checkboxes, one for each Lifestyle in the DB. Selected means that the lifestyle is connected to the profile, and unselected means to delete the relation.
I'm having great doubts that this is possible using ActiveAdmin and without having to create very complex logic to deal with this. I would really appreciate it if you'd give your opinion and advise me if I should go this way or approach it differently.
After some research, I am ready to answer my own question.
First, I have to say thanks to #Lichtamberg for suggesting the fix. However, that only complicates things (also regarding security, though not an issue in this case), and doesn't help me reach my ideal solution.
Digging more, I found out that this is a very common scenario in Rails, and it's actually explained in Ryan Bates' screencast no #17.
Therefore, in Rails, if you have a has_and_belongs_to_many (short form HABTM) association, you can easily set the ids of the other associated object through this method:
profile.lifestyle_ids = [1,2]
And this obviously works for forms if you've set the attr_accessible for lifestyle_ids:
class Profile < ActiveRecord::Base
attr_accessible :lifestyle_ids
end
In ActiveAdmin, because it uses Formtastic, you can use this method to output the correct fields (in this case checkboxes):
f.input :lifestyles, as: :check_boxes, collection: Lifestyle.all
Also, I have simplified my form view so it's now merely this:
form do |f|
f.inputs # Include the default inputs
f.inputs "Lifestlyes" do # Make a panel that holds inputs for lifestyles
f.input :lifestyles, as: :check_boxes, collection: Lifestyle.all # Use formtastic to output my collection of checkboxes
end
f.actions # Include the default actions
end
Ok, now this rendered perfectly in the view, but if I try and submit my changes, it gives me this database error:
PG::Error: ERROR: null value in column "created_at" violates not-null constraint
: INSERT INTO "lifestyles_profiles" ("profile_id", "lifestyle_id") VALUES (2, 1) RETURNING "id"
I found out that this is due to the fact that Rails 3.2 doesn't automatically update the timestamps for a HABTM association table (because they are extra attributes, and Rails only handles the _id attributes.
There are 2 solutions to fix this:
Either convert the association into a hm:t (has_many, :through =>)
Or remove the timestamps from the table
I'm going to go for 2) because I will never need the timestamps or any extra attributes.
I hope this helps other people having the same problems.
Edit: #cdesrosiers was closest to the solution but I already wrote this answer before I read his. Anyway, this is great nevertheless. I'm learning a lot.
Active Admin creates a thin DSL (Domain-Specific Language) over formtastic, so it's best to look at the formastic doc when you need form customization. There, you'll find that you might be able to use f.input :lifestyles, :as => :check_boxes to modify a has_and_belongs_to_many relationship.
I say "might" because I haven't tried this helper myself for your particular case, but these things have a tendency to just work automagically, so try it out.
Also, you probably won't need accepts_nested_attributes_for :lifestyles unless you actually want to modify the attributes of lifestyles from profiles, which I don't think is particularly useful when using active admin (just modify lifestyles directly).
Add
attr_accessible :lifestyles_attributes
f.e.:
class AccountsController < ApplicationController
attr_accessible :first_name, :last_name
end

Using accepts_nested_attributes_for with single table inheritance

I have a model Post which belongs_to one Section. There are two different Section subclasses and I use STI to implement different behavior for each of them. In the Post form I would like to have a tab for each Section. The tab will let the user either A) Pick from an existing Section using a <select> or B) Let the user create a new Section. I would like to know how to use accepts_nested_attributes_for and fields_for or whatever is required to get this done The Rails Way.
Any advice is greatly appreciated. Thanks.
Assuming the tabs correspond to the two subclasses
class Post
# the two subclasses. Each instance will only be using one or the other
belongs_to :section_foo
belongs_to :section_bar
accepts_nested_attributes_for :section_foo
accepts_nested_attributes_for :section_bar
end
And in the view (probably once per tab)
= form_for #post do |f|
= f.select :section_id, SectionFoo.all # etc
= fields_for #post.build_section_foo do |s|
= s.text_field :bla_bla_bla
That should get you 85% of the way there. You might need some :reject_if bidness on the accepts_* to avoid creating a new section and assigning an old section.

Resources