I am trying to implement multi-select in a form in rails 4, using Semantic UI. I want users to be able to select multiple categories for each post. So far, I am able to display the dropdown select field which pulls all the categories from the database as follows:
<%= form_for #post, :html => {:class => "ui form bk-form group"} do |f| %>
<label>Post title:</label><br />
<%= f.text_field :title, autofocus: true %>
<label>Choose a category:</label><br />
<%= f.select(:category_ids,options_for_select(Category.all.collect{|x| [x.name,x.id,class:'item']}),{prompt:'Select categories'}, multiple: true, class:'ui fluid dropdown selection multiple')%>
<% end %>
With this, I am able to create and save posts and the data is inserted in the database. However, when I try to edit an article, the pre-selected categories do not show. I have tried to set the value: #post.categories option in the select field and still cannot get the existing categories to show. Thanks in advance for your thoughts.
UPDATED
Models are as follows:
class Post < ActiveRecord::Base
has_many :post_categories
has_many :categories, through: :post_categories
end
class Category < ActiveRecord::Base
has_many :post_categories
has_many :posts, through: :post_categories
end
class PostCategory < ActiveRecord::Base
belongs_to :post
belongs_to :category, :counter_cache => :posts_count
end
Then my posts_controller.rb
class PostsController < ApplicationController
before_action :set_post, only: [:edit, :update, :show]
def index
#posts = Post.all
end
def new
end
def create
#post = Post.new(post_params)
#post.user = current_user
if #post.save
flash[:notice] = "Post was successfully created"
redirect_to user_posts_path
else
flash[:alert] = "Oh Snap!! We could not save your post"
render 'new'
end
end
def edit
end
def update
if #post.update(post_params)
flash[:notice] = "Post was successfully updated"
redirect_to user_posts_path
else
flash[:alert] = "Oh Snap!! We could not update your post"
render 'edit'
end
end
private
def post_params
params.require(:post).permit(:title, :description, :published, :tag_list, category_ids: [])
end
def set_post
#post = Post.find(params[:id])
end
end
Related
Trying to remove uploaded images using carrierwave
<%= f.fields_for :images do |ff| %>
<div class="form-group">
<label>
<%= ff.check_box :remove_image %>
<%= image_tag ff.object.image %>
</label>
</div>
<% end %>
Getting such params in controller
"images_attributes"=>{"0"=>{"remove_image"=>"0", "id"=>"13"}, "1"=>{"remove_image"=>"1", "id"=>"14"}, "2"=>{"remove_image"=>"0", "id"=>"15"}, "3"=>{"remove_image"=>"0", "id"=>"16"}, "4"=>{"remove_image"=>"0", "id"=>"17"}, "5"=>{"remove_image"=>"0", "id"=>"18"}}}
But when updating an object with these params nothing happens, what am I missing?
update
def update
#country = Country.find(params[:id])
if #country.update(country_params)
flash[:notice] = 'Country is successfully updated.'
redirect_to edit_admin_country_path
else
flash[:error] = #country.errors.full_messages[0]
render 'edit'
end
end
def country_params
permitted = [{images_attributes: ["image", "#original_filename", "#content_type", "#headers", "_destroy", "id", "remove_image"]}]
params.require(:country).permit(*permitted)
end
class Country < ActiveRecord::Base
has_many :images
....
end
class Image < ActiveRecord::Base
mount_uploader :image, ImageUploader
belongs_to :country
end
your form looks good but you are missing the controller action
mine looks like:
class ImageController < ApplicationController
...
def update
#image = Image.find(params[:id])
...
if params[:images][:remove_image].present?
#image.remove_image!
end
#image.save
end
end
If you want to remove the file manually, you can call remove_avatar!, then save the object.
I'm using the sign-up and login that we built with the rails tutorial as a base for a Reddit clone that I’m making. As it stands the application is functioning properly apart from the user_id in comments table is blank when I make a comment, the link_id is present and correct so I can make comments on a link. The user_id in links table is also present and correct.
I'm fairly certain that the error i've made is in the create action of comments_controller.rb but it could also be my original migration. What's confusing me (as a novice) is I had this working in it's current form once before with rails 4.1.8 and device. However, using this approach with rails 4.2.1 using the rails tutorial as a base, it doesn't work. I'm a bit new here so I hope i've formulated the post correctly and given enough information so somebody could give me some pointers as to the problem
Comment Controller
before_action :logged_in_user, only: [:create, :destroy]
def create
#link = Link.find(params[:link_id])
#comment = #link.comments.create(params[:comment].permit(:link_id, :body))
#comment.user = User.find(current_user.id)
redirect_to link_path(#link)
end
def destroy
#link = Link.find(params[:link_id])
#comment = #link.comments.find(params[:id])
#comment.destroy
redirect_to link_path(#link)
end
private
def logged_in_user
unless logged_in?
flash[:danger] = "Please log in."
redirect_to login_url
end
end
Form
app/views/links/show.html.erb
<h2 class="comment-count"><%= pluralize(#link.comments.count, "comment") %></h2>
<%= render #link.comments %>
<%= form_for([#link, #link.comments.build]) do |f| %>
<%= render 'comments/fields', f: f %>
<%= f.submit "Save Comment", class: 'btn btn-primary margin-bottom-10' %>
<% end %>
Partials
app/views/comments/_comment.html.erb
<p class="comment_body"><%= comment.body %></p>
<p class="comment_time"><%= time_ago_in_words(comment.created_at) %> Ago </p>
app/views/comments/_fields.html.erb
<%= render 'shared/comment_error_messages' %>
<%= f.label :body %>
<%= f.text_area :body, class: 'form-control' %>
Routes
config/routes.rb
resources :links do
member do
put "like", to: "links#upvote"
put "dislike", to: "links#downvote"
end
resources :comments
end
Models
app/models/link.rb
belongs_to :user
has_many :comments, dependent: :destroy
app/models/comment.rb
belongs_to :user
belongs_to :link
validates :body, presence: true
app/models/user.rb
has_many :links, dependent: :destroy
Migration
class CreateComments < ActiveRecord::Migration
def change
create_table :comments do |t|
t.integer :link_id
t.text :body
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
add_index :comments, :link_id
end
end
Hopefully that’s a good enough post and I’ve included everything you need so somebody can help me out, It's a rookie error but i can't see it. thanks in advance.
Regards
As it stands, you are currently creating a comment without saving user_id for it. Try the below code
#comments_controller.rb
def create
#link = Link.find(params[:link_id])
#comment = #link.comments.new(params[:comment].permit(:link_id, :body))
#comment.user = User.find(current_user.id)
#comment.save
redirect_to link_path(#link)
end
I am trying to update a nested form. The client table is updated successfully but the location table isn't update. Instead of its creating a new location. Do you guys have any solution ? I've already spend one day on it.
My models:
class Client < ActiveRecord::Base
has_many :locations, :dependent => :destroy
accepts_nested_attributes_for :locations, :allow_destroy => true, :update_only => true
end
class Location < ActiveRecord::Base
belongs_to :client
end
Selectcom::Application.routes.draw do
resources :clients
end
My Controller:
class ClientsController < ApplicationController
before_action :set_client, only: [:edit, :update]
def index
#clients = Client.paginate(page: params[:page])
end
def new
#client = Client.new
end
def create
#client = Client.new(client_params)
if #client.save
flash[:success] = "Client added successfully"
redirect_to clients_path
else
render 'new'
end
end
def edit
end
def update
if #client.update(only_client_params)
flash[:success] = "Job updated successfully"
redirect_to clients_path
else
render 'edit'
end
end
private
def set_client
#client = Client.find(params[:id])
end
def client_params
params.require(:client).permit(
:name,
:phone,
:fax,
:url,
:address,
:city,
locations_attributes: [
:site,
:fax,
:phone,
:url,
:address,
:_destroy
]
)
end
end
This is the client's edit.html.erb form
<%= form_for(#client, class: 'form-horizontal') do |f| %>
<%= render(partial: 'client_field', locals: {f: f}) %>
<%= f.fields_for :locations do |l| %>
<%= l.hidden_field :client_id, value: #client.id %>
<%= l.hidden_field :_destroy %>
<%= l.text_field :site, class: 'form-control' %>
<% end %>
<%= f.submit "Save", class: "btn lg-button" %>
<% end %>
Actually, you have two issues here. One in your Model and other in your Controller permitted params. Let's dig into them:
1) Model
The update_only option is ignored when used with collection association (it is your case), as said in Rails documentation:
For a one-to-one association, this option allows you to specify how
nested attributes are to be used when an associated record already
exists. In general, an existing record may either be updated with the
new set of attribute values or be replaced by a wholly new record
containing those values.
By default the :update_only option is false and the nested attributes are used to update the existing record only if they include
the record's :id value. Otherwise a new record will be instantiated
and used to replace the existing one.
However if the :update_only option is true, the nested attributes are
used to update the record's attributes always, regardless of whether
the :id is present. The option is ignored for collection
associations.
So, the first step would be removing the update_only option of your Client class, because it will be ignored since you have a has_many association (collection association) with locations:
class Client < ActiveRecord::Base
has_many :locations, :dependent => :destroy
accepts_nested_attributes_for :locations, :allow_destroy => true
end
2) Controller
You have to permit the :id key for the :locations_attributes array. Since your update_only option in the model was ignored, Rails needs the param to tell that it is a record that exists and it's being updated rather than created.
You can solve your issue using the following in your client_params method (note the addition of the id key in your :locations_attributes key):
def client_params
params.require(:client).permit(
:name,
:phone,
:fax,
:url,
:address,
:city,
locations_attributes: [
:id, # Should be present; otherwise, Rails thinks that is a new record
:site,
:fax,
:phone,
:url,
:address,
:_destroy
]
)
end
I hope it helps !!
I have:
a model User for common data such as email, password, first_name, last_name
a model Student for specific data such as faculty_id, etc..
What i want is to have a possibility to modify student's profile, e.g change its faculty and credentials. I found out that accepts_nested_attributes_for should help me with that. So that is what i have:
class User < ActiveRecord::Base
has_one :student
has_one :header
has_one :admin
has_many :progresses, :through => :student
accepts_nested_attributes_for :student
end
class Student < ActiveRecord::Base
belongs_to :user
belongs_to :group
has_one :specialization, through: :group
has_many :progresses
scope :activated, lambda {(where("users.activated = 'off'"))}
self.per_page = 1
end
class Admin::StudentsController < AdminController
before_action :set_user, only: [:show, :edit, :update, :destroy]
def index
#students = Student.joins(:user).joins(:group).paginate(:page => params[:page]).all
end
def show
end
def edit
end
def update
end
private
def set_user
#student = Student.find(params[:id])
#student.build_user
end
end
Form partial
= form_for([:admin,#student]) do |f|
- if #student.errors.any?
#error_explanation
h2
= pluralize(#student.errors.count, "error")
| prohibited this group from being saved:
ul
- #student.errors.full_messages.each do |msg|
li= msg
.field
= f.fields_for :user do |builder|
= builder.label :first_name
br/
= builder.text_field :first_name
.field
= f.label :faculty_id
br/
= f.text_field :faculty_id
.actions
= f.submit
I looked into inspect element in a browser and input for user object is
<input id="student_user_first_name" name="student[user][first_name]" type="text">
which seems correct. But the field is empty. Please tell me where i went wrong? Thank you.
Rails 4 uses Strong Parameters, so you need to whitelist your params in the controller. Here is a good explanation which considers the use of accepts_nested_attribute_for. http://edgeapi.rubyonrails.org/classes/ActionController/StrongParameters.html
I am a newbie to Ruby on Rails and have scoured Stackoverflow and the internet to the best of my abilities and am still stumped.
In my set-up, a Rating belongs_to a Product and has_many Comments. I'm using simple-form and trying to use nested fields_for to add RatingComment through the Rating form. Interestingly, when using the singular form of :rating_comment, the field is displayed. But as expected, I get the unpermitted parameter error when trying to save. When I use plural :rating_comments, the field disappears. This is similar to this SO posting, but adding #rating.rating_comments.build to new action still does not work for me. I've tried restarting the server many times, and even reset the database to no avail. Would appreciate any assistance as I've been struggling with this issue for the past few days.
Note: I've also taken out what I think is irrelevant code from the snippets below. If I need to show more information, please do let me know. Thanks in advance!
routes.rb
resources :ratings, only: [:new, :create, :edit, :update, :destroy] do
resources :rating_comments, shallow: true
end
rating.rb
class Rating < ActiveRecord::Base
belongs_to :product
belongs_to :user
has_many :rating_comments, foreign_key: "rating_id", dependent: :destroy
accepts_nested_attributes_for :rating_comments, reject_if: :all_blank
end
rating_comment.rb
class RatingComment < ActiveRecord::Base
belongs_to :rating
validates :rating_id, presence: true
end
ratings_controller.rb
class RatingsController < ApplicationController
before_action :signed_in_user, only: [:create, :new, :show]
def new
#product = Product.find(params[:id])
#rating = #product.ratings.new
#rating.rating_comments.build
end
def create
#product = Product.find(params[:product_id])
#rating = #product.ratings.build(rating_params)
#rating.user_id = current_user.id
...
private
def rating_params
params.require(:rating).permit(:user_id, :product_id, :rating, rating_comments_attributes: [:rating_id, :content])
end
end
ratings/_new_rating_form.html.erb
<%= simple_form_for([#product, #rating], html: { class: 'form-horizontal' }) do |f| %>
<%= f.error_notification %>
<%= f.input :rating, collection: 1..10, as: :radio_buttons,
item_wrapper_class: 'inline', checked: 5 %>
<%= f.simple_fields_for :rating_comments do |rc| %>
<fieldset>
<%= rc.input :content, label: "Comments" %>
</fieldset>
<% end %>
<%= f.error :base %>
<%= f.button :submit, "Submit" %>
<% end %>
Very embarrassed to admit that it turns out that I had the view written in the wrong action. Once I put it in the new.html, it finally worked.