when I submit the form :
Parameters: {"authenticity_token"=>"LJ/ZME2lHZ7VwCDgPKX6OFe326fXSXo5UB4M0cPwbCE=", "project_id"=>"second", "utf8"=>"✓", "commit"=>"Add Todo", "esthour"=>{"rfp_id"=>"2", "cms_est_hours"=>"", "modul1hours_attributes"=>{"0"=>{"module_est_hours"=>"11", "modul1_id"=>"3"}, "1"=>{"module_est_hours"=>"111", "modul1_id"=>"4"}}, "designpages_est_hours"=>"", "ecommerce_est_hours"=>""}}
models
class Esthour < ActiveRecord::Base
has_many :modul1hours
accepts_nested_attributes_for :modul1hours
end
class Modul1hour < ActiveRecord::Base
belongs_to :esthour
attr_accessible :module_est_hours,:module_act_hours,:modul1_id,:esthour_id
end
view
<% #m1.map(&:id).each do |id|%>
<%= b.fields_for :modul1hours, #esthour.modul1hours.build do |f| %>
<%= f.hidden_field :modul1_id, :value => id %>
<%= f.text_field :module_est_hours, :size => 30 %>
</tr>
<% end %>
<% end %>
controller
def new
#esthour = Esthour.new
#project = params[:project_id]
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #esthour }
end
end
You can see my earlier question regarding this.
I'm waiting for valuable reply. Thanks.
Related
I have two controllers - ItemsController and TradesController. I'm building a #trade inside the ItemsController #show action, which is sent to the TradesController #create action with a form.
class ItemsController < ApplicationController
def show
#item = Item.friendly.find(params[:id])
#trade = current_user.requested_trades.build
#approved_trades = #item.trades
respond_to do |format|
format.html
format.json { render :json => #items.to_json(:methods => [:image_url]) }
end
end
class TradesController < ApplicationController
def create
#trade = current_user.requested_trades.build(trade_params)
respond_to do |format|
if #trade.save
format.html { redirect_to #trade, notice: "Your request for trade has been submitted. You will be notified once it is approved or denied." }
format.json { render :index, status: :created, location: #trade }
else
format.html { redirect_to #trade, notice: "Pick another amount" }
end
end
end
private
def trade_params
params.require(:trade).permit(:trade_requester, :trade_recipient, :wanted_item, :collateral_item, :shares)
end
end
And then here's my Trade model
class Trade < ActiveRecord::Base
belongs_to :trade_requester, class_name: "User"
belongs_to :trade_recipient, class_name: "User"
belongs_to :wanted_item, class_name: "Item"
belongs_to :collateral_item, class_name: "Item"
end
Here's the form in my Item's show view:
<%= form_for(#trade) do |f| %>
<%= f.hidden_field :wanted_item, value: #item.id %>
<div class="field">
<%= f.text_field :shares, placeholder: "Pick a number between 1 and #{#item.shares}" %>
<%= f.submit "Trade", class: "button minty-button wide-button" %>
</div>
<% end %>
The above code for the ItemsController posts to the TradesController create action, but I'm getting an error that says ActiveRecord::AssociationTypeMismatch in TradesController#createItem(#70095717466760) expected, got String(#70095657672800)
Why is that expecting an Item? It seems that if #trade creation results in error, then it should redirect to #trade.
The quick solution is to change your hidden field from :wanted_item to :wanted_item_id:
<%= form_for(#trade) do |f| %>
<%= f.hidden_field :wanted_item_id, value: #item.id %>
<div class="field">
<%= f.text_field :shares, placeholder: "Pick a number between 1 and #{#item.shares}" %>
<%= f.submit "Trade", class: "button minty-button wide-button" %>
</div>
<% end %>
Also, make sure your trade_params method permits wanted_item_id:
def trade_params
params.require(:trade).permit(:trade_requester, :trade_recipient, :wanted_item_id, :collateral_item_id, :shares)
end
You may have a similar issue with :collateral_item in another form.
I am required to use nested forms on an assignment I am working on and I got stuck because my nested form attributes wont submit to database.
Here is what my controller looks like
def new
#booking = Booking.new
params[:no_of_passengers].to_i.times { #booking.passengers.build }
end
def create
#booking = Booking.new(booking_params)
respond_to do |format|
if #booking.save
format.html { redirect_to '/booking_confirmed', notice: 'Booking was successfully created.' }
format.json { render :show, status: :created, location: #booking }
else
format.html { render :new }
format.json { render json: #booking.errors, status: :unprocessable_entity }
end
end
end
private
def booking_params
params.permit(
:airline, :origin, :destination, :departure_date, :departure_time, :arrival_date,
:arrival_time, :flight_id, :price, :no_of_passengers, :user_id, :booking,
passenger_attributes: [
:id,:booking_id, :name, :email,:done,:_destroy
]
)
end
Here is the association between the models
class Booking < ActiveRecord::Base
has_many :passengers
accepts_nested_attributes_for :passengers, reject_if: lambda { |attributes| attributes['name'].blank? }
end
class Passenger < ActiveRecord::Base
belongs_to :bookings
end
And here is the form
<%= form_for #booking do |b| %>
<%= b.fields_for :passengers do |p| %>
<%= p.text_field :name, placeholder: "Passenger Name" %>
<%= p.text_field :email, placeholder: "Passenger Email" %>
<% end %>
<% end %>
I checked the passenger table using Passenger.all in rails console and it returns nothing.
What am I doing wrong?
After a pairing session with sunnyk, I was able to see the errors.
The first error was that my class Passenger has belongs_to :bookings instead of belongs_to :booking. This is a common error though. The Associations between these classes now looks like:
class Booking < ActiveRecord::Base
belongs_to :flight
has_many :passengers
accepts_nested_attributes_for :passengers, reject_if:
lambda {|attributes| attributes['name'].blank?}, :allow_destroy => true
end
class Passenger < ActiveRecord::Base
belongs_to :booking
end
class Flight < ActiveRecord::Base
has_many :bookings
has_many :passengers, through: :bookings
accepts_nested_attributes_for :passengers
accepts_nested_attributes_for :bookings
end
Next:
Instead of using the default value of no_of_passengers for building my nested form, I used the cocoon gem, which makes nested forms building and management easier. I also crated a new params method, in which I made the flight_id permitted, and then passed it as an argument for my booking instance in my new method alongside my current user. So now my new method looks like this.
def new
#booking = Booking.new(new_booking_params)
#booking.user = current_user if current_user
end
def new_booking_params
params.permit(:flight_id)
end
After that, I had to make another params method for my create method, so as to allow the parameters I want in the bookings table, this include the passengers_attributes. Now my create method looks like this.
def create
#booking = Booking.new(another_booking_params)
respond_to do |format|
if #booking.save
format.html { redirect_to '/booking_confirmed', notice: 'Booking was successfully created.' }
format.json { render :show, status: :created, location: #booking }
else
format.html { render :new }
format.json { render json: #booking.errors, status: :unprocessable_entity }
end
end
end
def another_booking_params
params.require(:booking).permit(:flight_id, :user_id, :no_of_passengers,
passengers_attributes:[:name, :email])
end
Lastly, I had to adjust my form to look like this.
<%= form_for(#booking, url: bookings_path) do |f| %>
<%= f.hidden_field(:flight_id)%>
<%= f.hidden_field(:user_id) %>
<%= f.hidden_field(:no_of_passengers)%>
<%= f.fields_for :passengers do |passenger| %>
<%= render 'passenger_fields', :f => passenger %>
<% end %>
<%= link_to_add_association 'Add Another passenger',f, :passengers, :class => 'btn btn-primary add' %>
<%= submit_tag "Book Now", class: "btn btn-primary book" %>
<% end %>
and passenger_fields partial looks like.
<div class="nested-fields form-inline">
<div class="form-group">
<%= f.text_field :name, :class => "form-control", placeholder: "Passenger Name" %>
</div>
<div class="form-group">
<label>-</label>
<%= f.text_field :email, :class => "form-control", placeholder: "Passenger Email" %>
</div>
<div class="links pull-right">
<%= link_to_remove_association "Delete", f, class: "btn btn-danger" %>
</div>
<hr>
</div>
All that did the trick. I hope this will help others to understand nested forms better
I am trying to update an invoice fields, when checking out in the carts controller. These must be present when checking out, or it should fail. However, I can't get it to update, much less validate them.
Here is my code:
cart show view:
<div class = "row">
<div class = "col-lg-3 col-lg-offset-6 text-left">
<strong>Customer: </strong>
<%= collection_select(:invoice, :customer_id, #customers, :id, :full_name, {:prompt => 'Please Select'}, class: 'form-control') %>
</div>
<div class = "col-lg-3 ext-left">
<strong>Seller: </strong>
<%= collection_select(:invoice, :employee_id, #employees, :id, :full_name, {:prompt => 'Please Select'}, class: 'form-control') %>
</div>
<div class = "col-lg-12 text-right">
<%= form_tag carts_checkout_path, method: :post do |f| %>
<%= submit_tag 'Complete', class: 'btn btn-success' %>
<% end %>
</div>
</div>
carts controller:
class CartsController < ApplicationController
def show
#invoice = current_invoice
#invoice_products = current_invoice.invoice_products
#customers = Customer.all
#employees = Employee.all
end
def checkout
current_invoice.customer_id = params[:customer_id]
current_invoice.employee_id = params[:employee_id]
current_invoice.save
redirect_to current_invoice
end
end
current_invoice is the current session's invoice, related to the cart. It redirects correctly, but doesn't update.
in the invoices controller:
def invoice_params
params.require(:invoice).permit(:invoice_number, :customer_id, :invoice_date, :invoice_status_id, :employee_id, invoice_products_attributes: [:id, :invoice_id, :product_id, :price, :tax, :discount, :value])
end
Can anyone please help me in identifying where I am going wrong? Could it be my approach is not even valid?
Thanks in advance
The type of functionality you're after is considered "business logic" and should be implemented in the model and called from the controller.
You can define a method in a model:
class Invoice < ActiveRecord::Base
def update_invoice(cust_id, emp_id)
if self.update_attributes(:customer_id => cust_id], :employee_id = emp_id])
puts "Success!
else
puts "Failed to update record. Handle the error."
end
end
You can call my_method from carts_controller.rb like this:
def update
# all your regular update logic here
# replace the bit of code that saves the cart with something like this:
respond_to do |format|
if(current_invoice.update_invoice(params[:customer_id], params[:employee_id])
if(#cart.update(cart_params))
format.html { redirect_to #activity, notice: 'Activity was successfully updated.' }
format.json { render :show, status: :ok, location: #activity }
else
format.html { render :edit }
format.json { render json: #activity.errors, status: :unprocessable_entity }
end
end
end
Also, note the use of update_attributes rather than save. Bear in mind that update_attributes will return false if you run into any problems updating (e.g. one or more validations failed). Don't confuse update_attributes with the singular update_attribute which updates a single field and will not run validations.
Finally got it.
current_invoice.update_attributes(customer_id: params[:invoice][:customer_id], employee_id: params[:invoice][:employee_id])
Also in view, changed location of form_tag:
<div class = "row">
<%= form_tag carts_checkout_path, method: :post do |f| %>
<div class = "col-lg-3 col-lg-offset-6 text-left">
<strong>Cliente: </strong>
<%= collection_select(:invoice, :customer_id, #customers, :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
</div>
<div class = "col-lg-3 ext-left">
<strong>Vendedor: </strong>
<%= collection_select(:invoice, :employee_id, #employees, :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
</div>
<div class = "col-lg-12 text-right">
<%= submit_tag 'Completar', class: 'btn btn-success' %>
</div>
<% end %>
</div>
Could it be my approach is not even valid
Your approach is definitely valid, it's great that you're using sessions in this way.
I'd do it slightly differently:
#config/routes.rb
resource :cart, except: [:edit, :new, :create], path_names: { update: "checkout" }
This will give you the following paths:
#url.com/cart -> carts#show (here you can invoke a cart if one doesn't exist)
#url.com/cart/checkout #-> POST to "update" method in carts controller
#url.com/cart/ (method: :delete) -> DELETE to "destroy" cart (refresh)
--
#app/controllers/carts_controller.rb
class CartsController < ApplicationController
before_action :setup_cart
def show
#cart = current_cart #-> products called from this. I don't know how you're linking them
#customers = Customer.all
#employees = Employee.all
end
def update
#invoice = Invoice.find_or_create_by(id: current_card.id)
#invoice.update update_params
redirect_to cart_path
end
def destroy
current_cart = nil
redirect_to carts_path, notice: "Cart Cleared"
end
private
def setup_cart
current_cart ||= sessions[:cart]
end
def update_params
params.require(:cart).permit(:customer_id, :employee_id)
end
end
Now, to update the cart, you'll want to take note from MarsAtomic's answer. However it must be noted that naked params are not available in the model.
If you use update_attributes, or just plain update, you'll need to do the following:
#app/models/cart.rb
class Invoice < ActiveRecord::Base
has_many :products
belongs_to :employee
belongs_to :customer
#validations here
#callbacks here (what MarsAtomic refers to as business logic)
before_save :do_something, only: :update
private
def do_something
#something here
#params appended to current instance of object
#eg self.customer_id
end
end
I'd also go more succinct in your view:
#app/views/carts/show.html.erb
<div class = "row">
<%= form_tag cart_checkout_path, method: :patch do |f| %>
<% options = [["cliente", "customer"], ["vendedor", "employee"]] %>
<% options.each do |name, type| %>
<%= content_tag :strong, "#{name.titleize}:" %>
<%= collection_select :cart, eval(":#{type}_id"), instance_variable_get("##{type.pluralize}"), :id, :full_name, {:prompt => 'Favor Seleccionar'}, class: 'form-control') %>
<% end %>
<% content_tag :div, class: "col-lg-12 text-right" do %>
<%= submit_tag 'Completar', class: 'btn btn-success' %>
<% end %>
<% end %>
</div>
when I submit the form :
Parameters: {"authenticity_token"=>"LJ/ZME2lHZ7VwCDgPKX6OFe326fXSXo5UB4M0cPwbCE=", "project_id"=>"second", "utf8"=>"✓", "commit"=>"Add Todo", "esthour"=>{"rfp_id"=>"2", "cms_est_hours"=>"", "modul1hours_attributes"=>{"0"=>{"module_est_hours"=>"11", "modul1_id"=>"3"}, "1"=>{"module_est_hours"=>"111", "modul1_id"=>"4"}}, "designpages_est_hours"=>"", "ecommerce_est_hours"=>""}}
models
class Esthour < ActiveRecord::Base
has_many :modul1hours
accepts_nested_attributes_for :modul1hours
end
class Modul1hour < ActiveRecord::Base
belongs_to :esthour
attr_accessible :module_est_hours,:module_act_hours,:modul1_id,:esthour_id
end
view
<% #m1.map(&:id).each do |id|%>
<%= b.fields_for :modul1hours, #esthour.modul1hours.build do |f| %>
<%= f.hidden_field :modul1_id, :value => id %>
<%= f.text_field :module_est_hours, :size => 30 %>
</tr>
<% end %>
<% end %>
controller
def new
#esthour = Esthour.new
#project = params[:project_id]
respond_to do |format|
format.html # new.html.erb
format.json { render :json => #esthour }
end
end
You can see my earlier question regarding this.
I'm waiting for valuable reply. Thanks.
you should add in your Esthour model:
attr_accessible :modul1hours_attributes
I have a nested form based in a has_one relationship but it's not showing the fields.
What am I missing?
New action
def new
#doctor = Doctor.new
1.times { #doctor.build_schedule }
respond_to do |format|
format.html # new.html.erb
format.json { render json: #doctor }
end
end
_form
<%= simple_form_for(#doctor, :html => { class: "form-horizontal"}) do |f| %>
<%= f.input :name %>
<%= f.simple_fields_for :schedule do |builder| %>
<%= render 'days_checkboxes', :f => builder %>
<%= f.submit %>
<% end %>
Model
class Doctor < ActiveRecord::Base
has_one :schedule, dependent: :destroy
end
Do I have to set the build in other actions?
If there is no schedule associated object on your model instance, yes you will need to call build_schedule wherever you want to reference it. If you didn't do this, the form wouldn't render at all because it has nothing to display the fields for.