Using
<%= collection_check_boxes(:employee, :skill_ids, Skill.all, :id, :name) %>
on its own works just fine in my project. This way, each employee shows all skills and if they are checked or not.
Additionally to that, I have some categories, which the skills are related, to (Category has many Skills).
Can anyone recommend a proper way, to use the collection_check_boxes correctly for each category so only the skills related to the category are displayed?
I think I am just missing the correct limitation for the "Skill.all" parameter.
Finally it should look like this, but instead of Submit buttons (by using jquery-ujs, method: put), I want those skills to be checkboxes.
http://d.pr/i/CYHaGH
Edit1: Skills and Categories model
class Category < ApplicationRecord
has_many :subcategories, class_name: "Category", foreign_key: "parent_id"
belongs_to :topcategory, class_name: "Category"
has_many :skills
end
class Skill < ApplicationRecord
has_many :employeeskillsets, foreign_key: "skill_id"
has_many :employees, through: :employeeskillsets
has_many :projectskillsets
has_many :projects, through: :projectskillsets
has_many :tagsets, dependent: :destroy
has_many :tags, through: :tagsets
belongs_to :category
end
Edit2: Still no progress. Does anybody have an idea how this could be done?
SOLUTION:
Ok, the solution was pretty simple. I just had to declare the collection on each category, which already was for a previous version of that app. <%= collection_check_boxes :employee, :skill_ids, category.skills, :id, :name%> (category.skills was set for each instance of categories).
Thanks for the help.
Using collection_check_boxes, you can pass a block like that :
collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
b.label { b.check_box }
end
And you can access value, text and more :
collection_check_boxes(:post, :author_ids, Author.all, :id, :name_with_initial) do |b|
b.label(:"data-value" => b.value) { b.check_box + b.text }
end
You should read the documentation here :
https://apidock.com/rails/v4.0.2/ActionView/Helpers/FormOptionsHelper/collection_check_boxes
You will find all you need to solve your issue,
Hope that help
Ok, the solution was pretty simple. I just had to declare the collection on each category, which already was for a previous version of that app. <%= collection_check_boxes :employee, :skill_ids, category.skills, :id, :name%> (category.skills was set for each instance of categories).
Related
I am working on a Rails app where users can post Quotes about different art forms (e.g. Music, Painting, Photography, etc.). Each Quote must be assigned a Medium (such as Music or Photography) and a Genre (such as Jazz or Rock and Roll for music or Landscape for photography).
I am using a grouped_collection_select for the Genre field on my Quote form, and that works great to sort the genres, but I'd like to prevent anyone from selecting a genre that doesn't belong to the medium they have selected.
I know that I can do this dynamically with javascript, as in this Railscast, but I'd like to create a validation to ensure that no bad data gets into the database.
Is there a way to validate this in my Quote model so that there's no way a Quote can be saved with a genre that does not have the correct medium? This would prevent someone from saving something that is the medium "Photography" and the genre "Jazz", for instance.
Here are my models:
class Quote < ApplicationRecord
belongs_to :medium
belongs_to :genre
end
class Medium < ApplicationRecord
has_many :quotes
has_many :genres
end
class Genre < ApplicationRecord
has_many :quotes
belongs_to :medium
end
And here are the fields on my Quote form:
<%= f.label :medium, 'Medium' %>
<%= f.collection_select :medium_id, Medium.order(:name), :id, :name, {prompt: 'Select a medium'}, {class: 'form-control'} %>
<%= f.label :genre, 'Genre' %>
<%= f.grouped_collection_select :genre_id, Medium.order(:name), :genres, :name, :id, :name, {prompt: 'Select a genre'}, {class: 'form-control'} %>
You can use rails validate method to achieve this,
validate: check_medium
def check_medium
errors.add(:base, "Your error message") if genre.try(:medium) != medium
end
My proposal here is to story only genre_id in quotes table. This belongs_to :medium is unnecessary because each genre already knows its medium. With architecture like this, you don't have to worry about medium-genre mismatch at all. Models:
class Quote
belongs_to :genre
# With delegation you still can do something like quote.medium
delegate :medium, to: :genre
end
class Medium
has_many :genres
has_many :quotes, through: :genres
end
class Genre
belongs_to :medium
has_many :quotes
end
I am trying to create a form to save quite a complexish relationship. The models for the relationship are below.
class Goal < ActiveRecord::Base
belongs_to :goal_status
belongs_to :goal_type
has_many :users, through: :user_goals
has_many :user_goals
has_many :parent_goals, class_name: 'GoalDependency', foreign_key: :parent_id
has_many :child_goals, class_name: 'GoalDependency', foreign_key: :child_id
has_many :children, through: :child_goals
has_many :parents, through: :parent_goals
validates_presence_of :goal_status_id, :goal_type_id
end
class GoalDependency < ActiveRecord::Base
belongs_to :parent, class_name: 'Goal', foreign_key: 'parent_id'
belongs_to :child, class_name: 'Goal', foreign_key: 'child_id'
end
So a goal can have many parents or it can have many children or both. I have tried using a multi select drop down to save these relationships and setting child_ids/parent_ids, but that doesn't seem to work because the goal_dependency requires both fields - i.e. child_id and parent_id. Rails only sets the one. So if I am saving a list of child_ids it sets those, but it doesn't know to set the parent_id with the current goal and visa versa.
I have tried using accepts_nested_attributes, but I am not really sure how I can use this with a multi-select drop down.
Any guidance or direction on how I could approach this would be appreciated.
Example of my current form.
.row
.col-md-6
= simple_form_for(#goal) do |f|
- if #goal.errors.any?
#error_explanation
h2 = "#{pluralize(#goal.errors.count, 'error')} prohibited this goal from being saved:"
ul
-#goal.errors.full_messages.each do |message|
li = message
= f.input :description
= f.input :goal_status_id, collection: #goal_statuses, value_method: :id, label_method: :name, as: :select
= f.input :goal_type_id, collection: #goal_types, value_method: :id, label_method: :name, as: :select
= f.input :user_ids, collection: #users, as: :select, label: 'Assigned To', input_html: { class: 'chosen-select', multiple: true }, selected: #goal.user_ids
= f.input :child_ids, collection: #goals, as: :select, label: 'Children Goals', input_html: { class: 'chosen-select', multiple: true }, selected: #goal.child_ids, value_method: :id, label_method: :description
br
= f.submit
After further investigation I thought of another way to do this, but I am not really happy with it. I can split the concept of a dependency into a parent dependency and a child dependency and have these as different models. The relationship can then be handled differently. See the code below for the child dependency.
class Goal < ActiveRecord::Base
belongs_to :goal_status
belongs_to :goal_type
has_many :users, through: :user_goals
has_many :user_goals
has_many :child_goals, class_name: 'GoalChildDependency'
has_many :children, through: :child_goals
validates_presence_of :goal_status_id, :goal_type_id
end
class GoalChildDependency < ActiveRecord::Base
belongs_to :goal
belongs_to :child, class_name: 'Goal', foreign_key: :child_id
end
From what I have read rails can't deal with composite keys and my case seems to be a case where a composite key would make sense.
Anyway if anyone can figure out how to make my initial code work that would be great.
Maybe it would be just easier to have single Goal model with self referencing parent_id column on it. Where than you can make
has_many :child_goals, class_name: 'Goal', foreign_key: :parent_id
belongs_to :parent_goal, class_name: 'Goal'
Additionally if you need ChildGoal model anyway, you can have a single table inheritance models, add column type to goals table and have ChildGoal < Goal and follow analogous approach as above.
My model has the following (relevant) declartions:
class Campaign < ActiveRecord::Base
has_many :placements, dependent: :destroy
has_many :clients, dependent: :destroy
accepts_nested_attributes_for :clients, allow_destroy: true
accepts_nested_attributes_for :placements, allow_destroy: true
end
class Placement < ActiveRecord::Base
has_one :client, through: :campaign
belongs_to :campaign
validates_presence_of :hashtags_instagram, :hashtags_twitter, :sms, :url, :channel
end
class Client < ActiveRecord::Base
belongs_to :campaign
belongs_to :placement
validates_presence_of :client_name
end
So effectively, campaigns have many placements and clients. Placements have exactly one client, and are associated with exactly one campaign (or at least should be, I'm still not sure I did that correctly).
So in my placement form, I want it to display a dropdown menu of all of the clients belonging to the placement's campaign (not all campaigns). If it is a new placement, it should display "Please Select or similar", otherwise, it should display the client belonging to that placement.
I can't seem to get the :prompt option to work though. It never actually displays a Please Select, just the first client in the list. I was attempting to use this to get it to work:
Oddly, if I change prompt to include_blank, it does actually show the blank item (although it still selects the first client in the list, not the blank item).
When I tried some things, I tried the code:
= f.collection_select(:placement_id, #placement.client.all.to_a, :id, :client_name, {:prompt => true}, class: "newdropdown-menu", id: "newdropdown")
And got the error message:
Could not find the source association(s) :client or :client in model Campaign. Try 'has_many :client, :through => :campaign, :source => <name>'. Is it one of :user, :placements, :clients, or :photo?
If I change the collection select to read:
= f.collection_select(:campaign_id, #placement.campaign.clients.all.to_a, :id, :client_name, {:prompt => true}, class: "newdropdown-menu", id: "newdropdown")
Then it runs, but does not actually display the prompt, just the list.
Changing #placement to placement does not seem to have any effect (which seems odd).
I don't want to change it to has_many, since placements should have exactly one client. How should I proceed? I tried messing with :source, but it doesn't seem to relate to my problem.
You need to place you html options in a hash:
f.collection_select(:campaign_id, #placement.campaign.clients.all, :id, :client_name, {:prompt => true}, {class: "newdropdown-menu", id: "newdropdown"})
Documentation available here:
http://apidock.com/rails/ActionView/Helpers/FormOptionsHelper/collection_select
I have 3 models:
class Brand
attr_accessible :obs, :site, :title
has_many :equipments
end
class Equipment
attr_accessible :brand_id, :category_id, :lending_id
belongs_to :brand
has_many :lendings
end
class Lending
attr_accessible :equipment_id
belongs_to :equipment
end
I'm trying to show the brand of an associated equipament:
Brand: <%= #lending.equipment.brand %>
that command show this: Brand:0xab7f2c8
As you can see, there's no association between brand and lending models and for me its strange if i do that. I want to use the equipment/brand association to retrieve the :title information and show it on my lending view.
Can anyone help me?
You can either use a delegate in Lending:
delegate :brand, :to => :equipment, allow_nil: true
Or you can setup a has-one-through association in Lending:
has_one :branch, :through => :equipment
Either way, you can now call branch directly from a Lending instance, and work on it (almost) as if it were a regular association.
Use delegate
class Lending
attr_accessible :equipment_id
belongs_to :equipment
delegate :brand, :to => :equipment, :allow_nil => true
end
now you can use
<%= #lending.brand.title%>
I have a Form_for for a Show model. I would like to use a fields_for within the form_for to add bands. The thing is I don't want the fields tied to the bands when using the form to update records. If the name of the band changes I would like to update the performance with the new band.
Shows are joined with Bands through Performances
class Show < ActiveRecord::Base
has_many :performances
has_many :bands, through: :performances
accepts_nested_attributes_for :bands
end
class Band < ActiveRecord::Base
attr_accessible :name, :website, :country, :state
has_many :performances
has_many :shows, through: :performances
validates :name, presence: true, uniqueness: true
end
class Performance < ActiveRecord::Base
attr_accessible :show, :band
belongs_to :show
belongs_to :band
end
Here is my form. (simplified)
<%= form_for #show do |f| %>
#fields
<%= f.fields_for :bands do |b| %>
<%= b.text_field :name %>
<% end %>
<%end>
The problem is if this is used to change a bands name, it changes the bands name (crazy right?). I don't want it to update the Band record-- I want it to do a Band.find_or_create and update the performance record with the new band's id. This way users can replace a band in a show by removing the name and adding in another band name.
The rendered html should include the performance id not the band id (I think)
Something like:
<input id="show_performance_attributes_1_id" name="show[performance_attributes][1][id]" type="hidden" value="62">
How is this done?
Ok, so I was able to find the solution to my own question. I may have not provided enough details in the original question.
But the solution is simply to use the performance model as the nested field in the Fields_for instead of the bands model. Change the show model to accepts_nested_attributes_for performances and change the performance model to accepts_nested_attributes_for band