Rails has_many :through, cocoon - ruby-on-rails

Player.rb
class Player < ApplicationRecord
belongs_to :user
has_many :player_games, dependent: :destroy, inverse_of: :player
has_many :games, :through => :player_games
validates :firstname, presence: true, length: { minimum: 3, maximum: 88 }
validates :lastname, presence: true, length: { minimum: 3, maximum: 88 }
validates :user_id, presence: true
accepts_nested_attributes_for :player_games, reject_if: :reject_posts, allow_destroy: true
def reject_posts(attributes)
attributes['game_id'].to_i == 0
attributes['score'].blank?
attributes['time'].blank?
end
def initialized_player_games # this is the key method
[].tap do |o|
Game.all.each do |game|
if g = player_games.find { |g| g.game_id == game.id }
o << g.tap { |g| g.enable ||= true }
else
o << PlayerGame.new(game: game)
end
end
end
end
end
players_controller.rb
class PlayersController < ApplicationController
before_action :set_player, only: [:edit, :update, :show, :destroy]
before_action :require_user, except: [:index, :show]
before_action :require_same_user, only: [:edit, :update, :destroy]
before_filter :process_player_games_attrs, only: [:create, :update]
def process_player_games_attrs
params[:player][:player_games_attributes].values.each do |game_attr|
game_attr[:_destroy] = true if game_attr[:enable] != '1'
end
end
.......
private
# Use callbacks to share common setup or constraints between actions.
def set_player
#player = Player.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def player_params
params.require(:player).permit(:id, :firstname, :lastname, player_games_attributes: [:id, :game_id, :score, :time, :enable, :_destroy] )
end
def require_same_user
if current_user != #player.user and !current_user.admin?
flash[:danger] = "You can edit or delete only your own player"
redirect_to root_path
end
end
end
_form of 'PLAYER'
<%= form_for(#player, :html => {class: "az-form", role: "form"}) do |player_form| %>
<%= player_form.label :firstname, class: "az-form__label" %> <br/>
<%= player_form.text_field :firstname, class: "az-form__input", placeholder: "Firstname of player", autofocus: true %>
<%= player_form.label :lastname, class: "az-form__label" %> </br>
<%= player_form.text_field :lastname, class: "az-form__input", placeholder: "Lastname of player" %>
<%= player_form.fields_for :player_games, #player.initialized_player_games do |builder| %>
<% #game = builder.object.game %>
<%= render 'result_fields', f: builder %>
<div class="links">
<%= link_to_add_association 'add result', player_form, :player_games, :partial => 'players/result_fields' %>
</div>
<hr>
<% end %>
<div class="text-center">
<%= button_tag(type: "submit", class: "az-form__submit") do %>
<%= player_form.object.new_record? ? "Create player" : "Update player" %>
<% end %>
</div>
<% end %>
_result_fields.html.erb
<div class="nested-fields">
<%= f.hidden_field :game_id, :value => #game.id%>
<div class="row">
<div class="col-md-12">
<label class="az-form__label az-form__label--unable js-az-form__checkbox" data-check="<%= #game.id %>">
<%= f.check_box :enable %>
<%= #game.title %>
</label>
</div>
</div>
<div class="row">
<div class="col-md-6">
<%= f.label :score,
class: "az-form__label", :data => {:check => #game.id } %> </br>
<%= f.number_field :score, step: :any, :data => {:check => #game.id },
class: "az-form__input az-form__input--disabled",
placeholder: "Score for '#{#game.title}'", disabled: true %>
</div>
<div class="col-md-6">
<%= f.label :time,
class: "az-form__label", :data => {:check => #game.id } %> </br>
<%= f.number_field :time, step: :any, :data => {:check => #game.id },
class: "az-form__input az-form__input--disabled",
placeholder: "Time for '#{#game.title}'", disabled: true %>
</div>
</div>
<div class="row">
<div class="col-md-12">
<%= link_to_remove_association "remove result", f %>
</div>
</div>
</div>
Question:
When edit 'player' have access to only one result per game, can not change others, conflict with 'initialized_player_games' method, but if i remove this method from form work well, but cannot create another game if dont create in new action, how can i change this method properly?

If I understand correctly, you want to add an entry for all games (which works), but adding a new Result does not. The problem is the #game which is probable either undefined or set to the last game.
I also do not really like the approach (giving the fields_for a specific set). Instead I would adapt the approach slightly. Instead of using initialized_player_games I would use a method, to be called in the controller, e.g. add_default_player_games, something like
def add_default_player_games
Game.all.each do |game|
if g = player_games.find { |g| g.game_id == game.id }
g.enable ||= true
else
player_games.build(game: game)
end
end
end
effectively adding new instances to the collection, without saving them.
So in your controller you would write
#player = Player.new
#player.add_default_player_games
or in edit
#player = Player.find(params[:id])
#player.add_default_player_games
Your view would then just iterate over player_games
<%= player_form.fields_for :player_games do |builder| %>
<%= render 'result_fields', f: builder %>
<% end %>
And then, if you would use simple-form it would be very easy to select a game if not yet selected, and there is no need for the ugly #game.
So do something like in _result_fields (in haml because I am a lazy typist)
.nested-fields
- game_id = f.object.game_id
- if game_id.present?
= f.hidden_field :game_id
- else
= f.collection_select :game_id, Game.all, :id, :name
...
So in short: if there is a game_id, do not allow to change it (would be useful to show the title of the game or something), but if not use a dropdown-select to choose the game.
And the rest stays the same (only use game_id instead of #game.id).

Related

Data not persisted between steps in wizard

I have followed Nicolas Blanco's tutorial to make a "goal" wizard for my app.
There are two steps in the wizard. The first consisting of the form fields "name", "description" and "plan", the second has "deadline", which is a datetimepicker, "reporting frequency" and "days missed tolerance".
It seems to work when I click continue in the first step, but on clicking finish in the second step, the object #goal_wizard doesn't seem to include the parameters from the first step.
My goal.rb:
module Wizard
module Goal
STEPS = %w(step1 step2).freeze
class Base
include ActiveModel::Model
attr_accessor :goal
delegate *::Goal.attribute_names.map { |attr| [attr, "#{attr}="] }.flatten, to: :goal
def initialize(goal_attributes)
#goal = ::Goal.new(goal)
end
end
class Step1 < Base
validates :name, presence: true, length: { maximum: 50 }
validates :description, presence: true, length: { maximum: 300 }
validates :plan, presence: true, length: { maximum: 1000 }
end
class Step2 < Step1
validates :reporting_frequency, presence: true,
numericality: { greater_than_or_equal_to: 0 }
validates :days_missed_tolerance, presence: true,
numericality: { greater_than_or_equal_to: 0}
validates :deadline, presence: true
end
end
end
wizards_controller.rb:
class WizardsController < ApplicationController
before_action :load_goal_wizard, except: :validate_step
def validate_step
current_step = params[:current_step]
#goal_wizard = wizard_goal_for_step(current_step)
#goal_wizard.goal.attributes = goal_wizard_params
session[:goal_attributes] = #goal_wizard.goal.attributes
if #goal_wizard.valid?
next_step = wizard_goal_next_step(current_step)
create and return unless next_step
redirect_to action: next_step
else
render current_step
end
end
def create
# #user = current_user
# #goal = #user.goals.new(#goal_wizard.goal)
if #goal_wizard.goal.save
session[:goal_attributes] = nil
redirect_to root_path, notice: 'Goal succesfully created!'
else
redirect_to({ action: Wizard::Goal::STEPS.first }, alert: 'There were a problem creating the goal.')
end
end
private
def load_goal_wizard
#goal_wizard = wizard_goal_for_step(action_name)
end
def wizard_goal_next_step(step)
Wizard::Goal::STEPS[Wizard::Goal::STEPS.index(step) + 1]
end
def wizard_goal_for_step(step)
raise InvalidStep unless step.in?(Wizard::Goal::STEPS)
"Wizard::Goal::#{step.camelize}".constantize.new(session[:goal_attributes])
end
def goal_wizard_params
params.require(:goal_wizard).permit(:name, :description, :plan, :deadline, :reporting_frequency, :days_missed_tolerance)
end
class InvalidStep < StandardError; end
end
step1.html.erb:
<ol class="breadcrumb">
<li class='active'>Step 1</li>
<li>Step 2</li>
</ol>
<%= form_for #goal_wizard, as: :goal_wizard, url: validate_step_wizard_path do |f| %>
<%= render "error_messages" %>
<%= hidden_field_tag :current_step, 'step1' %>
<%= f.label :name %>
<%= f.text_field :name, class: "form_control" %>
<%= f.label :description %>
<%= f.text_field :description, class: "form_control" %>
<%= f.label :plan %>
<%= f.text_field :plan, class: "form_control" %>
<%= f.submit 'Continue', class: "btn btn-primary" %>
<% end %>
step2.html.erb:
<ol class="breadcrumb">
<li><%= link_to "Step 1", step1_wizard_path %></li>
<li class="active">Step 2</li>
</ol>
<%= form_for #goal_wizard, as: :goal_wizard, url: validate_step_wizard_path do |f| %>
<%= render "error_messages" %>
<%= hidden_field_tag :current_step, 'step2' %>
<%= f.label :deadline %>
<div class='input-group date' id='datetimepicker1'>
<%= f.text_field :deadline, class: "form-control" %>
<span class="input-group-addon">
<span class="glyphicon glyphicon-calendar"></span>
</span>
</div>
<%= f.label "How often do I want to report? (1 = every day)" %>
<%= f.number_field :reporting_frequency, class: "form_control" %>
<%= f.label "How many times can I miss my report?" %>
<%= f.number_field :days_missed_tolerance, class: "form_control" %>
<script type="text/javascript">
$(function () {
$('#datetimepicker1').datetimepicker({
minDate:new Date()
});
});
</script>
<%= f.submit "Finish", class: "btn btn-primary" %>
<% end %>
Over here you're passing the goal_attributes to initialize, but you're never using them.
def initialize(goal_attributes)
#goal = ::Goal.new(goal)
end
If you look at Nicolas Blanco's code he doesn't make that mistake.

collection_select not inserting value from other model

I have two models, Roaster and Roast
I want to have the user select the value of :roaster in the new roast form, from the Roaster model. I am using a collection_select which displays the list of roasters in the dropdown ok, but it doesn't insert the value into the table. From the console, it actually looks like it's trying to pass the roaster_id
"roast"=>{"roaster_id"=>"1", "name"=>"Rugby", "beans"=>"", "countries_attributes"=>{"0"=>{"country_name"=>"", "regions_attributes"=>{"0"=>{"region_name"=>""}}}, "1"=>{"country_name"=>"", "regions_attributes"=>{"0"=>{"region_name"=>""}}}, "2"=>{"country_name"=>"", "regions_attributes"=>{"0"=>{"region_name"=>""}}}}, "bestfor"=>"", "roast"=>"", "tastingnotes"=>""}, "commit"=>"Create Roast"}
My select:
<%= form.collection_select(:roaster_id, Roaster.all, :id, :roaster_name, :prompt => 'Select Roaster') %>
I've tried
<%= form.collection_select(:roaster_name, Roaster.all, :id, :roaster_name, :prompt => 'Select Roaster') %>
but this gives and undefined method error.
My roast_params
params.require(:roast).permit(:roaster, :roaster_id, :name, :bestfor, :beans, :roast, :tastingnotes, :notes, :slug, :avatar, countries_attributes: [:country_id, :country_name, regions_attributes: [:id, :region_name]])
Adding in :roaster_name doesn't solve either.
As requested full form:
<%= form_with(model: roast, local: true, multipart: true) do |form| %>
<% if roast.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger" role="alert">
<h2><%= pluralize(roast.errors.count, "error") %> prohibited this roast from being saved:</h2>
<ul>
<% roast.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
</div>
<% end %>
<form>
<div class="row">
<div class="col-6">
<div class="field form-group">
<%= form.label :roaster, class: 'control-label' %>
<%= form.collection_select(:roaster_id, Roaster.all, :id, :roaster_name, :prompt => 'Select Roaster') %>
</div>
</div>
<div class="col-6">
<div class="form-group">
<%= form.label :name, class: 'control-label' %>
<%= form.text_field :name, class: "form-control" %>
</div>
</div>
</div>
<div class="form-group">
<%= form.label :beans, "Blend", class: 'control-label' %><br />
<%= form.select :beans, [ 'Single Origin','Two Country Blend', 'Three Country Blend' ], :prompt => 'Select One', id: :roast_beans, class: "form-control" %>
</div>
<div class="row">
<%= form.fields_for :countries do |countries_form| %>
<div class="col-6">
<div class="form-group">
<%= countries_form.label :country %>
<%= countries_form.text_field :country_name, class: "form-control" %>
</div>
</div>
<div class="col-6">
<!-- note the appending of `countries_` to form.fields to allow for deeper nested to work-->
<%= countries_form.fields_for :regions do |regions_form| %>
<%= regions_form.label :region %>
<%= regions_form.text_field :region_name, class: "form-control" %>
<% end %>
<br />
</div>
<% end %>
</div>
<div class="form-group">
<%= form.label :bestfor, "Style", class: 'control-label' %><br />
<%= form.select :bestfor, [ 'Espresso','Filter' ], :prompt => 'Select One', id: :roast_bestfor, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :roast, "Strength", class: 'control-label' %><br />
<%= form.select :roast, [ 'Light','Medium','Dark' ], :prompt => 'Select One', id: :roast_roast, class: "form-control" %>
</div>
<div class="form-group">
<%= form.label :tastingnotes, "Tasting Notes (separate with commas, e.g chocolate, citrus)", class: 'control-label' %><br />
<%= form.text_area :tastingnotes, id: :roast_tastingnotes, class: "form-control" %>
</div>
<br />
<div class="form-group">
<%= form.label :avatar, "Upload image...", class: 'control-label' %>
<%= form.file_field :avatar %>
</div>
<div class="actions">
<%= form.submit class: "btn btn-success" %> <%= link_to "Cancel", "/roasts", class: "btn btn-secondary"%>
</div>
<% end %>
</form>
roast_controller.rb
class RoastsController < ApplicationController
before_action :set_roast, only: [:show, :edit, :update, :destroy]
before_action :authenticate_user!, only: [:create, :edit, :update, :destroy]
before_action :set_search
# GET /roasts
# GET /roasts.json
def index
#q = Roast.ransack(params[:q])
#roastsalpha = #q.result.order(:name)
#roastcount = Roast.count(:country)
#roasts = Roast.order(:name).count
#countroastschart = Roast.order("roaster DESC").all
end
# GET /roasts/1
# GET /roasts/1.json
def show
#roast = Roast.friendly.find(params[:id])
#commentable = #roast
#comments = #commentable.comments
#comment = Comment.new
#sameroaster = Roast.where(roaster: #roast.roaster)
#samecountry = Roast.where(country: #roast.country)
#roastcount = Roast.where(roaster: #roast.roaster)
end
# GET /roasts/new
def new
#roast = Roast.new
3.times {#roast.countries.build.regions.build}
end
# GET /roasts/1/edit
def edit
3.times {#roast.countries.build.regions.build}
end
# POST /roasts
# POST /roasts.json
def create
#roast = Roast.new(roast_params)
respond_to do |format|
if #roast.save
format.html { redirect_to #roast, notice: 'Roast was successfully created.' }
format.json { render :show, status: :created, location: #roast }
else
format.html { render :new }
format.json { render json: #roast.errors, status: :unprocessable_entity }
end
end
end
# PATCH/PUT /roasts/1
# PATCH/PUT /roasts/1.json
def update
respond_to do |format|
if #roast.update(roast_params)
format.html { redirect_to #roast, notice: 'Roast was successfully updated.' }
format.json { render :show, status: :ok, location: #roast }
else
format.html { render :edit }
format.json { render json: #roast.errors, status: :unprocessable_entity }
end
end
end
# DELETE /roasts/1
# DELETE /roasts/1.json
def destroy
#roast.destroy
respond_to do |format|
format.html { redirect_to roasts_url, notice: 'Roast was successfully destroyed.' }
format.json { head :no_content }
end
end
private
# Use callbacks to share common setup or constraints between actions.
def set_roast
#roast = Roast.friendly.find(params[:id])
end
# Never trust parameters from the scary internet, only allow the white list through.
def roast_params
params.require(:roast).permit(:roaster, :roaster_id, :name, :bestfor, :beans, :roast, :tastingnotes, :notes, :slug, :avatar, countries_attributes: [:country_id, :country_name, regions_attributes: [:id, :region_name]])
end
end
I think you are doing many wrong things. By looking your other questions I saw your models. I put some important things:
class Roast < ApplicationRecord
has_many :countries
accepts_nested_attributes_for :countries
end
class Country < ApplicationRecord
has_many :regions, inverse_of: :country
accepts_nested_attributes_for :regions
belongs_to :roast
end
class Region < ApplicationRecord
belongs_to :country, inverse_of: :regions
end
In these models I didn't see the Roaster. I assume a Roast belongs_to :roaster.
So: your Roast has many countries and each country has many regions. But you are passing country names and region names in your view to the create controller. You need to pass the ids, so that you save references to these models.
You have many unnecessary field in params, and some missing ones. This is how it should be:
def roaster_params
params.require(:roast).permit(:roaster_id, :name, :bestfor, :beans, :tastingnotes, :notes, :slug, :avatar, countries_attributes: [:id, regions_attributes: [:id]])
end
You don't need roast, roaster, country_name, region_name. You need the id of the country (and not the country_id), and the id of the region (and not the region_id)
In your form you should ask for country and region ids:
<%= countries_form.collection_select(:id, Country.all, :id, :name, :prompt => 'Select Country') %>
<%= regions_form.collection_select(:id, Region.all, :id, :name, :prompt => 'Select Region') %>
In fact this is more difficult, because a region belongs to a country, but here you are showing all regions. You should only show regions for the selected country (which is dynamic).

How to set up error messages of associated model?

Hello rails community!
I have booking_post model that has_many reservations.
class BookingPost < ApplicationRecord
has_many :reservations, dependent: :destroy
end
All reservation belongs_to booking_post and have some validations
class Reservation < ApplicationRecord
belongs_to :booking_post
before_save { self.email = email.downcase }
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX }
validates :name, :email, :phone_number, :start, :end, presence: true
end
My routes are next:
resources :booking_posts do
resources :reservations, only: [:new, :create]
end
Methods:
class BookingPostsController < ApplicationController
def show
#booking_picture = #booking_post.booking_pictures.build
#booking_pictures = #booking_post.booking_pictures
#reservation = #booking_post.reservations.build
#reservations = #booking_post.reservations
end
end
class ReservationsController < ApplicationController
def new
#reservation = Reservation.new
end
def create
#booking_post = BookingPost.find(params[:booking_post_id])
#email= User.where(admin: true).first.email
#reservation = #booking_post.reservations.build(reservation_params)
if #reservation.save
#saved_reservation = #reservation
redirect_to :back
flash[:notice] = 'Reservation was successfully created.'
ReservationMailer.fresh_message(#saved_reservation, #email).deliver_now
else
redirect_to #booking_post
flash[:info] = #reservation.errors.full_messages do |m|
m
end
end
end
end
I would like to create on booking_posts/show.html.erb form_for #reservation, and render on this page errors for #reservation. When I create valid #reservation, I see on booking_posts/show.html.erb successfull flash message, but unvalid #reservation appear without any error flash messages.
form_for #reservation on booking_posts/show.html.erb:
<div class="card-action">
<%= form_for([#reservation.booking_post, #reservation], html: {multipart: true}, class: "col s12") do |f| %>
<% if #reservation.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#reservation.errors.count, "error") %> prohibited this post from being saved:</h2>
<ul>
<% #reservation.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="col s6">
<%= f.label :start %>
<%= f.date_field :start, placeholder: "start time", class: "datepicker" %>
</div>
<div class="col s6">
<%= f.label :end %>
<%= f.date_field :end, placeholder: "end time", class: "datepicker" %>
</div>
<div class="col s6">
<%= f.label :reservation_time %>
<%= f.time_field :reservation_time, placeholder: "time", class: "timepicker", id: "timepicker", type: "time" %>
</div>
<div class="input-field col s6">
<%= f.label :name %>
<%= f.text_field :name, class: "validate" %>
</div>
<div class="input-field col s6">
<%= f.label :email %>
<%= f.text_field :email, class: "validate" %>
</div>
<div class="input-field col s6">
<%= f.label :phone_number %>
<%= f.text_field :phone_number, class: "validate" %>
</div>
<div class="waves-effect waves-light btn">
<%= f.submit t(:submit_reservation)%>
</div>
<% end %>
<br>
</div>
I would like render error messages for #reservation on #booking_post page
(in booking_post_path, not in new_reservation_path or anyting else). How can I do so?
Thanks for solutions
In your else block, Please update it like this
flash[:notice] = #reservation.errors.full_messages.to_sentence
redirect_to #booking_post

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

Why do I get "NameError in Discussions"?

I'm currently working on a Ruby on Rails application that involves users creating discussions and commenting with microposts. It's a pretty simple concept but I'm new and have run into some trouble.
When trying to look at the discussions page (index of discussion) I get the error "NameError in Discussions#index":
undefined local variable or method `discussion' for #<#<Class:0x00000100c6e020>:0x0000010380edd8>
This is my Discussion controller:
class DiscussionsController < ApplicationController
before_filter :signed_in_user, only: [:index, :edit, :update]
def show
#user = User.find(params[:id])
#discussions = #user.discussion.paginate(page: params[:page])
#microposts = #user.micropost.paginate(page: params[:page])
end
def index
#discussions = Discussion.all
end
def create
#discussion = current_user.discussions.build(params[:discussion])
if #discussion.save
flash[:success] = "Discussion Started!"
redirect_to root_url
else
render 'static_pages/home'
end
end
def destroy
end
def edit
end
def update
end
def new
end
end
This is my Micropost form:
<% #micropost = Micropost.new %>
<% #micropost.discussion_id = discussion.id %>
<%= form_for(#micropost) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_area :content, placeholder: "Compose new micropost..." %>
</div>
<%= f.hidden_field :discussion_id, discussion.id%>
<%= f.submit "Post", class: "btn btn-large btn-primary" %>
<% end %>
This is my Discussion partial:
<% content_for :script do %>
<%= javascript_include_tag 'hover_content' %>
<% end %>
<li>
<div class = "intro-bar"><span class = "intro"><%=discussion.intro %></span></div>
<div class = "content-bar">
<span class = "content"><%= discussion.content %></span>
<div class = "buttons">
<div class = "vote-neg"><%= link_to "Break Up", signup_path,class: "btn btn-large btn-breakup" %></div>
<div class = "vote-plus"><%= link_to "Stay Together", signup_path,class: "btn btn-large btn-staytogether" %></div>
</div>
</div>
</li>
<span class = "timestamp">
Posted <%= time_ago_in_words(discussion.created_at) %> ago.
</span>
<div class = "comments">
<% discussion.microposts.each do |micropost| %>
<li>
<div class = "post-comment"><%= micropost.content%></div>
</li>
<% end %>
</div>
<% if signed_in? %>
<div class = "row">
<aside class = "span4">
<section>
<%= render 'shared/micropost_form', :locals => {:discussion => discussion }%>
</section>
</aside>
</div>
<% end %>
This is my Micropost controller:
class MicropostsController < ApplicationController
before_filter :signed_in_user, only: [:create, :destroy]
def index
end
def create
#discussion = current_user.discussions.new
#micropost = current_user.microposts.build(params[:micropost])
if #micropost.save
flash[:success] = "Posted!"
redirect_to root_url
else
render 'static_pages/home'
end
end
def destroy
end
end
This is my Discussion model:
class Discussion < ActiveRecord::Base
attr_accessible :content, :intro
has_many :microposts, dependent: :destroy
belongs_to :user
validates :content, presence: true, length: { maximum: 600 }
validates :intro, presence: true
default_scope order: 'discussions.created_at DESC'
end
This is my User model:
class User < ActiveRecord::Base
attr_accessible :email, :username, :age, :sex, :points, :password, :password_confirmation
has_secure_password
has_many :microposts, dependent: :destroy
has_many :discussions, dependent: :destroy
before_save {|user| user.email = email.downcase}
before_save :create_remember_token
validates :username, presence: true, length: {maximum: 15}
VALID_EMAIL_REGEX = /\A[\w+\-.]+#[a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, format: { with: VALID_EMAIL_REGEX }, uniqueness: {case_sensitive: false}
validates :age, presence: true
validates :sex, presence: true
validates :password, presence: true, length: { minimum: 6 }
validates :password_confirmation, presence: true
private
def create_remember_token
self.remember_token = SecureRandom.urlsafe_base64
end
end
This is my Discussion index:
<% provide(:title, "Discussions") %>
<h1>Discussions</h1>
<ul class = "discussions">
<% #discussions.each do |discussion| %>
<li>
<%= render :partial =>"discussions/discussion", :locals=>{:discussion=>discussion} %>
</li>
<% end %>
</ul>
Try rewriting this line:
<%= render 'shared/micropost_form', :locals => {:discussion => discussion }%>
as:
<%= render 'shared/micropost_form', :discussion => discussion %>

Resources