add property to many lists rails4 - ruby-on-rails

I want to create property . Then add it on many lists. One Property can be on many list.
here is my models:
class List < ActiveRecord::Base
has_many :propertyships
has_many :properties, :through => :propertyships
end
class Propertyship < ActiveRecord::Base
belongs_to :list
belongs_to :property
end
class Property < ActiveRecord::Base
has_many :propertyships
has_many :lists, :through => :propertyships
end
properties/show.html.erb
<%= form_for #property do |f| %>
<% List.all.each do |list| %>
<%= check_box_tag "property[list_ids][]", list.id,#property.list_ids.include?(list.id) %>
<%= list.name %>
<% end %>
<%= f.submit %>
<% end %>
property is not adding to lists.
what am i doing wrong??

Use collection_check_boxes instead of manually creating the inputs:
<%= form_for #property do |f| %>
<%= f.collection_check_boxes(:list_ids, List.all, :id, :name) %>
<%= f.submit %>
<% end %>
Whitelisting the params is a bit special since params[:property][:list_ids] will contain an array:
class PropertiesController < ApplicationController
# ...
def create
#property = Property.new(property_params)
# ...
end
# ...
private
def property_params
params.require(:property)
.permit(:foo, :bar, list_ids: [])
end
end

Related

Rails not saving nested attributes

I have the tables Task and Item. I have a form for Item where I record all the possible items that my Tasks may have, which is working fine. Then I have the form for Task where all the Items are displayed alongside a field to put a cost value for each item. This will result in a join between Task and Item: TaskItem (this table contains task_id, item_id and cost).
When I submit the form, it's saving the Task but not the TaskItems associated. I don't see what I'm missing as I searched a lot for this problem and nothing seems to work. Please, see the code below.
Model:
class Task < ApplicationRecord
has_many :task_items
has_many :items, :through => :task_items
accepts_nested_attributes_for :task_items, :allow_destroy => true
end
class Item < ApplicationRecord
has_many :task_items
has_many :tasks, :through => :task_items
end
class TaskItem < ApplicationRecord
belongs_to :task
belongs_to :item
accepts_nested_attributes_for :item, :allow_destroy => true
end
Controller:
def new
#items = Item.all
#task = Task.new
#task.task_items.build
end
def create
#task = Task.new(task_params)
#task.save
redirect_to action: "index"
end
private def task_params
params.require(:task).permit(:id, :title, task_items_attributes: [:id, :item_id, :cost])
end
My view:
<%= form_for :task, url:tasks_path do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field(:title, {:class => 'form-control'}) %><br>
</p>
<% #items.each do |item| %>
<% #task_items = TaskItem.new %>
<%= f.fields_for :task_items do |ti| %>
<%= ti.label item.description %>
<%= ti.text_field :cost %>
<%= ti.hidden_field :item_id, value: item.id %>
<% end %>
<% end %>
<p>
<%= f.submit({:class => 'btn btn-primary'}) %>
</p>
You need to add inverse_of option to the has_many method in class Task:
class Task < ApplicationRecord
has_many :task_items, inverse_of: :task
has_many :items, through: :task_items
accepts_nested_attributes_for :task_items, :allow_destroy => true
end
This is due to the when creating a new TaskItem instance, it requires that the Task instance already exists in database to be able to grab the id fo the Task instance. Using this option, it skips the validation.
You can read this post about inverse_of option and its use cases.
fields_for has an option to specify the object which is going to store the information. This combined with building each of the TaskItem from the has_many collection should ensure that all the relationship are set correctly.
View Code:
<%= form_for #task do |f| %>
<p>
<%= f.label :title %><br>
<%= f.text_field(:title, {:class => 'form-control'}) %><br>
</p>
<% #items.each do |item| %>
<% task_item = #task.task_items.build %>
<%= f.fields_for :task_items, task_item do |ti| %>
<%= ti.label item.description %>
<%= ti.text_field :cost %>
<%= ti.hidden_field :item_id, value: item.id %>
<% end %>
<% end %>
<p>
<%= f.submit({:class => 'btn btn-primary'}) %>
</p>
<% end %>
Controller Code:
def index
end
def new
#items = Item.all
#task = Task.new
end
def create
#task = Task.new(task_params)
#task.save
redirect_to action: "index"
end
private
def task_params
params.require(:task).permit(:id, :title, task_items_attributes: [:id, :item_id, :cost])
end

rails4 collection select with has_many through association and nested model forms

I have a rails4 app. At the moment my collection select only works if I select only one option. Below is my working code. I only have product form. Industry model is populated with seeds.rb. IndustryProduct is only use to connect the other 2 models.
I'd like to know what I have to change in the code to be able to choose more.
I saw some working examples with multiple: true option like (https://www.youtube.com/watch?v=ZNrNGTe2Zqk at 10:20) but in this case the UI is kinda ugly + couldn't pull it off with any of the sample codes. Is there an other solution like having more boxes with one option chosen instead of one box with multiple options?
models:
class Product < ActiveRecord::Base
belongs_to :user
has_many :industry_products
has_many :industries, through: :industry_products
has_many :product_features
accepts_nested_attributes_for :industry_products, allow_destroy: true
accepts_nested_attributes_for :product_features
validates_associated :industry_products
validates_associated :product_features
end
class Industry < ActiveRecord::Base
has_many :industry_products
has_many :products, through: :industry_products
accepts_nested_attributes_for :industry_products
end
class IndustryProduct < ActiveRecord::Base
belongs_to :product
belongs_to :industry
end
_form.html.erb
<%= form_for #product do |f| %>
<%= render 'layouts/error_messages', object: f.object %>
......
<%= f.fields_for :industry_products do |p| %>
<%= p.collection_select :industry_id, Industry.all, :id, :name %>
<% end %>
<%= f.fields_for :product_features do |p| %>
<%= p.text_field :feature, placeholder: "add a feature", class: "form-control" %>
<% end %>
<%= f.submit class: "btn btn-primary" %>
<% end %>
products controller
def new
#product = Product.new
#product.industry_products.build
#product.product_features.build
end
def create
#product = current_user.products.new(product_params)
if #product.save
redirect_to #product
else
render action: :new
end
end
......
def product_params
params.require(:product).permit(....., industry_products_attributes: [:id, :industry_id, :_destroy], industries_attributes: [:id, :name], product_features_attributes: [:feature])
end
Firstly, you could fix your first collection select by using it to set the industry_ids for the #product:
<%= form_for #product do |f| %>
<%= f.collection_select :industry_ids, Industry.all, :id, :name %>
<% end %>
This will allow you to set the collection_singular_ids method, which exists for all has_many associations.
You'd have to back it up in the params method:
#app/controllers/products_controller.rb
....
def product_params
params.require(:product).permit(.... industry_ids: [])
end
A lot more succinct than using nested attributes.
To get that "multiple" selection, you'll want to use the following:
<%= f.collection_select :industry_ids, Industry.all, :id, :name, {}, { multiple: true } %>
Tested & working
--
You may also want to look at collection_check_boxes:
<%= f.collection_check_boxes :industry_ids, Industry.all, :id, :name %>

Model form within another model's form in Ruby on Rails

I have a Content model, a Playlist model and a join association in a model called PlaylistItem.
This is how they're connected:
class Content < ActiveRecord::Base
has_many :playlist_items
has_many :playlists, through: :playlist_items
end
class Playlist < ActiveRecord::Base
has_many :playlist_items, -> { order 'position ASC' }
has_many :contents, through: :playlist_items
end
class PlaylistItem < ActiveRecord::Base
belongs_to :content
belongs_to :playlist
end
When I edit a Playlist, the form only shows its field Name, that's the only attribute that it has. But I want to be able to add a Content to that Playlist (in a certain position, that's the PlaylistItems only attribute) within that form.
How can I do that?
This is the code I have by now:
<%= form_for(#playlist) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
</div>
<%= f.fields_for :playlist_items do |builder| %>
<fieldset>
<%= builder.label :content, "Content ID" %><br>
<%= builder.text_area :content_id %>
</fieldset>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
First, you can let you Playlist model accept nested attributes for playlist_items.
class Playlist < ActiveRecord::Base
has_many :playlist_items, -> { order 'position ASC' }
has_many :contents, through: :playlist_items
accepts_nested_attributes_for :playlist_items
end
Then, you can add content_id as a collection select, which will create a drop down selection of contents for each playlist item.
<%= f.fields_for :playlist_items do |builder| %>
<%= builder.label :position %><br>
<%= builder.integer :position %><br>
<%= builder.label :content %><br>
<%= builder.collection_select :content_id, #contents, :id, :title, include_blank: 'Please Select' %>
<% end %>
Finally, in your controller, you'll need to allow playlist items attributes to be passed as params, and also set your #contents instance variable.
class PlaylistController < ApplicationController
before_action :load_contents, only: [:new, :edit]
def edit
#playlist = Playlist.find(params[:id])
# Build 10 playlist_items to be added to your playlist
10.times do; #playlist.playlist_items.build; end
end
private
def load_contents
#contents = Content.all # or another query to get the specific contents you'd like
end
def playlist_params
params.require(:playlist).permit(:name, playlist_items_attributes: [:id, :position, :content_id])
end
end
Remember that you'll need to build each playlist item that you'd like to use. You can do that in your new and edit methods as you desire.

Nested form with has_many

I'm trying to make a nested form,
my form is for model A which have many Bs, and have to save them all on the save form.
The problem is: I'm building 3 bs on A controller, but only one field is showing up,
here's the code:
class A:
class A < ActiveRecord::Base
has_many :bs
accepts_nested_attributes_for :bs
end
class B:
class B < ActiveRecord::Base
belongs_to :a
end
A controller:
class AController < ApplicationController
def new
#a = A.new
3.times { #a.bs.build }
end
end
A and B form:
<%= form_for :a do |f| %>
<%= f.fields_for :bs do |b| %>
<%= b.text_field :name, :class => 'default' %>
<% end %>
<% end %>
<%= form_for #a do |f| %>
<%= f.fields_for :bs do |b| %>
<%= b.text_field :name, :class => 'default' %>
<% end %>
<% end %>
#a, not :a

ruby on rails - presenting many to many relation

I have recipe and ingredient in a many to many relation.
I have defined the following commands in my presentation.
<div>
<%= render :partial => 'ingredients/form',
:locals => {:form => recipe_form} %>
</div>
the partial begins with
<%= form_for(#ingredient) do |ingredient_form| %>
but received #ingredient nill.
Then I tried
<%= recipe_form.fields_for :ingredients do |builder| %>
<%= render 'ingredient_fields', f: builder %>
<% end %>
where my render was
<p class="fields">
<%= f.text_field :name %>
<%= f.hidden_field :_destroy %>
</p>
but nothing was printed.
then I tried
<% #recipe.ingredients.each do |ingredient| %>
<%= ingredient.name %>
<% end %>
and only then all of the ingredients were printed.
What was I doing wrong in the previous tries ?
Thank you.
my ingredient recipe relation defined as follows
class Ingredient < ActiveRecord::Base
has_many :ingredient_recipes
has_many :recipes, :through => :ingredient_recipes
...
class Recipe < ActiveRecord::Base
has_many :ingredient_recipes
has_many :ingredients, :through => :ingredient_recipes
...
accepts_nested_attributes_for :ingredient_recipes ,:reject_if => lambda { |a| a[:content].blank?}
class IngredientRecipe < ActiveRecord::Base
attr_accessible :created_at, :ingredient_id, :order, :recipe_id
belongs_to :recipe
belongs_to :ingredient
end
You don't exactly specify what you are trying to do, so I am presuming you have a page that shows a recipe, with many ingredients that can be edited and added to. In your controller you have something like:
class RecipeController < ApplicationController
def edit
#recipe = Recipe.find(params[:id]
end
end
I am also presuming that you are looking to have a form that post backs to the create action. I therefore think you want a form like this:
<%= form_for #recipe do |form| %>
<%= label_for :name %>
<%= text_field :name %>
<%= form.fields_for :ingredients do |ingredients_fields| %>
<div class="ingredient">
<%= f.text_field :name %>
<%= f.hidden_field :_destroy %>
</div>
<% end %>
<% end %>
Also, change your recipe to accept nested attributes for ingredients, not ingredient_recipes:
class Recipe < ActiveRecord::Base
has_many :ingredient_recipes
has_many :ingredients, :through => :ingredient_recipes
...
accepts_nested_attributes_for :ingredients, :reject_if => lambda { |a| a[:content].blank?}
And finally, add attr_accessible for your content:
class Ingredient < ActiveRecord::Base
attr_accessible :content
...
Does that work for you?

Resources