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.
Related
i have a restaurant controller, and in the show method i render a form of another controller (dishes)
#new.html.erb (from dishes controller)
<%= render 'dishes/dish_form' %>
#show.html.erb (from restaurant controller)
<%= render template: 'dishes/new' %>
and this is the form:
<%= form_for #dish, :url => { :controller => "dishes", action_name => "create" } do |f| %>
<div class="field">
<%= f.label :dish_name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :description %>
<%= f.text_field :description %>
</div>
<div class="field">
<%= f.label :image %>
<%= f.file_field :avatar %>
</div>
<div class="actions">
<%= f.submit 'Add new dish' %>
</div>
<% end %>
but when i try so add a dish, i have this error
this is my dishes controller:
def new
#dish = Dish.new
end
def create
#dish = Dish.new(dish_params)
respond_to do |format|
if #dish.save
format.html { redirect_to #restaurant, notice: 'dish was successfully created.' }
format.json { render action: 'show', status: :created, location: :restaurant }
else
format.html { render action: 'show', location: :restaurant }
format.json { render json: #restaurant.errors, status: :unprocessable_entity }
end
end
end
private
def dish_params
params.require(:dish).permit(:avatar, :name, :description)
end
and this are my models:
class Restaurant < ApplicationRecord
has_many :dish, inverse_of: :restaurant
accepts_nested_attributes_for :dish
end
class Dish < ApplicationRecord
belongs_to :restaurant
end
im learning rails so maybe is a dumb error but im stuck
you should try to this to the controller action where the form is rendered
#dish = Dish.new
as you directly call the 'create' action rails is missing the instance variable Dish.new. Therefore the error message. Normally rails works like this:
def new
#dish = Dish.new
end
and than you call the create action on 'submit'.
However, as it looks like you call the form from the #show action just add this code there and you will be fine. May be not the best solution but it will work like that. Add to #show
#dish = Dish.new
Sorry mate. Yes #dish is correct and not :dishes.
OK.
My latest guess is that you want the restaurants to be able to create many dishes.
So you should set up your models accordingly:
#restaurants_model
has_many :dishes
#dishes_model
belongs_to :restaurant
next is that you add a column called restaurant_id to your dishes table
t.string :restaurant_id
than in your restaurant controller #show
def show
end
if you use the routes as normal, e.g.
resources :restaurants
resources :dishes
the show action of the restaurant controller should give you a url that looks like this: localhost:3000/restaurants/1
where the 1 is the id of the restaurant.
This id you want to save to the dish by adding a hidden field
=f.hidden_field :restaurant_id, value: #restaurant.id
you need to permit the restaurant_id in the dishes controller params, e.g. dish_params. Just add
:restaurant_id
and it should be fine. This way the id will be saved to the dishes table and you can later call it from there.
This gives you the chance to call #restaurant.dishes which will show all dishes of that restaurant.
If you just want to redirect back you can use redirect :back
Otherwise, you can try to integrate a helper to get the right restaurant using the hidden restaurant_id field
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 have a rails 4 application that has an add page and and a edit page. You can add elements easily (there is no issues), but then when you go to edit those and click save, it adds the fields you added initially a second time.
Here is my _form.html.erb
<%= nested_form_for #store do |f| %>
<%= f.fields_for :products do |product_form| %>
<div class='field'>
<%= product_form.text_field :name %>
<%= product_form.hidden_field :_destroy %>
<%= link_to "REMOVE PRODUCT", '#', class: "remove_fields" %>
</div>
<% end %>
<p><%= f.link_to_add "Add PRODUCT", :products %></p>
<%= f.submit 'Save', :class => "primary small" %>
<% end %>
and my store.rb model:
class Store < ActiveRecord::Base
has_many :products, class_name: "StoreProduct"
accepts_nested_attributes_for :products, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
end
my update action in my controller looks like:
def update
respond_to do |format|
if #store.update(store_params)
format.html { redirect_to store_products_path(#store), notice: 'Store was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: 'edit' }
format.json { render json: #store.errors, status: :unprocessable_entity }
end
end
end
What does store_params look like in your controller? If id isn't one of the permitted values, then you can start to see the nested models created as new records each time the update action occurs. You would want to have something like:
params.require(:store).permit(products_attributes: [:id, :name, :_destroy])
See the documentation on strong parameters for the nested_form gem.
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 these models:
class User < ActiveRecord::Base
has_one :city
accepts_nested_attributes_for :city
end
class City < ActiveRecord::Base
belongs_to :user
end
This controller action:
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" }
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
and this view:
<%= form_for :user,:url => users_path,:method => :post do |f| %>
<%= f.fields_for :city do |b| %>
<%= b.collection_select :id,City.all,:id,:name %>
<% end %>
<div class="field">
<%= f.label :name %><br />
<%= f.text_field :name %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I am trying to allow the user to select a city from the list of already added cities. I am trying to present him a select. The select part it works, but the generated html code for it, looks like this:
<select name="user[city][id]" id="user_city_id">
<option value="1">One</option>
<option value="2">Two</option>
</select>
Notice that it's name doesn't have attribute anywhere. So, when I try to save it, I get this error:
City(#37815120) expected, got ActiveSupport::HashWithIndifferentAccess(#32969916)
How can I fix this?
EDIT: there is some progress, I tried to change the fields_for to this:
<%= f.fields_for :city_attributes do |b| %>
<%= b.collection_select :id,City.all,:id,:name %>
<% end %>
and now, the html seems to generate correctly. But I get this error now:
Couldn't find City with ID=1 for User with ID=
I have no idea what to do next.
EDIT2: overriding the city_attributes= method seems to work:
def city_attributes=(attribs)
self.city = City.find(attribs[:id])
end
I don't know if it's the way to go, but it seems good.
Have a look at this question that seems similar to yours :
Rails 3: How does "accepts_nested_attributes_for" work?
Actually, since the Cities already exsit, I think there is no need for nested forms here.
Try Replacing
<%= f.fields_for :city_attributes do |b| %>
<%= b.collection_select :id,City.all,:id,:name %>
<% end %>
With
<%= f.collection_select :city, City.all,:id,:name %>
Updated afters comments
Could you change your relationship with (and update database scheme accordingly)
class User < ActiveRecord::Base
belongs_to :city
end
class City < ActiveRecord::Base
has_many :users
end
And then try using:
<%= f.collection_select :city_id, City.all,:id,:name %>
You could also do a
<%= f.collection_select :city_id, City.all, :id, :name %>
in your view and then add virtual attributes to your User model:
class User < ActiveRecord::Base
...
def city_id(c_id)
update_attribute(:city, City.find(c_id))
end
def city_id
city.id
end
end
This might not be very clean, since the associated City model is "saved" whenever assigning an ID to some_user.city_id. However, this solution keeps your controller and view nice and clean.
Note: you might also want to account for a blank ID being passed in to the setter method.
Try this
<%= f.select(:city_id, City.all.collect {|p| [ p.name, p.id ] }) %>