select box by simple_form in Rails - save id, not title - ruby-on-rails

I use Simple Form in Rails app for generate forms.
Schema:
create_table "product_materials", force: :cascade do |t|
t.integer "product_id"
t.integer "material_id"
t.integer "level", default: 0
t.integer "value", default: 0
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Model:
class ProductMaterial < ActiveRecord::Base
belongs_to :product
belongs_to :material
enum level: [:joinery, :grinding, :painting, :assembly]
end
View:
= simple_form_for [:admin, #product_material], remote: true do |f|
=> f.hidden_field :product_id, value: #product.id
=> f.input :level, collection: [t('product.joinery'), t('product.grinding'), t('product.painting'), t('product.assembly')], label: false
=> f.association :material
=> f.submit t('form.save')
And now is a question:
How I can in select_box show some collection of names (using I18n), but save a number of selected item (integer)?

How I can in select_box show some collection of names (using I18n),
but save a number of selected item (integer)?
ProductMaterial.levels should return a hash like(note a plural form levels):
=> {"joinery"=>0, "grinding"=>1, "painting"=>2, "assembly"=>3}
You can use it, all what you need it's make an array of arrays:
=> ProductMaterial.levels.keys.map{ |x| t("product.#{x}") }.zip(ProductMaterial.levels.values)
#> [[t("product.joinery"), 0], [t("product.grinding"), 1], [t("product.painting"), 2], [t("product.assembly"), 3]]
In input:
=> f.input :level, collection: ProductMaterial.levels.keys.map{ |x| t("product.#{x}") }.zip(ProductMaterial.levels.values), label: false
enum have a good documentation.

Related

How to use collection select for hidden value

I have a table called sublet post which has these columns and there is a reference column to the property table.
create_table "sublet_posts", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "property_id"
t.index ["property_id"], name: "index_sublet_posts_on_property_id"
Property table
create_table "properties", force: :cascade do |t|
t.string "address"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
In the sublet post form view
<div class="field">
<%= collection_select(:sublet_post, :property_id, #properties, :id, :address, prompt: true) %>
<%= form.hidden_field :property_id, value: params[:sublet_post][:property_id] %>
</div>
In the form view, I am using the address as the selections and if a user clicks on an address, I would like the id to be stored in the for the value of the hidden value :property_id. I would like to do something similar to the code above.
You can add an id to both elements and catch the change event.
The html will be so:
<div class="field">
<%= collection_select(:sublet_post, :property_id, #properties, :id, :address, prompt: true, id: 'the_collection') %>
<%= form.hidden_field :property_id, value: params[:sublet_post][:property_id], id: 'the_field'%>
</div>
And the javascript, if you are using jQuery :
$(document).on('change', '#the_collection', function(event) {
var element = $(event.target);
element.siblings('#the_field').val(element.val());
});
https://api.jquery.com/siblings/

Rails 4 - Unpermitted parameters for nested param despite whitelisting

I have two models user_item and user_item_images.
schema.rb
create_table "user_item_images", force: :cascade do |t|
t.integer "user_item_id"
t.datetime "created_at"
t.datetime "updated_at"
t.string "picture"
end
create_table "user_items", force: :cascade do |t|
t.integer "user_id"
t.integer "item_id"
t.integer "status", default: 0
t.boolean "hide_banner", default: false
t.datetime "created_at"
t.datetime "updated_at"
t.string "picture"
t.string "declined_reason"
end
I have a form where the user should submit a new user_item. The only field in the form is for pictures where the user can upload multiple pictures. On success, a single new user_item is created along with a new user_item_image for each picture that is uploaded.
form
<%= simple_form_for :user_item, url: user_items_path, html: {multipart: true} do |user_item_builder| %>
<%= user_item_builder.input :item_id, as: :hidden, input_html: { value: "#{#item.id}" } %>
<%= user_item_builder.simple_fields_for :user_item_images do |user_item_images_builder| %>
<%= user_item_images_builder.input :picture, as: :file, label: false, name: "user_item_images[picture][]", input_html: { multiple: true } %>
<% end %>
<% end %>
user_item_controller.rb
def create
#user_item = current_user.user_items.new(user_item_params)
raise 'foo'
if #user_item.save
params[:user_item][:user_item_images]['picture'].each do |a|
#user_item_image = #user_item.user_item_images.create!(:picture => a)
end
# Sends email to user when item request is created.
itemMailer.user_item_submission_email(current_user, #user_item.item).deliver_later
flash[:notice] = "Thank you for your item request!"
else
#user_item.errors.full_messages.each do |message|
flash[:alert] = message
end
end
redirect_to item_path(#user_item.item)
end
private
def user_item_params
params.require(:user_item).permit(:item_id, user_item_images_attributes: [:user_item_id, :picture])
end
user_item.rb
belongs_to :user
has_many :user_item_images
mount_uploader :picture, PictureUploader
accepts_nested_attributes_for :user_item_images
user_item_image.rb
mount_uploader :picture, PictureUploader
belongs_to :user_item
When I submit the form I get Unpermitted parameter: user_item_images in the console and I can't figure out why.
Change this:
<%= ..., name: "user_item_images[picture][]", ... %>
to this:
<%= ..., name: "user_item_images_attributes[picture][]", ... %>

Has_many through: association for category_sizes

I'm creating a clothing store. I have Categories that have Sizes. A womens shirt (Category) might have XS, S, M, Large. A mens shirt can have XS, S, M, L. Shoes can have 4-16 and so on.
I have created a has_many through: association that connects the Category table with Sizes table by a Cateogry_Sizes table.
When an admin creates a Category, they should select all the Sizes that the Category will need.
How can I select the Sizes in the below view?
The current code is incorrect. In the console, when I go to category.sizes, I just get an empty array.
View:
<div class="container">
<div class=“row”>
<div class="col-md-6 col-md-offset-3">
<div class="panel panel-primary">
<div class="panel-body">
<%= simple_form_for(#category) do |f| %>
<div class="form-inputs">
<%= f.input :name %>
<%= f.select(:sizes, Size.all.map {|s| [s.title, s.id]}, :multiple => true) %>
<%= f.collection_select :parent_id, Category.order(:name), :id, :name, {prompt: "Select Parrent ID If Applicable"},include_blank: true %>
<div class="form-actions"><%= f.button :submit %></div>
</div>
<% end %>
</div>
</div>
</div>
</div>
</div>
Category model:
class Category < ActiveRecord::Base
has_ancestry
has_many :items
validates :name, presence: true, length: { maximum: 20 }
has_many :category_sizes
has_many :sizes, through: :category_sizes
end
Size model:
class Size < ActiveRecord::Base
validates :title, presence: true, length: { maximum: 15 }
validates :title, uniqueness: true
has_many :category_sizes
has_many :categories, through: :category_sizes
end
Category_size model:
class CategorySize < ActiveRecord::Base
belongs_to :category
belongs_to :size
end
Schema:
ActiveRecord::Schema.define(version: 20150920013947) do
create_table "categories", force: :cascade do |t|
t.string "name"
t.string "ancestry"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "categories", ["ancestry"], name: "index_categories_on_ancestry"
create_table "category_sizes", force: :cascade do |t|
t.integer "category_id"
t.integer "size_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "category_sizes", ["category_id"], name: "index_category_sizes_on_category_id"
add_index "category_sizes", ["size_id"], name: "index_category_sizes_on_size_id"
create_table "items", force: :cascade do |t|
t.string "title"
t.decimal "price"
t.text "description"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "category_id"
end
add_index "items", ["user_id", "created_at"], name: "index_items_on_user_id_and_created_at"
add_index "items", ["user_id"], name: "index_items_on_user_id"
create_table "sizes", force: :cascade do |t|
t.text "title"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "taggings", force: :cascade do |t|
t.integer "tag_id"
t.integer "taggable_id"
t.string "taggable_type"
t.integer "tagger_id"
t.string "tagger_type"
t.string "context", limit: 128
t.datetime "created_at"
end
add_index "taggings", ["tag_id", "taggable_id", "taggable_type", "context", "tagger_id", "tagger_type"], name: "taggings_idx", unique: true
add_index "taggings", ["taggable_id", "taggable_type", "context"], name: "index_taggings_on_taggable_id_and_taggable_type_and_context"
create_table "tags", force: :cascade do |t|
t.string "name"
t.integer "taggings_count", default: 0
end
add_index "tags", ["name"], name: "index_tags_on_name", unique: true
create_table "users", force: :cascade do |t|
t.string "username"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.string "activation_digest"
t.boolean "activated", default: false
t.datetime "activated_at"
t.string "reset_digest"
t.string ">"
t.datetime "reset_sent_at"
t.string "avatar_file_name"
t.string "avatar_content_type"
t.integer "avatar_file_size"
t.datetime "avatar_updated_at"
t.text "description"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
end
Controller:
class CategoriesController < ApplicationController
before_action :set_category, only: [:show]
before_action :admin_user, only: [:destroy, :index, :edit, :show]
def index
#categories = Category.all
end
def show
#tags = Item.where(category_id: #category.id).tag_counts_on(:tags)
if params[:tag]
#items = Item.tagged_with(params[:tag])
else
#items = Item.where(category_id: #category.id).order("created_at DESC")
end
end
def new
#category = Category.new
end
def edit
#category = Category.find(params[:id])
end
def create
#category = Category.new(category_params)
if #category.save
redirect_to #category
flash[:success] = "You have created a new category"
else
flash[:danger] = "Your category didn't save"
render "new"
end
end
def update
#Cateogry = Category.find(params[:id])
if #Cateogry.update(category_params)
redirect_to #Cateogry
flash[:success] = 'Category was successfully updated.'
else
render "edit"
end
end
def destroy
Category.find(params[:id]).destroy
flash[:success] = "Category deleted"
redirect_to categories_path
end
private
def set_category
#category = Category.find(params[:id])
end
def category_params
params.require(:category).permit(:name, :parent_id)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.try(:admin?)
end
end
Here is what happens when in console:
2.1.2 :026 > c = Category.last
Category Load (0.3ms) SELECT "categories".* FROM "categories" ORDER BY "categories"."id" DESC LIMIT 1
=> #<Category id: 57, name: "Test20", ancestry: "20", created_at: "2015-09-23 12:35:14", updated_at: "2015-09-23 12:35:14">
2.1.2 :027 > c.sizes
Size Load (0.2ms) SELECT "sizes".* FROM "sizes" INNER JOIN "category_sizes" ON "sizes"."id" = "category_sizes"."size_id" WHERE "category_sizes"."category_id" = ? [["category_id", 57]]
Here is what happens on form submit in server log:
Started POST "/categories" for ::1 at 2015-09-23 22:37:28 +1000
Processing by CategoriesController#create as HTML
Parameters: {"utf8"=>"✓", "authenticity_token"=>"4pMZ9PUr5yTSCNRiQeATljZsOIDeQCwhQPy9djEbAmejntpb8/DkK20JrMUeZkStsB5UU6YhbtExGwDKs7tT2Q==", "category"=>{"name"=>"test21", "sizes"=>"6", "parent_id"=>"20"}, "commit"=>"Create Category"}
Unpermitted parameter: sizes
Category Load (0.1ms) SELECT "categories".* FROM "categories" WHERE "categories"."id" = ? LIMIT 1 [["id", 20]]
(0.1ms) begin transaction
SQL (0.3ms) INSERT INTO "categories" ("name", "ancestry", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["name", "test21"], ["ancestry", "20"], ["created_at", "2015-09-23 12:37:28.927360"], ["updated_at", "2015-09-23 12:37:28.927360"]]
(1.0ms) commit transaction
Redirected to http://localhost:3000/categories/58
Completed 302 Found in 5ms (ActiveRecord: 1.5ms)
Started GET "/categories/58" for ::1 at 2015-09-23 22:37:28 +1000
Processing by CategoriesController#show as HTML
Parameters: {"id"=>"58"}
You need to allow the sizes in the params of the controller like this:
def category_params
params.require(:category).permit(:name, :parent_id, size_ids: [])
end
In your form you should probably change:
<%= f.select(:sizes, Size.all.map {|s| [s.title, s.id]}, :multiple => true) %>
to
<%= f.association :sizes %>
Simple form should then do the magic. See also: https://github.com/plataformatec/simple_form#associations for more information.
As an aside, I felt it apt to highlight that you may be able to use has_and_belongs_to_many to make this work in its current form. It works very similarly to has_many :through except it doesn't have a join model:
#app/models/category.rb
class Category < ActiveRecord::Base
has_and_belongs_to_many :sizes
end
#app/models/size.rb
class Size < ActiveRecord::Base
has_and_belongs_to_many :categories
end
This will mean you'll have to drop your category_sizes table and replace it with categories_sizes with category_id | size_id columns:
This is only recommended if you don't want to include any further information in your join model. For example, if you wanted to include stock levels or something, the join model of has_many :through would be vital; not as you have it now.
It will also allow you to call:
#category = Category.find params[:id]
#category.sizes #-> collection of sizes for category.
--
Form
HABTM also would make the form much simpler:
<%= simple_form_for(#category) do |f| %>
<%= f.input :name %>
<%= f.collection_select :sizes, Size.all, :id, :name, { multiple: true } %>
<%= f.collection_select :parent_id, Category.order(:name), :id, :name, {prompt: "Select Parent ID If Applicable"},include_blank: true %>
<%= f.submit %>
<% end %>
#app/controllers/categories_controller.rb
class CategoriesController < ApplicationController
def create
#category = Category.new category_params
end
private
def category_params
params.require(:category).permit(:etc, :etc, :sizes)
end
end

Category(#69942071276340) expected, got String(#8940340)

I'm trying to submit a form for my rails app but I'm getting the error in the title and I'm not entirely sure how to fix it. Category is my foreign key.
= form_for #menu_price do |f|
- if #menu_price.errors.any?
#error_explanation
h2 = "#{pluralize(#menu_price.errors.count, "error")} prohibited this menu_price from being saved:"
ul
- #menu_price.errors.full_messages.each do |message|
li = message
.form-group
= f.label :category
= f.select :category, options_for_select(#categories.map{ |c| [c.name] })
.form-group
= f.label :price
= f.number_field :price
.form-group
= f.label :description
= f.text_field :description
.form-group
= f.label :serves
= f.text_field :serves
= f.submit
= link_to 'Back', menu_prices_path, class:'button'
My models look like this
class Category < ActiveRecord::Base
has_many :menu_prices
validates :category, :presence => true
end
***********Updated***********
class CreateMenuPrices < ActiveRecord::Migration
def change
create_table :menu_prices do |t|
t.text :description, :null => false
t.decimal :price , :default => nil , :null => true
t.string :serves, :default => nil , :null => true
t.integer :small , :default => nil , :null => true
t.integer :regular, :default => nil , :null => true
t.integer :large, :default => nil , :null => true
t.integer :party, :default => nil, :null => true
t.timestamps null: false
end
add_reference :menu_prices, :categories
end
end
I understand that it wants a foreign key but I'm not sure how to go about submitting the foreign key in the form. Any help would be greatly appreciated.
******UPDATE******
My schema.rb is below
create_table "categories", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "menu_prices", force: :cascade do |t|
t.text "description", null: false
t.decimal "price"
t.string "serves"
t.integer "small"
t.integer "regular"
t.integer "large"
t.integer "party"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "categories_id"
end
end
Since you're actually passing only category_id from view (and not whole Category object), instead of
f.label :category
and
f.select :category
you should have:
f.label :category_id
and
f.select :category_id
You should also make sure category_id column is present in menu_prices table.

Rails, Ransack, HABTM - Empty result when multiple select

I have a form with :
<%= f.collection_select :carburants_id_eq_all, Carburant.order(:name), :id, :name, {}, { multiple: true } %>
When I select only 1 Carburant, it's render me the result normaly, but if I select more than 1 Carburant, the result is empty.
Here's my form in 'index.html.erb' :
<%= search_form_for #search do |f| %>
<%= f.label :title_cont, "Name contains" %>
<%= f.text_field :title_cont %>
<%= f.collection_select :carburants_id_eq_all, Carburant.order(:name), :id, :name, {}, { multiple: true } %>
<%= f.submit "Search" %>
<% end %>
My index action in my 'post_controller.rb':
def index
#search = Post.search(params[:q])
#posts = #search.result(distinct: true)
end
My post.rb, carburant.rb and category.rb model :
class Category < ActiveRecord::Base
attr_accessible :carburant_id, :post_id
belongs_to :carburant
belongs_to :post
end
class Post < ActiveRecord::Base
attr_accessible :content, :title, :carburant_ids
has_many :categories
has_many :carburants, through: :categories
end
class Carburant < ActiveRecord::Base
attr_accessible :name
has_many :categories
has_many :posts, through: :categories
end
My DB shema :
ActiveRecord::Schema.define(:version => 20130114004613) do
create_table "carburants", :force => true do |t|
t.string "name", :limit => nil
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "categories", :force => true do |t|
t.integer "post_id"
t.integer "carburant_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
create_table "posts", :force => true do |t|
t.string "title"
t.text "content"
t.datetime "created_at"
t.datetime "updated_at"
end
create_table "recherches", :force => true do |t|
t.string "title"
t.integer "carburant_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
end
Thank you by advance.
EDIT 1:
I tried replacing :carburants_id_eq_all by :carburants_id_eq_any in my form just to test, and it work perfecly with the EQ_ANY predicate, but still not with the EQ_ALL.

Resources