Tag model on another Rails - ruby-on-rails

I want to tag my product model on my post model.
post.rb
has_many :taggings
has_many :products, through: :taggings
product.rb
has_many :taggings
has_many :posts, through: :taggings
tagging.rb
belongs_to :post
belongs_to :product
On my post view, I have a product list. I want that when the user clicks on a product, it creates a new product/post link via post method.
What link can I use? How to set up controllers and params?
Thanks for the help

If you want to let the user create multiple taggings at once you can just add a select / checkbox to the forms for a post.
<%= form_for(#post) do |f| %>
# ...
<div class="field">
<%= f.label :product_ids %>
<%= f.collection_select :product_ids, Product.all, :name, :id %>
</div>
<% end %>
def post_params
params.require(:post)
.permit(:foo, :bar, product_ids: [])
end
Rails will automatically create the records in the join table.
If you want the user to create the linkings one by one you need to setup a nested route:
Rails.application.routes.draw do
# ...
resources :posts do
resources :taggings, only: :create
end
end
You then need to setup a form for each product on the posts/show.html.erb page:
<ul>
<% #post.products.each do |product| %>
<li>
<%= product.name %>
<%= form_for [#post, product.taggings.new] do |f| %>
<%= f.hidden_field :product_id %>
<%= f.submit 'tag' %>
<% end %>
</li>
<% end %>
</ul>
You can pretty this up later with CSS/JS.
And a controller to handle creating the taggings.
class TaggingsController < ApplicationController
# POST /posts/:post_id/taggings
def create
#post = Post.find(params[:post_id])
#tagging = #post.taggings.new(product: Product.find(params[:tagging][:product_id]))
if #tagging.save
redirect_to #product, success: 'Tagging saved.'
else
redirect_to #product, error: 'Tagging not saved.'
end
end
end

Related

Rails has many through fields_for

I have a form I would like to create that populates the corresponding model form for, and a fields_for that populates a has many through table.
The plan.rb model:
class Plan < ApplicationRecord
has_many :plan_materials
has_many :materials, through: :plan_materials
accepts_nested_attributes_for :materials
end
The materials.rb model:
class Material < ApplicationRecord
has_many :plan_materials
has_many :plans, through: :plan_materials
end
And the PlanMaterial model:
class PlanMaterial < ApplicationRecord
belongs_to :plan
belongs_to :material
end
This is what I have in the plan form:
<%= form_for #plan do |form| %>
<div class="form-group">
<%= form.label :name %>
<%= form.text_field :name, class: 'form-control' %>
</div>
<div class="form-group">
<%= form.label :description %>
<%= form.text_field :description, class: 'form-control' %>
</div>
<%= form.fields_for :materials, plan.materials.build do |material_fields| %>
<%= material_fields.text_field :title %>
<% end %>
<%= form.submit %>
<% end %>
I have created fields_for forms before but never trying to input the information for the form being created, and the id of the material they are selecting, in a new table from that same form.
I am creating this through a plan.materials.build which I realize could possibly be the wrong way to go about doing this because I don't think it would be building that in the plan_materials table.
class PlansController < ApplicationController
def index
#plans = Plan.all
end
def new
#plan = Plan.new
#plan.materials.build
end
def create
#plan = Plan.new(plan_params)
respond_to do |format|
if #plan.save
format.html { redirect_to plan_path, notice: 'Plan has been created' }
else
format.html { render :new, notice: 'There was an error saving your plan' }
end
end
end
private
def plan_params
params.require(:plan).permit(:name, :description, :grade_id, :subject_id, :unit_id, materials_attributes: [:title])
end
end
So, just to recap, I would like to create a plan, and be able to add materials to that plan. I need to have that plan created in the plans table, along with the id of the plan that was created and the id of the material that was added in that form. How do I go about doing this?

Rails nested form fields_for doubled in edit

I'm trying to create a form where users can see a list of their friends and add or remove friends from lists (sort of like facebook's groups). For the create list view, I can get the form working properly, but for the edit view I'm having trouble with check_box syntax and fields_for.
routes.rb
resources :friendships
resources :friend_lists
class User < ActiveRecord::Base
has_many :friendships, dependent: :destroy
has_many :friends, through: :friendships
has_many :friend_lists
has_many :flist_memberships, class_name: 'FlistMembership', foreign_key: 'member_id', dependent: :destroy
...
end
class FriendList < ActiveRecord::Base
belongs_to :user
has_many :flist_memberships
has_many :members, through: :flist_memberships
accepts_nested_attributes_for :members, allow_destroy: true
accepts_nested_attributes_for :flist_memberships, allow_destroy: true
end
class FlistMembership < ActiveRecord::Base
belongs_to :friend_list
belongs_to :member, class_name: 'User'
end
class FriendListsController < ApplicationController
def new
#friend_list = current_user.friend_lists.build
#friends = current_user.friends.paginate(page: params[:page])
#friend_list.flist_memberships.build
end
def create
#friend_list = current_user.friend_lists.build(friend_list_params)
respond_to do |format|
format.html {
if #friend_list.save
flash[:success] = "Friends list created!"
redirect_to friendships_path
else
render 'friend_lists/new'
end
}
end
end
def edit
#friend_list = FriendList.find(params[:id])
#friends = current_user.friends
end
def update
#friend_list = FriendList.find(params[:id])
if #friend_list.update_attributes(friend_list_params)
flash[:success] = "Friend list updated!"
redirect_to friend_list_path(#friend_list)
else
render 'edit'
end
end
private
def friend_list_params
params.require(:friend_list).permit(:name, flist_memberships_attributes: [:id, :member_id, :friend_list_id])
end
end
new.html.erb
<%= form_for #friend_list do |f| %>
<%= f.label :name, "Name for this friends list:" %>
<%= f.text_field :name %>
<% #friends.each do |friend| %>
<%= f.fields_for :flist_memberships do |m| %>
<%= m.check_box :member_id, {}, friend.id %>
<%= m.label :member_id, friend.name %>
<% end %>
<% end %>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
edit.html.erb
<%= form_for #friend_list do |f| %>
<%= f.label :name, "Name for this friends list:" %>
<%= f.text_field :name %>
<%= f.fields_for :flist_memberships do |m| %>
<%= m.collection_check_boxes(:member_id, #friends.all, :id, :name) %>
<% end %>
<%= f.submit "Save", class: "btn btn-primary" %>
<% end %>
new.html.erb renders and saves the selected items correctly, but I can't figure out how to write the edit form so that it doesn't iterate through the collection once for each item in the collection (both with collection_check_boxes and the form as written in new.html.erb). Trying
<%= f.fields_for :flist_memberships do |m| %>
<%= m.check_box :id %>
<%= m.label :id, :name %>
<% end %>
in the edit form just passes the string "id" as params rather than pulling the id of each member in the collection, and I'm not sure how to pull the individual item's name/id without iterating over the collection, which goes back to the multiple renders problem. I'm using cocoon elsewhere in the project, but for this I'd rather present the user a list of all options and allow them to check a box for each one, rather than having to manually add each item on the list. If there is a way to do it with cocoon, I'd be happy to hear it.

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 %>

How to add tags to posts using checkbox?

I want to build a many-to-many association through tagging between posts and tags. Users can create posts with tags by checking the existed tags. But I don't know how to create a nested form and save the association.
My form
<%= form_for(#post) do |f| %>
<div class="field">
<%= f.label :text %><br />
<%= f.text_field :text %>
</div>
<div>
<%= hidden_field_tag "post[tag_ids][]", nil %>
<% Tag.all.each do |tag| %>
<%= check_box_tag "post[tag_ids][]", tag.id, #post.tag_ids.include?(tag.id) %>
<%= tag.name %>
<% end %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
posts controller here:
class PostsController < ApplicationController
def index
#posts = Post.all
end
def new
#post = Post.new
end
def create
#post = Post.new(post_params)
if #post.save
redirect_to #post
else
render 'new'
end
end
def show
#post = Post.find(params[:id])
end
private
def post_params
params.require(:post).permit(:text)
end
end
Model
class Post < ActiveRecord::Base
has_many :taggings
has_many :tags, :through => :taggings
end
class Tag < ActiveRecord::Base
has_many :taggings
has_many :posts, :through => :taggings
end
class Tagging < ActiveRecord::Base
belongs_to :tag
belongs_to :post
end
I don't think you actually need to have a nested form in this case. Rails automagically figures out the association, when given the attribute :tag_ids
This is what I did for a multiselect form_helper, which would need minor changes for a check_box helper
f.select :tag_ids, Tag.all.collect {|tag| [tag.name, tag.id]}, {}, :multiple => true

Rails: Comment Model - Connected to a Micropost_id and User_id

EDIT
The Routing Error has disappeared thanks to carlosramireziii who helped changed the Form to
<%= form_for (#micropost) do |f| %>
<%= fields_for :comments do |ff| %>
<%= ff.text_area :content %>
<% end %>
<div class="CommentButtonContainer">
<%= f.submit "Comment" %>
</div>
<% end %>
But the issue now is that the post will not save, any suggestions?
I am currently making a comments model that is connected to a:
micropost_id :integer and user_id :integer
The problem that I continue to receive is when I post something I get this in return:
Routing Error
No route matches [POST] "/microposts/comments"
This is my routes.eb
Project::Application.routes.draw do
resources :microposts do
resources :comments
end
This is the Comment Form
<%= form_for([#micropost, #micropost.comments.new]) do |f| %>
<%= f.text_area :content %>
<div class="CommentButtonContainer">
<%= f.submit "Comment" %>
</div>
<% end %>
This is the Comment Template
<%= div_for comment do %>
<div class='UserCommentContainer'>
<div class='UserComment'>
<div class='UserName sm'>
Anonymous
</div>
<div class='UserCommentText'>
<%= comment.content %>
</div>
</div>
</div>
<% end %>
And finally this is what is inside the Micropost
<div id='CommentContainer' class='Condensed2'>
<div class='Comment'>
<%= render "comments/form" %>
</div>
<div id='comments'>
<%= render #micropost.comments %>
</div>
</div>
Everything else related to the comments model and controller I have posted below, I have been mulling around this for a long time and could really use help, thank you!
Comments Model
class Comment < ActiveRecord::Base
attr_accessible :content
belongs_to :micropost
validates :content, presence: true, length: { maximum: 140 }
validates :user_id, presence: true
validates :micropost_id, presence: true
default_scope order: 'comments.created_at DESC'
end
Micropost Model
class Micropost < ActiveRecord::Base
belongs_to :user
has_many :comments
validates :user_id, presence: true
end
User Model
class User < ActiveRecord::Base
has_many :microposts
has_many :replies, :through => :microposts, :source => :comments
end
Comments Controller
class CommentsController < ApplicationController
def create
#comment = #micropost.comments.new(params[:comment])
if #comment.save
redirect_to #user
else
redirect_to #user
end
end
end
User Controller
class UsersController < ApplicationController
def show
#user = User.find(params[:id])
#micropost = Micropost.new
#comment = #micropost.comments.new
#microposts = #user.microposts.paginate(page: params[:page])
end
end
I believe the problem is that you are trying to save a new comment on a micropost that hasn't been saved yet. Since you have your comments routes nested underneath the microposts routes, the micropost needs to exist before you can create a new comment.
If you want to create both objects in the same form, you need to use nested model attributes.
Micropost
class Micropost < ActiveRecord::Base
belongs_to :user
has_many :comments
accepts_nested_attributes_for :comments
validates :user_id, presence: true
end
Form
<%= form_for(#micropost) do |f| %>
<%= f.fields_for :comments do |ff %>
<%= ff.text_area :content %>
<% end %>
<div class="CommentButtonContainer">
<%= f.submit "Comment" %>
</div>
<% end %>

Resources