Rails 7 stimulus Hide dropdown if empty - ruby-on-rails

I have a 3 category/subcategory/subsubcategory dependant dropdowns
everything works fine but I have a small issue. the second dropdown (subcategories) does not always have subsubcategories so I want to hide the last dropdown and only show it when there is a subsubcategory so it only shows based on the user selection of the subcategory
I would appreciate if someone can help.
my categories table
create_table :categories do |t|
t.string "name", limit: 255
t.text "description", limit: 65535
t.integer "parent_id", limit: 4
t.boolean "important", default: false
t.integer "position", limit: 4, default: 0
t.timestamps
end
in modules category.rb
class Category < ApplicationRecord
validates :name, presence: true
has_many :jobs
belongs_to :parent, foreign_key: :parent_id, class_name: 'Category' , :optional => true
has_many :subcategories, foreign_key: :parent_id, class_name: 'Category'
has_many :subsubcategories, foreign_key: :parent_id, class_name: 'Category'
end
in modules job.rb
class Job < ApplicationRecord
validates :title,:category_id, :description, presence: true
validates :category, :presence => true
belongs_to :user
belongs_to :category , -> { order("name") }
belongs_to :subcategories, class_name: "Category"
belongs_to :subsubcategories, class_name: "Category"
end
dropdown_controller.js
import { Controller } from "#hotwired/stimulus";
// connect to data-controller="dropdown"
export default class extends Controller {
submit () {
this.element.requestSubmit();
}
}
in my jobs_controller.rb
class JobsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
before_action :set_categories
def index
#jobs = Job.all
end
def new
#category = Category.find_by(id: params[:category])
#categories = Category.where(parent_id: nil)
#subcategories = #category.subcategories if #category
#subsubcategories = Category.where(:parent_id => params[:subcategories])
#job = Job.new
end
def create
#job = Job.new(job_params.merge({ user: current_user }))
if #job.save
format.html { redirect_to root_path, notice: "Job was successfully created." }
else
render :new, status: :unprocessable_entity
end
end
private
def job_params
params.require(:job).permit(:title, :description,:category_id).merge(user: current_user)
end
def set_categories
#category = Category.find_by(id: params[:category].presence)
#subcategory = Category.find_by(id: params[:subcategories])
#subsubcategory = Category.find_by(id: params[:subsubcategories])
end
end
views/jobs/new.html.erb
<%= turbo_frame_tag "form" do %>
<%= form_tag new_job_path, method: :get, data: { controller: "dropdown", action: "change->dropdown#submit" } do %>
<%= select_tag :category, options_from_collection_for_select(#categories, "id", "name", #category&.id ), prompt: "Select a category" %>
<%= select_tag :subcategories, options_from_collection_for_select(#subcategories || [], "id" , "name", #subcategory&.id), prompt: "Select a subcategories category" %>
<%= select_tag :subsubcategories, options_from_collection_for_select(#subsubcategories || [], "id" , "name", #subsubcategory&.id), prompt: "Select a subsubcategories category" %>
<% end %>
<% end %>

Why don't you hide it definitely. Like changing views/jobs/new.html.erb to something like this :
<%= turbo_frame_tag "form" do %>
<%= form_tag new_job_path, method: :get, data: { controller: "dropdown", action: "change->dropdown#submit" } do %>
<%= select_tag :category, options_from_collection_for_select(#categories, "id", "name", #category&.id ), prompt: "Select a category" %>
<%= select_tag :subcategories, options_from_collection_for_select(#subcategories || [], "id" , "name", #subcategory&.id), prompt: "Select a subcategories category" %>
<% if #subsubcategories.any? %>
<%= select_tag :subsubcategories, options_from_collection_for_select(#subsubcategories || [], "id" , "name", #subsubcategory&.id), prompt: "Select a subsubcategories category" %>
<% end %>
<% end %>
<% end %>

I actually got it working exactly as I want without Javascript:
<% if !#subsubcategories.blank? %>
<%= select_tag :subsubcategories , options_from_collection_for_select(#subsubcategories || [], "id" , "name", #subsubcategory&.id), prompt: "Select a subsubcategories category" %>
<% end %>

Related

Creating three Dependent Drop-Downs With Ruby on Rails 7 and turbo/stimulus

I wanted to create a three dropdown selects category>subcategory>susubcategory
I have followed this method here https://www.youtube.com/watch?v=ap551f2a_d0 with some changes to suit my needs. everything seemed fine and each dropdown gets populated based on the selection of the dropdown before but when I select any value in the dropdowns it does not keep it selected it just goes back to the default -Please select-! I am not sure where I went wrong.
note: I only have one module (categories for the categories and using the parant_id for the subcategories)
I would really appreciate it if someone can point me in the right direction. Thank you
my categories table
create_table :categories do |t|
t.string "name", limit: 255
t.text "description", limit: 65535
t.references "parent_id", limit: 4
t.boolean "important", default: false
t.integer "position", limit: 4, default: 0
t.timestamps
end
in modules category.rb
class Category < ApplicationRecord
validates :name, presence: true, uniqueness: true
has_many :jobs
belongs_to :parent_category, foreign_key: :parent_id, class_name: 'Category'
has_many :subcategories, foreign_key: :parent_id, class_name: 'Category'
has_many :subsubcategories, foreign_key: :parent_id, class_name: 'Category'
end
in modules job.rb
class Job < ApplicationRecord
validates :title,:category_id, :description, presence: true
validates :category, :presence => true
belongs_to :user
belongs_to :category , -> { order("name") }
end
dropdown_controller.js
import { Controller } from "#hotwired/stimulus";
// connect to data-controller="dropdown"
export default class extends Controller {
submit () {
this.element.requestSubmit();
}
}
in my jobs_controller.rb
before_action :authenticate_user!, except: [:index, :show]
before_action :set_categories
def index
#jobs = Job.all
end
def new
#categories = Category.where(parent_id: nil)
#subcategory = #subcategory&.category || []
#subcategories = #subcategories&.subcategories || []
#subsubcategories = #subsubcategories&.subsubcategories || []
#job = Job.new
end
def create
#job = Job.new(job_params.merge({ user: current_user }))
if #job.save
format.html { redirect_to root_path, notice: "Job was successfully created." }
else
render :new, status: :unprocessable_entity
end
end
private
def job_params
params.require(:job).permit(:title, :description,:category_id).merge(user: current_user)
end
def set_categories
#category = Category.find_by(id: params[:category].presence)
#categories = Category.find_by(id: params[:category].presence)
#subcategories = Category.find_by(id: params[:category].presence)
#subsubcategories = Category.find_by(id: params[:subcategories].presence)
end
end
views/jobs/new.html.erb
<%= turbo_frame_tag "form" do %>
<%= form_tag new_job_path, method: :get, data: { controller: "dropdown", action: "change->dropdown#submit" } do %>
<%= select_tag :category, options_from_collection_for_select(#categories, "id", "name", #category&.id ), prompt: "Select a category" %>
<%= select_tag :subcategories, options_from_collection_for_select(#subcategories, "id" , "name", #category&.id), prompt: "Select a subcategories category" %>
<%= select_tag :subsubcategories, options_from_collection_for_select(#subsubcategories, "id" , "name", #category&.id), prompt: "Select a subsubcategories category" %>
<% end %>
<% end %>
I think that you miss update select_tag after request successfully.
you create a new file views/jobs/new.turbo_stream.erb
Ex: The select_tag has category_id
<%= turbo_stream.update "category_id" do %>
<%= select_tag :category, options_from_collection_for_select(#categories, "id", "name", #category&.id ), prompt: "Select a category" %>
<% end %>
Make sure select_tag selected value with #category&.id (ex: #category&.id = 1)
I done it but i have 2 select_tags.
https://drive.google.com/file/d/1MBY3T0_O2TAE6zFUywDsEoIOjnxr1hMy/view?usp=share_link
P/S: Contact me if you wanna know anymore.
This was the solution to the problem:
my categories table
create_table :categories do |t|
t.string "name", limit: 255
t.text "description", limit: 65535
t.integer "parent_id", limit: 4
t.boolean "important", default: false
t.integer "position", limit: 4, default: 0
t.timestamps
end
in modules category.rb
class Category < ApplicationRecord
validates :name, presence: true
has_many :jobs
belongs_to :parent, foreign_key: :parent_id, class_name: 'Category' , :optional => true
has_many :subcategories, foreign_key: :parent_id, class_name: 'Category'
has_many :subsubcategories, foreign_key: :parent_id, class_name: 'Category'
end
in modules job.rb
class Job < ApplicationRecord
validates :title,:category_id, :description, presence: true
validates :category, :presence => true
belongs_to :user
belongs_to :category , -> { order("name") }
belongs_to :subcategories, class_name: "Category"
belongs_to :subsubcategories, class_name: "Category"
end
dropdown_controller.js
import { Controller } from "#hotwired/stimulus";
// connect to data-controller="dropdown"
export default class extends Controller {
submit () {
this.element.requestSubmit();
}
}
in my jobs_controller.rb
class JobsController < ApplicationController
before_action :authenticate_user!, except: [:index, :show]
before_action :set_categories
def index
#jobs = Job.all
end
def new
#category = Category.find_by(id: params[:category])
#categories = Category.where(parent_id: nil)
#subcategories = #category.subcategories if #category
#subsubcategories = Category.where(:parent_id => params[:subcategories])
#job = Job.new
end
def create
#job = Job.new(job_params.merge({ user: current_user }))
if #job.save
format.html { redirect_to root_path, notice: "Job was successfully created." }
else
render :new, status: :unprocessable_entity
end
end
private
def job_params
params.require(:job).permit(:title, :description,:category_id).merge(user: current_user)
end
def set_categories
#category = Category.find_by(id: params[:category].presence)
#subcategory = Category.find_by(id: params[:subcategories])
#subsubcategory = Category.find_by(id: params[:subsubcategories])
end
end
views/jobs/new.html.erb
<%= turbo_frame_tag "form" do %>
<%= form_tag new_job_path, method: :get, data: { controller: "dropdown", action: "change->dropdown#submit" } do %>
<%= select_tag :category, options_from_collection_for_select(#categories, "id", "name", #category&.id ), prompt: "Select a category" %>
<%= select_tag :subcategories, options_from_collection_for_select(#subcategories || [], "id" , "name", #subcategory&.id), prompt: "Select a subcategories category" %>
<%= select_tag :subsubcategories, options_from_collection_for_select(#subsubcategories || [], "id" , "name", #subsubcategory&.id), prompt: "Select a subsubcategories category" %>
<% end %>
<% end %>

Undefined method error in rails?

I have a model where students can enter information about themselves and others can view them. Since there are a lot of profiles, I have added a search section too. If they want only CS majors, they can select from a drop down. I had things like major, college... today I added "level", which is level of education and I get an error (Error shown at the end). I know this is long, but any help would be really really useful. Thanks!
(Userinfo is the student model)
My controller looks like this:
class UserinfosController < ApplicationController
before_action :find_userinfo, only: [:show, :edit, :update, :destroy, :log_impression]
def index
#userinfors = Userinfo.search(params[:search])
#search = Search.new
#college = Userinfo.uniq.pluck(:college)
#major = Userinfo.uniq.pluck(:major)
#level = Userinfo.uniq.pluck(:level)
end
def show
end
def new
#userinformation = current_user.build_userinfo
end
def create
#userinformation = current_user.build_userinfo(userinfo_params)
if #userinformation.save
redirect_to userinfo_path(#userinformation)
else
render 'new'
end
end
def edit
end
def update
if #userinformation.update(userinfo_params)
redirect_to userinfo_path(#userinformation)
else
render 'edit'
end
end
def destroy
#userinformation.destroy
redirect_to root_path
end
private
def userinfo_params
params.require(:userinfo).permit(:name, :email, :college, :gpa, :major, :token, :skills, :level)
end
def find_userinfo
#userinformation = Userinfo.friendly.find(params[:id])
end
end
My information fill out form (please note how the way of input is different in major and level of edu, maybe thats the problem?):
<%= simple_form_for #userinformation, :html => { :multipart => true } do |f| %>
<%= f.input :name, required: true, label: 'Full name' %>
<%= f.input :college, required: true, label: 'Name of college' %>
<%= f.input :gpa, required: true, label: 'Enter GPA' %>
<%= f.input :email, required: true, label: 'Enter email address' %>
<%= f.input :major, required: true, label: 'Major' %>
<%= f.input :level, collection: ["College undergrad", "College grad"], required: true, label: 'Level of education' %>
<%= f.input :skills, required: true, label: 'Skills' %>
<%= f.button :submit, 'Create account' %>
<% end %>
Search controller:
class SearchesController < ApplicationController
def new
#search = Search.new
#college = Userinfo.uniq.pluck(:college)
#major = Userinfo.uniq.pluck(:major)
#level = Userinfo.uniq.pluck(:level)
end
def create
#search = Search.create(search_params)
redirect_to #search
end
def show
#search = Search.find(params[:id])
end
private
def search_params
params.require(:search).permit(:keyword, :college, :min_gpa, :major, :token, :level )
end
end
Search model:
class Search < ActiveRecord::Base
def search_userinfos
userinfos = Userinfo.all
userinfos = userinfos.where(["name LIKE ?","%#{keyword}%"]) if keyword.present?
userinfos = userinfos.where(["college LIKE ?", college]) if college.present?
userinfos = userinfos.where(["cast(gpa as numeric) >= ?", min_gpa]) if min_gpa.present?
userinfos = userinfos.where(["major LIKE ?", major]) if major.present?
userinfos = userinfos.where(["level LIKE ?", level]) if level.present?
userinfos = userinfos.order("gpa")
return userinfos
end
end
This is where the search section is displayed (index view):
<%= simple_form_for #search do |s| %>
<%= s.input :keyword, label: 'Name' %>
<label>College</label>
<%= s.select :college, options_for_select(#college), :include_blank => true %>
<%= s.input :min_gpa, label: "Minimum GPA" %>
<label>Major</label>
<%= s.select :major, options_for_select(#major), :include_blank => true %>
<label>Job type</label>
<%= s.select :level, options_for_select(#level), :include_blank => true %>
<%= s.button :submit, "Search" %>
<% end %>
The error I get when I try to visit the index view:
NoMethodError in Userinfos#index
Showing app-path/index.html.erb where line #25 raised:
undefined method `level' for #<Search:0x000000139d6c38>
Line 25 is:
<%= s.select :level, options_for_select(#level), :include_blank => true %>
Everything works if I remove that line. When I show the education level in the user profile page, it works perfectly.
Schema for searches:
create_table "searches", force: :cascade do |t|
t.string "keyword"
t.string "college"
t.decimal "min_gpa"
t.string "major"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
As you commented, you do not have a level attribute in your Search model.
rails g migration add_level_to_searches level:string
And of course
rails db:migrate
Happy to help!

Rails nested routes for create method smart_listing

I have two models: apartment and room. Apartment has_many rooms, and rooms belongs_to apartment. I use smart_listing gem as ajax form. I show my table in edit_apartment_path
= render 'rooms/index' # index is partial
And I add this to my apartment_controller
def edit
#rooms = smart_listing_create :rooms,
Room.where(apartment_id: params[:apartment_id]),
partial: "rooms/list"
end
Now I must set paths for my form
= simple_form_for object, url: object.new_record? ? apartment_rooms_path : apartment_room_path(id: object),
remote: true, html: {class: "form-horizontal"} do |f|
= f.input :title
= f.button :submit
I can edit my created room, but I can't create new room in apartment. My error:
ActionController::UrlGenerationError - No route matches {:action=>"edit", :apartment_id=>nil, :controller=>"rooms", :id=>#<Room id: 83, title: "dawawd">, created_at: "2016-02-11 10:36:30", updated_at: "2016-02-11 10:36:30", apartment_id: 4>} missing required keys: [:apartment_id]:
My routes
resources :apartments do
resources :rooms
end
Propably smart_listing not support nested routes. Anyone have idea? :)
here's simple example of nested routes with smart_listing. I think that should cover the subject.
I used Rails 4.2, ruby 2.2.0, smart_listing 1.1.2
config/routes.rb
resources :users do
resources :bios
end
root 'users#index'
models/user.rb
class User < ActiveRecord::Base
has_one :bio
accepts_nested_attributes_for :bio, allow_destroy: true
scope :like, ->(args) { where("email like '%#{args}%' OR name like '%#{args}%' OR surname like '%#{args}%'")}
end
models/bio.rb
class Bio < ActiveRecord::Base
belongs_to :user
end
controllers/users_controller.rb
class UsersController < ApplicationController
include SmartListing::Helper::ControllerExtensions
helper SmartListing::Helper
before_action :set_user, only: [:update, :destroy]
def index
users_scope = User.all.includes(:bio)
users_scope = users_scope.like(params[:filter]) if params[:filter]
# #users = smart_listing_create :users, users_scope, partial: "users/list", page_sizes: [5, 7, 13, 26]
#users = smart_listing_create(:users, users_scope, partial: 'users/list',
sort_attributes: [
[:name, 'name'],
[:surname, 'surname'],
[:email, 'email'],
[:city, 'bio.city'],
[:birthday, 'bio.birthday']
],
default_sort: { start_at: 'desc' }
)
end
def new
#user = User.new
#user.build_bio
end
def create
#user = User.new(user_params)
#user.save
end
def edit
#user = User.includes(:bio).find(params[:id])
#user.bio.build if #user.bio.nil?
end
def update
#user.update(user_params)
end
def delete
end
def destroy
#user.destroy
end
private
def set_user
#user = User.find(params[:id])
end
def user_params
params.require(:user).permit(:name, :surname, :email, :bio_attributes => [:birthday, :city])
end
end
views/users/index.html.haml
= smart_listing_controls_for(:users, {class: "form-inline text-right"}) do
.form-group.filter.input-append
= text_field_tag :filter, '', class: "search form-control",
placeholder: "Search...", autocomplete: :off
= smart_listing_render :users
views/users/_list.html.haml
- unless smart_listing.empty?
%table.table.table-striped
%thead
%th= smart_listing.sortable "Name", :name
%th= smart_listing.sortable "Surname", :surname
%th= smart_listing.sortable "Email", :email
%th= smart_listing.sortable "City", :city
%th= smart_listing.sortable "Birthday", :birthday
%tbody
- smart_listing.collection.each do |o|
%tr.editable{data: {id: o.id}}
= smart_listing.render object: o, partial: "users/user", locals: {object: o}
= smart_listing.item_new colspan: 6, link: new_user_path
= smart_listing.paginate
= smart_listing.pagination_per_page_links
- else
%p.warning No users
views/users/_user.html.haml
%td= object.name
%td= object.surname
%td= object.email
%td= object.bio.city
%td= object.bio.birthday
%td.actions= smart_listing_item_actions [ {name: :edit, url: edit_user_path(object)}, {name: :destroy, url: user_path(object), confirmation: "Are you sure you want to delete this?"}]
views/users/_form.html.haml
%td{colspan: 6}
= form_for object, url: object.new_record? ? users_path : user_path(object),
remote: true, html: {class: "form-horizontal"} do |f|
%p
Name:
= f.text_field :name
%p
Surname:
= f.text_field :surname
%p
Email:
= f.text_field :email
= f.fields_for :bio do |ff|
%p
Birthday
= ff.date_field :birthday
%p
City
= ff.text_field :city
= f.submit "Save", class: "btn btn-primary"
%button.btn.btn-link.cancel Cancel
views/users/create.js.erb
<%= smart_listing_item :users, :create, #user, #user.persisted? ? "users/user" : "users/form" %>
views/users/edit.js.erb
<%= smart_listing_item :users, :edit, #user, "users/form" %>
views/users/destroy.js.erb
<%= smart_listing_item :users, :destroy, #user %>
views/users/index.js.erb
<%= smart_listing_update(:users) %>
views/users/new.js.erb
<%= smart_listing_item :users, :new, #user, "users/form" %>
views/users/update.js.erb
<%= smart_listing_item :users, :update, #user, #user.valid? ? "users/user" : "users/form" %>
you should receive to form #apartment also, and in this case if your #room.persisted? form recive request to edit, else to create:
= simple_form_for [#apartment, #room], remote: true, html: {class: "form-horizontal"} do |f|
= f.input :title
= f.button :submit

Nested forms with join table attributes

I have a new Match form:
EDIT:
= form_for(#match) do |f|
= f.label :match_date
= f.date_select :match_date, :order => [:day, :month, :year]
= f.label :player_ids, 'Select players'
= f.collection_select :player_ids, #players, :id, :lastname, {}, { multiple: true }
= f.fields_for :match_edits do |ff|
= ff.label :result
= ff.number_field :result, in: 0..10
%div
= f.button :submit
When I choose two players I want set for each one match result like this:
id: 1, match_id: 1, player_id: 1, result: 4
id: 2, match_id: 1, player_id: 2, result: 10
I'm new in rails and I don't know how to fix that
MatchController
class MatchesController < ApplicationController
respond_to :html
def index
#matches = Match.all
end
def show
#match = Match.find(params[:id])
#results = #match.match_edits
end
def new
#match = Match.new
#players = Player.all
2.times {#match.match_edits.build}
end
def create
#match = Match.new(match_params)
respond_to do |format|
if #match.save
format.html { redirect_to #match, notice: 'Match was successfully created.' }
format.json { render :show, status: :created, location: #match }
else
format.html { render :new }
format.json { render json: #match.errors, status: :unprocessable_entity }
end
end
end
private
def match_params
params[:match].permit :match_date, player_ids: [], :match_edits_attributes => [:id, :result]
end
end
MatchEdit model
class MatchEdit < ActiveRecord::Base
belongs_to :match
belongs_to :player
end
Match model
class Match < ActiveRecord::Base
has_many :match_edits
has_many :players, through: :match_edits
accepts_nested_attributes_for :match_edits, allow_destroy: true, reject_if: proc { |attrs| attrs['result'].blank? }
end
Schema.rb
ActiveRecord::Schema.define(version: 20150629144534) do
create_table "match_edits", force: :cascade do |t|
t.integer "match_id"
t.integer "player_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "result"
end
add_index "match_edits", ["match_id"], name: "index_match_edits_on_match_id"
add_index "match_edits", ["player_id"], name: "index_match_edits_on_player_id"
create_table "matches", force: :cascade do |t|
t.date "match_date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "players", force: :cascade do |t|
t.string "firstname"
t.string "lastname"
t.string "picture"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
You need to update your form to remove redundant code i.e.:
= ff.number_field :result, in: 0..10
Your form will look like:
= form_for(#match) do |f|
= f.label :match_date
= f.date_select :match_date, :order => [:day, :month, :year]
= f.label :player_ids, 'Select players'
= f.collection_select :player_ids, #players, :id, :lastname, {}, { multiple: true }
= f.fields_for :match_edits do |ff|
= ff.label :result
= ff.number_field :result, in: 0..10
%div
= f.button :submit
Your controller's new method is responsible to to provide multiple fields for result:
class MatchsController << ApplicationContoller
...
def new
...
2.times { #match.match_edits.build }
...
end
...
end
Your model should allow to accept nested attributes as following:
class Match
...
has_many :match_edits
accepts_nested_attributes_for :match_edits, allow_destroy: true,
reject_if: proc { |attrs| attrs['result'].blank? }
...
end
class MatchEdit
...
belongs_to :match
...
end
I found solution. Form should look like this:
= form_for(#match) do |f|
= f.label :match_date
= f.date_select :match_date, :order => [:day, :month, :year]
= f.fields_for :match_edits do |ff|
= ff.label :player_id, 'Select player'
= ff.collection_select :player_id, #players, :id, :lastname, {}, { multiple: false }
= ff.label :result
= ff.number_field :result, in: 0..10
%div
= f.button :submit
and in matches_controller:
def match_params
params[:match].permit :match_date, :match_edits_attributes => [:id, :result, :player_id]
end

Save successful on nested attributes on has many through but doesn't add to database

I and order and item system with Rails 4 with a has many through association. When I select create order the webpage says that the order was created successfully however the link is not made in the OrderItems linking tables meaning that the items relating to that order do not appear on the show page or edit page for an order.
The order is also linked to an employee. The current employee ID is linked to that order. I have just not been able to figure out how to add each item to the database.
p.s. I am using a gem called nested_form to handle all of the jQuery on the front end of dynamically adding and removing new items on the _form.html.erb for Orders.
orders_controller.rb
class OrdersController < ApplicationController
before_action :logged_in_employee, only:[:new, :show, :create, :edit, :update, :index]
before_action :admin_employee, only:[:destroy]
before_action :set_order, only: [:show, :edit, :update, :destroy]
def new
#order = Order.new
#items = Item.all
end
def edit
#items = Item.all
end
def create
#order = current_employee.orders.build(order_params)
if #order.save
flash[:success] = "Order successfully created"
redirect_to #order
else
render 'new'
end
end
def update
if #order.update_attributes(order_params)
flash[:success] = "Order updated!"
redirect_to current_employee
else
render 'edit'
end
end
....
private
def set_order
#order = Order.find(params[:id])
end
def order_params
params.require(:order).permit(:table_number, :number_of_customers, :status, :comment, order_items_attributes: [:id, :order_id, :item_id, :_destroy])
end
end
order.rb
class Order < ActiveRecord::Base
belongs_to :employee
has_many :order_items
has_many :items, :through => :order_items
default_scope { order('status DESC') }
validates :employee_id, presence: true
validates :table_number, numericality: { only_integer: true, greater_than: 0, less_than_or_equal_to: 20 }, presence: true
validates :number_of_customers, numericality: { only_integer: true, greater_than: 0, less_than_or_equal_to: 50 }, presence: true
validates :status, inclusion: { in: %w(Waiting Ready Closed), message: "%{value} is not a status" }
accepts_nested_attributes_for :order_items, :reject_if => lambda { |a| a[:item_id].blank? }
end
item.rb
class Item < ActiveRecord::Base
belongs_to :menu
has_many :order_items
has_many :orders, :through => :order_items
before_save { name.capitalize! }
VALID_PRICE_REGEX = /\A\d+(?:\.\d{0,2})?\z/
validates :name, presence: true, length: { maximum: 100 }
validates :price, format: { with: VALID_PRICE_REGEX }, numericality: { greater_than: 0, less_than_or_equal_to: 100}, presence: true
validates :course, inclusion: { in: %w(Starter Main Dessert Drink), message: "%{value} is not a course" }
validates :menu_id, presence: true
end
order_item.rb
class OrderItem < ActiveRecord::Base
belongs_to :item
belongs_to :order
validates :order_id, presence: true
validates :item_id, presence: true
end
orders/_form.html.erb
<% provide(:title, "#{header(#order)} #{#order.new_record? ? "order" : #order.id}") %>
<%= link_to "<< Back", :back, data: { confirm: back_message } %>
<h1><%= header(#order) %> <%= #order.new_record? ? "order" : #order.id %></h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= nested_form_for #order do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="row">
<div class="col-xs-6">
<%= f.label :table_number %>
<%= f.number_field :table_number, class: 'form-control' %>
</div>
<div class="col-xs-6">
<%= f.label :number_of_customers %>
<%= f.number_field :number_of_customers, class: 'form-control' %>
</div>
</div>
<%= f.fields_for :order_items do |oi| %>
<%= oi.grouped_collection_select :item_id, Menu.where(active: true).order(:name), :items, :name, :id, :name, { include_blank: 'Select Item' }, class: 'items_dropdown' %>
<%= oi.hidden_field :item_id %>
<%= oi.hidden_field :order_id %>
<%= oi.link_to_remove "Remove item" %>
<% end %>
<p><%= f.link_to_add "Add an item", :order_items %></p>
<br>
<% if !#order.new_record? %>
<%= f.label "Status" %>
<%= f.select(:status, options_for_select([['Waiting', 'Waiting'], ['Ready', 'Ready'], ['Closed', 'Closed']], #order.status), class: 'form-control') %>
<% end %>
<%= f.label "Comments - how would you like your steak cooked? Or feedback" %>
<%= f.text_area :comment, size: "20x5", class: 'form-control' %>
<%= f.submit submit_label(#order), class: "btn btn-success col-md-6" %>
<% end %>
<% if !#order.new_record? && current_employee.try(:admin?) %>
<%= link_to "Cancel", :back, data: { confirm: back_message }, class: "btn btn-warning col-md-offset-1 col-md-2" %>
<%= link_to "Delete", #order, method: :delete, data: { confirm: "Are you sure? The employee will be deleted! "}, class: "btn btn-danger col-md-offset-1 col-md-2" %>
<% else %>
<%= link_to "Cancel", :back, data: { confirm: back_message }, class: "btn btn-warning col-md-offset-1 col-md-5" %>
<% end %>
</div>
</div>
Issue: Let's say your order has got many items and you want the items to be saved just when the order is created.
order = Order.new(order_params)
item = Item.new(name: 'item-name', product_info: 'etc etc')
if order.save
item.create
order.items << item
end
You need to follow similar approach in your case. just get the item_params properly and apply above rule.
Try below approach, hope that helps. :)
def create
#order = current_employee.orders.build(order_params)
if #order.save
item = params["order"]["order_items_attributes"]
#please debug above one and try to get your items from params.
order_item = Item.create(item)
#makes sure above item hold all item attributes then create them first
#order.items << item
redirect_to #order
end
end

Resources