Rails edit adding fields issue - ruby-on-rails

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.

Related

rails simple_forms and active_storage images are resulting in controller errors when editing products

I am building a small marketplace app using rails and currently am stuck on trying to get my product edit and create pages to work. I have added image upload capability and have utilised simple forms to add details for the products but any time I try to proceed with editing I get the following error:
The action 'update' could not be found for ProductsController
Meanwhile if I try to create a new product I get a different error:
{"seller":["must exist"]}
Please see my code below:
-products_controller.rb
def create
#product = Product.create
#product_id = #product.id
if #product.save
render :show, status: :created
else
render json: #product.errors, status: :unprocessable_entity
end
end
end
# PATCH/PUT /products/1 or /products/1.json
def update
#product = Product.update (product_params)
if #product.save
render products:id, status: :created
else
render json: #product.errors, status: :unprocessable_entity
end
end
def product_params
params.require(:product).permit(:title, :description, :price, :buyer_id, :seller_id, :category, :image_url)
end
edit.html.erb
<%= simple_form_for edit_product_path, url: {action: "update"} do |f| %>
<h1 class="heading">Edit Product</h1>
<%= render 'form', product: #product %>
<% end %>
-form.html.erb
<div class="form-inputs">
<%= f.input :title %>
<%= f.input :description %>
<%= f.input :price %>
<%= f.input :category, collection: ["footwear", "accessories", "menswear", "womenswear"] %>
<div class="form-group">
<% if product.picture.attached? %>
<%= image_tag product.picture, style: "width: 200px; display: block" %>
<% end %>
<%= f.file_field :picture %>
</div>
</div>
Any help I can get is greatly appreciated.
#product = Product.create
#product_id = #product.id
if #product.save
render :show, status: :created
else
render json: #product.errors, status: :unprocessable_entity
end
end <--- extra end
end
It looks like you have an extra hanging end in the middle of your create action. This would probably explain what's going on with it.
If this doesn't fix the issue, ensure that you have the proper routes defined in routes.rb.
If you keep getting the seller must exist error, Rails 5/6 automatically assumes that a belongs_to association will have a model present to associate to when the record is saved. You can turn this off by adding optional: true to your relationship definition like this:
class Product
belongs_to :seller, optional: true
end
You can include a seller_id in your SimpleForm on page load and associate it with that as long as you add the appropriate filter in product_params. It might look something like:
<%= f.input :seller_id, :input_html => { :value => #seller.id } %>

Nested form attributes wont save

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

Nested form not showing fields

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.

Rails nested form error: undefined method `update_attributes' for nil:NilClass

I'm using Mongoid, awesome_nested_fields gem and rails 3.2.8.
I have the functionality working on the client (adding multiple fields), but when I try to save I get a "undefined method `update_attributes' for nil:NilClass" error.
Here is all the relevant info:
profile.rb
class Profile
include Mongoid::Document
include Mongoid::Timestamps
has_many :skills, :autosave => true
accepts_nested_attributes_for :skills, allow_destroy: true
attr_accessible :skills_attributes
end
skill.rb
class Skill
include Mongoid::Document
belongs_to :profile
field :skill_tag, :type => String
end
View
<%= simple_form_for(#profile) do |f| %>
<%= f.error_notification %>
<div class="form-inputs">
<div class="items">
<%= f.nested_fields_for :skills do |f| %>
<fieldset class="item">
<%= f.input :skill_tag, :label => 'Skills:' %>
Remove Skill
<%= f.hidden_field :id %>
<%= f.hidden_field :_destroy %>
</fieldset>
<% end %>
</div>
Add Skill
</div>
<div class="form-actions">
<%= f.button :submit, :class => 'btn' %>
</div>
<% end %>
profiles_controller.rb
# PUT /profiles/1
# PUT /profiles/1.json
def update
#profile = Profile.find(params[:id])
respond_to do |format|
if #profile.update_attributes(params[:profile])
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' } # Notice
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: #profile.errors, status: :unprocessable_entity }
end
end
end
*********** UPDATE ************
I ended up switching to Ryan's nested_form gem and it works like a charm.
Your message directly points to the line of execution where it failed, ergo the line:
if #profile.update_attributes(params[:profile])
The problem is that the #profile instance variable is nil and rails can not find the update_attributes method for a nil object.
You could easly check your server log for the params hash, to see what params[:id] (probably it isn't defined at all) by going in the terminal where you started your app.
Or you can check your development log when being in your app folder:
tail -n 1000 development.log | egrep params

Select statement in a form partial for an HABTM association

I have two models Client and Topic. Both of which have a HABTM association between them.
I am trying to add a select statement in my _form partial in my Client views, that allows the user to add a topic to a client (or edit that topic, etc.).
This is what my form partial looks like:
<%= form_for(#client) do |f| %>
<div class="field">
<%= f.label :topic %><br />
<%= f.select :topics, Topic.all.collect { |topic| [topic.name, topic.id] }, {:include_blank => 'None'} %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
The first error I got was this:
ActiveModel::MassAssignmentSecurity::Error in ClientsController#create
Can't mass-assign protected attributes: topics
So, in my Client model, I added this:
attr_accessible :email, :firm_id, :name, :phone, :topics
This is the error I now get:
NoMethodError in ClientsController#create
undefined method `each' for "1":String
The create action of my Clients controller is very standard:
def create
#client = Client.new(params[:client])
respond_to do |format|
if #client.save
format.html { redirect_to #client, notice: 'Client was successfully created.' }
format.json { render json: #client, status: :created, location: #client }
else
format.html { render action: "new" }
format.json { render json: #client.errors, status: :unprocessable_entity }
end
end
end
These are the params submitted (notice that topics is being passed - instead of topic_id but topic_id doesn't work either):
{"utf8"=>"✓",
"authenticity_token"=>"J172LuZQc5NYoiMSzDD3oY9vGmxxCX0OdxcGm4GSPv8=",
"client"=>{"name"=>"Jack Daniels",
"email"=>"jack.daniels#some-email.com",
"phone"=>"2345540098",
"firm_id"=>"2",
"topics"=>"1"},
"commit"=>"Create Client"}
How can I get a topic assigned to my client on the creation of the client with this select statement?
Thanks!
When setting a "Topic" attribute, Client expects an instance of a Topic class.
Since you are passing IDs, you need to change:
<%= f.select :topics, Topic.all.collect { |topic| [topic.name, topic.id] }, {:include_blank => 'None'} %>
to set topic_ids instead:
<%= f.select :topic_ids, Topic.all.collect { |topic| [topic.name, topic.id] }, {:include_blank => 'None'} %>
And, of course, in attr_accessible:
attr_accessible :email, :firm_id, :name, :phone, :topic_ids

Resources