How do I render a list of existing categories in my views?
My categories table has a :name column.
products view
= f.select :category_name, Category.all.map{|s| [s.name]}
Category.rb
class Category < ActiveRecord::Base
has_many :categoricals
validates :name, uniqueness: { case_sensitive: false }, presence: true
acts_as_tree order: "name"
end
Product.rb
class Product < ActiveRecord::Base
include ActionView::Helpers
include Categorizable
end
UPDATE:
Full product form
= simple_form_for #product, html: { multipart: true } do |f|
= f.input :title, placeholder: "Product Name", required: false, label: false
= f.input :description, placeholder: "Description", required: false, label: false
= f.select :category_name, Category.all.map{|s| s.name}
= f.input :image, label: false
= f.button :submit, class: "button"
UPDATE #2:
products controller
def update
if #product.update(product_params)
redirect_to #product, notice: "Your Product was successfully updated!"
else
render 'edit'
end
end
categorical model
class Categorical < ActiveRecord::Base
belongs_to :category
belongs_to :categorizable, polymorphic: true
validates_presence_of :category, :categorizable
end
categorizable module
module Categorizable
extend ActiveSupport::Concern
included do
has_many :categoricals, as: :categorizable
has_many :categories, through: :categoricals
end
def add_to_category(category)
self.categoricals.create(category: category)
end
def remove_from_category(category)
self.categoricals.find_by(category: category).maybe.destroy
end
module ClassMethods
end
end
I never understand why people don't use collection_select over select:
= f.collection_select :category_id, Category.all, :id, :name
It seems the real problem is due to your relations:
#app/models/category.rb
class Category < ActiveRecord::Base
has_many :products
end
#app/models/product.rb
class Product < ActiveRecord::Base
belongs_to :category #-> requires category_id column in products table
end
This will allow you to reference the :category_id as follows:
= simple_form_for #product, html: { multipart: true } do |f|
= f.input :title, placeholder: "Product Name", required: false, label: false
= f.input :description, placeholder: "Description", required: false, label: false
= f.collection_select :category_id, Category.all, :id, :name
= f.input :image, label: false
= f.button :submit, class: "button"
Try:
= f.select :category_name, Category.all.map{|s| s.name}
In order to make this work, you have to have category_name column present in your products table.
Related
I have problem. I'm trying to get an email member from wallet_id which that relation not direct to member instead to investor account first like the sample model below.
member.rb
class Member < ActiveRecord::Base
has_one :investor_account, dependent: :destroy
end
investor_account.rb
class InvestorAccount < ActiveRecord::Base
belongs_to :member
has_many :wallets, dependent: :destroy
end
wallet.rb
class Wallet < ActiveRecord::Base
belongs_to :investor_account
end
top_up.rb
belongs_to :wallet
/top_ups/_form.html.slim
= simple_form_for [:transaction, #top_up] do |f|
.form-group
= f.input :wallet_id, collection: #wallet, input_html: { class: 'form-control' }, include_blank: "Select..."
.form-group
= f.input :amount, input_html: { min: 0, value: 0 }
/controllers/top_ups_controller.rb
def new
#top_up = TopUp.new
#wallet = Wallet.joins(:investor_account).where(investor_accounts: {approval_status: 'approved'})
end
The data on "f.input :wallet_id ...." was showed up, but it is not as email of the member instead it showing #<Wallet:0x007fd6d795e808> on all wallet dropdown, previously I also write code like below.
= f.input :wallet_id, collection: #wallet, :id, :email, input_html: { class: 'form-control' }, include_blank: "Select..."
But it throwing problem the email not found. My question was how do I pass the member on that #wallet = ... variable to get email member showing on my form? Is there better way to get that?
You can use label_method and value_method params (docs):
= f.input :wallet_id, collection: #wallet, value_method: :id, label_method: :email, input_html: { class: 'form-control' }, include_blank: "Select..."
Also if you need only id and email, there is no need to fetch all the other data from the DB, you can use pluck:
# controller
def new
#top_up = TopUp.new
#wallet = Wallet.joins(:investor_account).where(investor_accounts: {approval_status: 'approved'}).pluck(:id, :email)
end
# form
= f.input :wallet_id, collection: #wallet, value_method: :first, label_method: :last, input_html: { class: 'form-control' }, include_blank: "Select..."
I'm trying to implement a multiple select and save with simple form and nested forms.
My view shows all the branches with name and description in a card format (Bootstrap). Next to te name I would like to have a checkbox. I would like to save only the selected ones
= simple_form_for(#my_branch, html: { class: 'form-horizontal' }) do |f|
= f.error_notification
= f.input :user_id, input_html: { value: current_user.id}, as: :hidden
- #branches.each do |branch|
= f.simple_fields_for :my_branch_items do |b|
.col-md-4
.card{:style => "width: 18rem;"}
.card-header
= branch.name
= b.input :branch_id, input_html: { value: branch.id }, as: :radio_buttons
%ul.list-group.list-group-flush
= branch.description
.form-group
= f.submit 'Save', class: 'btn btn-primary'
Here are the associations
class Branch < ApplicationRecord
has_many :my_branch_items
end
class MyBranch < ApplicationRecord
belongs_to :user
has_many :my_branch_items
accepts_nested_attributes_for :my_branch_items, allow_destroy: true, reject_if: proc { |att| att['name'].blank? }
end
class MyBranchItem < ApplicationRecord
belongs_to :my_branch
belongs_to :branch
end
And the Controller
class MyBranchesController < BaseController
before_action :set_my_branch, only: [:show]
def new
#branches = Branch.all
#my_branch = MyBranch.new
end
def create
#my_branch = Quiz.new(my_branch_params)
if #my_branch.save
redirect_to my_branch_path(#my_branch), notice: 'Thanks for taking the Branch Quiz'
else
render :new
end
end
def show
end
private
def set_my_branch
#my_branch = MyBranch.find(params[:id])
end
def my_branch_params
params.require(:my_branch).permit(:name, :note, :user_id, my_branch_items_attributes: MyBranchItem.attribute_names.map(&:to_sym).push(:_destroy))
end
end
Fixed!
Form
= simple_form_for(#my_branch, html: { class: 'form-horizontal' }) do |f|
= f.error_notification
= f.input :user_id, input_html: { value: current_user.id}, as: :hidden
- #branches.each do |branch|
= f.simple_fields_for :my_branch_items do |b|
.col-md-4
.card{:style => "width: 18rem;"}
.card-header
= branch.name
= b.input_field :branch_id, checked_value: branch.id, as: :boolean, boolean_style: :inline, include_hidden: false
%ul.list-group.list-group-flush
= branch.description
.form-group
= f.submit 'Save', class: 'btn btn-primary'
Model was using a validation for blanks field that did not exist in the table.
class MyBranch < ApplicationRecord
belongs_to :user
has_many :my_branch_items, dependent: :destroy
accepts_nested_attributes_for :my_branch_items, allow_destroy: true, reject_if: proc { |att| att['branch_id'].blank? }
end
This is driving me crazy. Probably I'm overlooking something obvious, but I've read all other posts on the same topic and can't solve the problem. Thanks in advance for any help.
I have a model Klasse and a Model Klp. An instance of klasse has many klps. I would like to create klp in the same form as klasse. I've set this up using form_with and fields_for, and in klasses_controller.rb I have 3.times { #klasse.klps.build } But this iteration does not seem to work. The fields_for block is only shown once instead of 3 times.
The form in new.html was created following the rails "getting started" guide, and the fields_for block following the "form helpers" guide.
Here is my code:
app\models\klasse.rb:
class Klasse < ApplicationRecord
has_many :klps
has_many :people, :through => :klps
accepts_nested_attributes_for :klps, allow_destroy: true, :reject_if => :all_blank
validates :name, presence: true,
length: { minimum: 1 },
uniqueness: true
validates :klp_std_soll, presence: true,
length: { minimum: 1 }
def klp_std_ist
self.klps.sum("std")
end
end
app\models\person.rb
class Person < ApplicationRecord
has_many :klps
has_many :klasses, :through => :klps
validates :name, presence: true,
length: { minimum: 1 },
uniqueness: true
validates :vorname, presence: true,
length: { minimum: 1 }
def last_and_first_name
"#{name}, #{vorname}"
end
end
app\models\klp.rb
class Klp < ApplicationRecord
belongs_to :klasse
belongs_to :person
end
app\controllers\klasses_controller.rb
class KlassesController < ApplicationController
def new
#klasse = Klasse.new
3.times { #klasse.klps.build }
end
private
def klasse_params
params.require(:klasse).permit(:name, :klassentyp_id, :klp_std_soll, klps_attributes: [:id, :person_id, :std, :_destroy])
end
end
app\controllers\klps_controller.rb
class KlpsController < ApplicationController
def new
#klp = Klp.new
end
private
def klp_params
params.require(:klp).permit(:klasse_id, :person_id, :std)
end
end
app\views\klasses\new.html.erb
<%= form_with scope: :klasse, url: klasses_path, local: true do |form| %>
<ul>
<%= form.fields_for :klps do |klps_form| %>
<p>
<%= klps_form.label :person %>
<%= klps_form.collection_select :person_id, Person.all, :id, :last_and_first_name %>
<%= klps_form.label :std %>
<%= klps_form.text_field :std %>
</p>
<% end %>
</ul>
<p>
<%= form.submit %>
</p>
<% end %>
Try setting your collection of klps to an instance variable that you can then pass as an explicit "record object" argument to the fields_for method:
apps/controllers/klasses_controller.rb
def new
#klasse = Klasse.new
#klps = 3.times { #klasse.klps.build }
end
app\views\klasses\new.html.erb
<%= form.fields_for :klps, #klps do |klps_form| %>
... your form...
<% end %>
See also: How do I pass an array to fields_for in Rails?
Reference: https://apidock.com/rails/ActionView/Helpers/FormHelper/fields_for
I am giving an example see
customer.rb
class Customer < ApplicationRecord
has_many :addresses, dependent: :destroy
accepts_nested_attributes_for :addresses, allow_destroy: true
end
address.rb
class Address < ApplicationRecord
belongs_to :customer
end
customers_controller.rb
class CustomersController < ApplicationController
def new
#customer = Customer.new
3.times {
#customer.addresses.build //You can use #customer.addresses.new
}
end
end
def customer_params
params.require(:customer).permit(:id, :name, addresses_attributes:[:id,:country, :state])
end
new.html.haml
= simple_form_for #customer do |f|
= f.input :name, label: false, wrapper: false, input_html: { class: "gui-input"}, placeholder: "Customer Name"
= f.fields_for :addresses do |a|
= render 'addresses/fields', a: a
view/addresses/_fields.html.haml
= a.input :country, as: :select, label:false, :wrapper => false, prompt: "Select country"
= a.input :city, label:false, :wrapper => false, input_html: {class: "gui-input", maxlength: 80, minlength: 2}, placeholder: "City"
for more info
https://www.pluralsight.com/guides/ruby-ruby-on-rails/ruby-on-rails-nested-attributes
use
<%= form.fields_for :klps_attributes do |klps_form| %>
I'm really new to the ruby on rails framework and i try to build a recipe website to learn more about this framework.
I have a entity Recipe.rb which has many RecipeIngredient. A RecipeIngredient is a entity in relation with a Ingredient, a Unit and have a quantity.
Here are the files :
Recipe.rb
class Recipe < ActiveRecord::Base
extend Enumerize, FriendlyId
belongs_to :user
has_many :directions
has_many :recipe_ingredients
has_many :ingredients, through: :recipe_ingredients
accepts_nested_attributes_for :directions, reject_if: :all_blank, allow_destroy: true
accepts_nested_attributes_for :recipe_ingredients, reject_if: :all_blank, allow_destroy: true
friendly_id :title, use: :slugged
has_attached_file :image, styles: { thumb: "270x200#", main: "570x420#" }, default_url: "/images/:style/missing.png"
validates_attachment_content_type :image, content_type: /\Aimage\/.*\Z/
enumerize :difficulty, in: [:easy, :medium, :hard], default: :easy
end
RecipeIngredient.rb
class RecipeIngredient < ActiveRecord::Base
belongs_to :ingredient
belongs_to :unit
belongs_to :recipe
end
Ingredient.rb
class Ingredient < ActiveRecord::Base
belongs_to :recipe_ingredient
end
Unit.rb
class Unit < ActiveRecord::Base
belongs_to :recipe_ingredient
end
As you can see, I use the gem Cocoon to handle nested fields. So, when the user wants to create a recipe, he has to provide the ingredients with the recipe.
My _form looks like this : (I have removed all html tag (the indentation is not good here)
= simple_form_for #recipe, html: {multipart: true} do |f|
- if #recipe.errors.any?
.alert.alert-danger
- #recipe.errors.full_messages.each do |msg|
li= msg
= f.input_field :title, label: false, placeholder: 'Nom de la recette', required: true
= f.input_field :prepareTime, label: false, placeholder: 'Temps de préparation (en mn)', required: true
= f.input_field :cookingTime, label: false, placeholder: 'Temps de cuisson (en mn)', required: true
= f.input_field :difficulty, label: false, required: true, placeholder: 'Difficulté'
= f.input_field :serves, label: false, placeholder: 'Nombre de personnes', required: true
= f.input_field :description, label: false, placeholder: 'Description', required: true
= f.simple_fields_for :recipe_ingredients do |recipe_ingredient|
= render 'recipe_ingredient_field', f: task
= link_to_add_association 'Ajouter un ingrédient', f, :recipe_ingredients, class: 'add'
= f.simple_fields_for :directions do |direction|
= render 'direction_field', f: task
= link_to_add_association 'Ajouter une étape', f, :directions, class: 'add'
= f.input_field :image, as: :file, label: false
= f.button :submit, value: 'Ajouter la recette', class: 'button'
_recipe_ingredient_fields.html.slim
.f-row.ingredient
.nested_fields
.large
= f.input_field :ingredient, label: false, placeholder: "Nom de l'ingrédient"
.small
= f.input_field :quantity, label: false, placeholder: 'Quantité'
.third.mb
= f.association :unit, label: false
= link_to_remove_association '-', f, class: 'remove'
And last but not least, my recipe controller
class RecipesController < ApplicationController
before_action :authenticate_user!, only: [:new, :create]
before_action :find_recipe, only: [:show]
def index
#recipes = Recipe.all
end
def new
#recipe = current_user.recipes.build
end
def create
#recipe = current_user.recipes.build(recipe_params)
if #recipe.save
redirect_to #recipe, notice: 'La recette a été ajoutée'
else
render 'new'
end
end
def show
end
private
def recipe_params
params.require(:recipe).permit(:title, :prepareTime, :cookingTime, :difficulty, :serves, :description, :image, directions_attributes: [:id, :step, :done, :_destroy], recipe_ingredients_attributes: [:id, :ingredient, :unit, :quantity, :done, :_destroy])
end
def find_recipe
#recipe = Recipe.friendly.find(params[:id])
end
end
So now, when I tr to create a new recipe, I have an error "Ingredient excepted, String given".
I know it's because my new ingredient is not created in my create method but I really don't know how to do this. If someone can help me :)
If you want to save the ingredient in recipe_ingredients as a string then I think your issue is caused by a name clash between your model and the input field so rename your input field to something else and update your strong parameters.
However...If instead you intend to build a new ingredient record when you add a recipe_ingredient record then you need to have a fields_for clause in order to build the ingredient record and have it associated to your recipe_ingredient field. Also make sure than your recipe_ingredient includes accepts_nested_attributes_for :ingredients
New to rails and I'm trying to reject an empty nested file_field but it just keeps going though. Here is the setup.
EDIT: It does actually save the image properly if one is included, just doesn't reject the empty ones.
Fihish Model
class Finish < ActiveRecord::Base
default_scope order('finishes.id ASC')
attr_accessible :name,
:title,
##belongs_to##
:sku_id,
##has_many##
:image_attributes
belongs_to :skus
has_one :image, as: :imageable, :dependent => :destroy
accepts_nested_attributes_for :image, :reject_if => lambda { |a| a[:asset].blank? }, :allow_destroy => true
validates_presence_of :title
before_save :create_name
private
def create_name
self.name = title.parameterize
end
end
Finishes Controller
def new
#finish = Finish.new
#finish.build_image
respond_to do |format|
format.html # new.html.erb
# format.json { render json: #finish }
end
end
Image Model
class Image < ActiveRecord::Base
attr_accessible :content, #remove if no longer necessary
:asset
belongs_to :imageable, polymorphic: true
mount_uploader :asset, ImageUploader
end
Form
= form_for #finish, :html => { :multipart => true } do |f|
- if #finish.errors.any?
#error_explanation
%h1= "#{pluralize(#finish.errors.count, "error")} prohibited this finish from being saved:"
%ul
- #finish.errors.full_messages.each do |msg|
%li= msg
%fieldset{id: "finishes"}
.field
= f.label :title
= f.text_field :title
#finish-image.images
= f.fields_for :image do |image_builder|
= render 'images/image_fields', f: image_builder
.actions
= f.submit 'Save'
image_fields partial
.field
= f.label :asset, "Image"
= f.file_field :asset
- if f.object.asset
.image-box
= image_tag f.object.asset_url(:thumb).to_s
.remove-fields
= link_to_remove_fields f