nested attributes prefilled child data with empty parent - ruby-on-rails

I am stuck in a situation where I need to give prefilled data for the fields_for with an empty form. Let me explain with an example
Association
class User < ApplicationRecord
has_one :account, foreign_key: :user_id
accepts_nested_attributes_for :account
end
Class Account
belongs_to :user
end
Now I have a form on the dashboard index page.
<%= form_for #account, :url => account_path, html: { class: "abc" } do |f| %>
<%= f.hidden_field :user_id, :value => current_user.id %>
<%= f.fields_for :user do |user| %>
<div class="medium-4 columns">
<label>First Name</label>
<%= user.text_field :first_name , class: 'xyz', data: {input: 'someinput'} %>
</div>
<div class="medium-4 columns">
<label><b>Last Name<span class="invalid_message"> This field is required</span></b></label>
<%= user.text_field :last_name, class: 'xyz', data: {input: 'someinput'} %>
</div>
<% end %>
<div class="medium-4 medium-offset-2 columns">
<label>Phone Number</label>
<%= f.text_field :phone_number, class: 'xyz', data: {input: 'someinput'} %>
</div>
<% end %>
Controller
class AccountsController < ApplicationController
def create
#account = Account.new(account_params)
if #account.save
render json: {status: 'successfull'}
else
render json: {error: "#{#account.errors.full_messages}"}, status: 400
end
end
private
def account_params
params.require(:account).permit(:phone_number, :user_id, user_attributes: [:first_name, :last_name])
end
end
class DashboardController < ApplicationController
def index
##I will always have current_user. For the account it will be blank for the first time
if current_user.account.blank?
#account = Account.new
else
#account = current_user.account
end
end
end
Parameters
{"utf8"=>"✓", "authenticity_token"=>"asdasdadadadadsadadadadadQpy0tA82asdaalAgJsUcNk1i/kGETfZqnuQA==", "account"=>{"user_id"=>"123", "user"=>{"first_name"=>"sd", "last_name"=>"ad"}, "phone_number"=>"1212"}}
There are two issues
1) First and Last name are not getting prefilled
2) Params are not going correctly. It should be account_attributes instead of account in the parameter.
Note: In my case, #account will be blank for the first time but still user object(which is the current user) has first_name and last_name already wHich I need to prefill in the fields_for. Also, I need a way to update the first and last name
Can anyone tell me where I am doing wrong

Change #account = Account.new for #account = current_user.build_account. You should see the fields prefilled.

Finally, I found the solution. I was doing a couple of things wrong
1) In form_for it should be #user instead of #account
2) Then in the controller this form will always send it to update action instead of create. The reason is I will always have current_user so rails will automatically check if the object(current_user) exists so instead of sending to create it will send to update action
3) Lastly, when working with one to one assocation we need to load the parent object and build the child object.
<%= form_for #user, :url => user_path, html: { class: "abc" } do |f| %>
<div class="medium-4 columns">
<label>First Name</label>
<%= f.text_field :first_name , class: 'xyz', data: {input: 'someinput'} %>
</div>
<div class="medium-4 columns">
<label><b>Last Name<span class="invalid_message"> This field is required</span></b></label>
<%= f.text_field :last_name, class: 'xyz', data: {input: 'someinput'} %>
</div>
<%= f.fields_for :account do |account_info| %>
<div class="medium-4 medium-offset-2 columns">
<label>Phone Number</label>
<%= account_info.text_field :phone_number, class: 'xyz', data: {input: 'someinput'} %>
</div>
<% end %>
<% end %>
class UsersController < ApplicationController
def update
if current_user.update(account_params)
render json: {status: 'successfull'}
else
render json: {error: "#{#account.errors.full_messages}"}, status: 400
end
end
private
def account_params
params.require(:account).permit(:first_name, :last_name, account_attributes: [:phone_number])
end
end
class DashboardController < ApplicationController
def index
##I will always have current_user. For the account it will be blank for the first time
if current_user.account.blank?
#account = current_user
#account.build_account
end
end
end

Related

Nested Route New object creation does not complete if loop

I currently have review model that will allow a user to create reviews for a tea model. The user who creates the review can edit or delete the review. I have a nested route within teas that allows you to create a new review for teas as you are viewing all reviews for that specific tea. Currently the nested new route does not allow creation as well as a google authenticated user can not create a review. Below is my controller action and view. I am not experiencing any error it just appears to rollback the database and follow the else logic and render the new page again.
Model
class Review < ApplicationRecord
belongs_to :user
belongs_to :tea
validates :title, presence: true
validates :rating, numericality: {only_integer: true, greater_than_or_equal_to: 0, less_than: 11}
validates :tea, uniqueness: {scope: :user, message: "has already been reviewed by you" }
scope :order_by_rating, ->{left_joins(:reviews).group(:id).order('avg(rating) desc')}
end
Controller Action
def create
#review = current_user.reviews.build(review_params)
if #review.valid?
#review.save
redirect_to new_review_path(#review)
else
render :new
end
end
View
<%= form_for Review.new do |f|%>
<% if params[:tea_id] %>
<%= f.hidden_field :tea_id %>
<% else %>
<div>
<%= f.label :tea_id, "Select a Tea Blend" %>
<%= f.collection_select :tea_id, Tea.alpha, :id, :flavor_and_brand, include_blank: true %>
</div>
<% end %>
<div>
<%= f.label :rating %>
<%= f.number_field :rating, min:0, max:10 %>
</div>
<br>
<div>
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<br>
<div>
<%= f.label :content %>
<br>
<%= f.text_area :content, size: "60x25" %>
</div>
<br>
<%= f.submit %>
<% end %>
The simple answer was that I did not include create in my before action. This is what was causing my set_tea to not be automatically done as a before action.
The correct way to do this is by defining a nested route and setting up the form so that it posts to that route. So instead of creating a single form where the user has to select the tea you create a form on the show page or by each tea in a index page where the user can create reviews.
# config/routes.rb
resources :teas do
resources :reviews, shallow: true
end
shallow: true makes it so that the member actions (show, edit, update, destroy) are not nested.
Then setup a partial for the form so that you can reuse it:
# app/views/reviews/_form.html.erb
<%= form_for([local_assigns(:tea), review]) do |f| %>
<div class="field">
<%= f.label :rating %>
<%= f.number_field :rating, min:0, max:10 %>
</div>
<div class="field">
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :content %>
<%= f.text_area :content, size: "60x25" %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
By passing an array you get the nested route as the action attribute (/teas/1/reviews) and don't have to monkey around with a hidden input. local_assigns(:tea) avoids a NoMethodError if its not passed to the partial. The array is compacted so that this partial will work for both creating and updating.
# app/views/reviews/new.html.erb
<%= render partial: 'form', tea: #tea, review: #review >
# app/views/reviews/edit.html.erb
<%= render partial: 'form', review: #review >
# app/views/teas/show.html.erb
<h2>Review this tea</h2>
<%= render partial: 'reviews/form', tea: #tea, review: #tea.reviews.new >
In the controller you can just fetch the tea from params[:tea_id] since you passed it in the path.
class ReviewsController < ApplicationController
before_action :set_tea, only: [:new, :index, :create]
before_action :set_review, only: [:show, :edit, :update, :destroy]
# POST /teas/1/reviews
def create
# creating the review off the tea reveals intent better than doing
# it off the user
#review = #tea.reviews.new(review_params) do |r|
r.user = current_user
end
# Always check if the record is actually persisted
# - not just if the applications validations pass!
if #review.save
# you could also redirect to the review but this makes more
# sense from a ux perspective
redirect_to #tea, notice: 'Thank you for your review'
else
render :new
end
end
# GET /reviews/:id/edit
def edit
end
# PUT|PATCH /reviews/:id
def update
if #review.update(review_params)
redirect_to #review, notice: 'Review updated.'
else
render :edit
end
end
private
def set_tea
#tea = Tea.find(params[:tea_id])
end
def set_review
#review = Review.find(params[:id])
end
def review_params
params.require(:review).permit(:rating, :title)
end
end

Ruby on Rails - Image Upload

I'd like to add images to a model. I have Users set up with Devise and they have_many Items that they are selling.
I would now like to add an array of images to the Item model (not sure if this is the best approach).
I have looked a Paperclip but can only do one image. Also looked at carrier wave but not sure how to implement on existing model.
Here is some of my code.
Item.rb
class Item < ActiveRecord::Base
belongs_to :user
validates :title, presence: true
validates :price, presence: true
validates :description, presence: true
end
items_controller.rb
class ItemsController < ApplicationController
before_action :findItem, only: [:edit, :update, :sold]
def index
#items = Item.all
end
def new
#item = Item.new
end
def create
#item = Item.create(item_params)
#item.user = current_user
if #item.save
flash[:success] = "Your item was successfully listed."
render 'show'
else
flash[:error] = "Your item could not be listed. Please try again."
render 'new'
end
end
def show
#item = Item.find(params[:id])
end
def edit
end
def update
if #item.update(item_params)
flash[:success] = "Your item listing was updated successfully."
redirect_to item_path(#item)
else
flash[:error] = "Your listing was not updated. Please try again."
render 'edit'
end
end
def sold
#item.toggle(:sold)
#item.save
redirect_to item_path(#item)
end
private
def item_params
params.require(:item).permit(:title, :price, :description)
end
def findItem
#item = Item.find(params[:id])
end
end
Form for Item/new.html.erb
<div class="row">
<div class="col-xs-12">
<%= form_for(#item, :html => {class: "form-horizontal", role: "form"}) do |f| %>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :title %>
</div>
<div class="col-sm-8">
<%= f.text_field :title, class: "form-control", placeholder: "What are you selling?", autofocus: true %>
</div>
</div>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :price %>
</div>
<div class="col-sm-8">
<%= f.number_field :price, class: "form-control" %>
</div>
</div>
<div class="form-group">
<div class="control-label col-sm-2">
<%= f.label :description %>
</div>
<div class="col-sm-8">
<%= f.text_area :description, rows: 10, class: "form-control", placeholder: "Describe your item. The more detail you include, the more likely it is to sell." %>
</div>
</div>
<div class="form-group">
<div class="center col-sm-offset-1 col-sm-10">
<%= f.submit class: "btn btn-primary btn-lg" %>
</div>
</div>
<% end %>
<div class="center col-xs-4 col-xs-offset-4">
<%= link_to "[ Cancel and return to listing page ]", items_path %>
</div>
You simply want to create an Image model. And set the relation with Item as follows: Item has_many :images, and Image belongs_to :item.
But, yeah, paperclip is a good start.
EDIT: Oh, and welcome Rails, you will find a good community ready to help. You might also find useful to search for accepts_nested_attributes_for so you can have the images uploaded in your Item form and cocoon to dynamically add and remove images on the item's form.
#app/models/item.rb
class Item < ActiveRecord::Base
has_many :images
accepts_nested_attributes_for :images
validates :title, :price, :description, presence: true
end
#app/models/image.rb
class Image < ActiveRecord::Base
belongs_to :item
has_attached_file :picture
end
--
The above will allow you to create an Item, which will have any number of images that you want. You can pass the new images through accepts_nested_attributes_for:
#app/controllers/items_controller.rb
class ItemsController < ApplicationController
def new
#item = Item.new
#item.images.build
end
def create
#item = Item.new item_params
#item.save
end
private
def item_params
params.require(:item).permit(images_attributes: [:picture])
end
end
#app/views/items/new.html.erb
<%= form_for #item do |f| %>
<%= f.fields_for :images do |i| %>
<%= i.file_field :picture %>
<% end %>
<%= f.submit %>
<% end %>

Ruby on Rails - Display field from related table

I'm learning RoR and I'm having trouble getting a value from a related table to display. I've tried the suggestions mentioned here and here but I still haven't gotten it to work.
I have a customer record that has a one-to-many relationship with contacts.
Here are snips of my models:
class Contact < ActiveRecord::Base
belongs_to :customer
class Customer < ActiveRecord::Base
has_many :contacts, dependent: :destroy
and this is my Contacts index.html.erb that is producing the error
<% #contacts.each do |contact| %>
<tr class="<%= cycle("even pointer", "odd pointer") %>">
<td><%= contact.first_name %> <%= contact.last_name %></td>
<td><%= contact.customer.name %></td> <!-- this is the error line -->
<td><%= contact.office_phone %></td>
<td><%= contact.cell_phone %></td>
<td><%= contact.email %></td>
<td>
<% if current_user.admin? %>
<%= link_to "edit", contact, method: :edit %>
<% end %>
</td>
</tr>
<% end %>
The error I get is: undefined method `name' for nil:NilClass
What am I doing wrong? It seems to me like the way I'm referencing the field I'm not actually getting to the data in the Customers table but I'm not really sure. Did I build the relationship correctly in my models?
UPDATE to add controller code
class ContactsController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :new, :create]
def index
#contacts = Contact.paginate(page: params[:page])
end
def show
#contact = Contact.find(params[:id])
end
def new
#contact = Contact.new
end
def create
#contact = Contact.new(contact_params)
if #contact.save
flash[:success] = "Contact created!"
redirect_to #contact
else
render 'new'
end
end
def edit
#contact = Contact.find(params[:id])
end
def update
#contact = Contact.find(params[:id])
if #contact.update_attributes(contact_params)
flash[:success] = "Contact updated"
redirect_to #contact
else
render 'edit'
end
end
private
def contact_params
params.require(:contact).permit(:first_name, :last_name, :email, :address1,
:address2, :office_phone, :cell_phone, :website, :city, :zip, :facebook, :twitter)
end
end
class CustomersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :new, :create]
def index
#customers = Customer.paginate(page: params[:page])
end
def show
#customer = Customer.find(params[:id])
end
def new
#customer = Customer.new
end
def create
end
def edit
#customer = Customer.find(params[:id])
end
def update
#customer = Customer.find(params[:id])
if #customer.update_attributes(customer_params)
flash[:success] = "Customer updated"
redirect_to #customer
else
render 'edit'
end
end
private
def customer_params
params.require(:customer).permit(:name, :email, :address1,
:address2, :phone, :fax, :website, :city, :zip, :facebook, :duns_number)
end
end
EDIT:
This is my contact.html.erb form.
<%= form_for #contact, html: { class: "form-horizontal form-label-left" } do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="form-group">
<%= f.label :first_name, nil, class: 'control-label col-md-3 col-sm-3 col-xs-12' %>
<div class="col-md-6 col-sm-6 col-xs-12">
<%= f.text_field :first_name, class: 'form-control col-md-7 col-xs-12' %>
</div>
</div>
<div class="form-group">
<%= f.label :last_name, nil, class: 'control-label col-md-3 col-sm-3 col-xs-12' %>
<div class="col-md-6 col-sm-6 col-xs-12">
<%= f.text_field :last_name, class: 'form-control col-md-7 col-xs-12' %>
</div>
</div>
<div class="form-group">
<%= f.label :customer_id, nil, class: 'control-label col-md-3 col-sm-3 col-xs-12' %>
<div class="col-md-6 col-sm-6 col-xs-12">
<%= select_tag :customer_id, options_for_select(Customer.all.collect {|c| [ c.name, c.id ] }), class: 'form-control' %>
</div>
</div>
<div class="form-group">
<%= f.label :office_phone, nil, class: 'control-label col-md-3 col-sm-3 col-xs-12' %>
<div class="col-md-6 col-sm-6 col-xs-12">
<%= f.text_field :office_phone, class: 'form-control col-md-7 col-xs-12' %>
</div>
</div>
<div class="ln_solid"></div>
<div class="form-group">
<div class="col-md-6 col-sm-6 col-xs-12 col-md-offset-3">
<%= f.submit "Save changes", class: "btn btn-primary" %>
</div>
</div>
<% end %>
You are referencing it correctly.
It looks like your contact has not yet been saved to the database. This would cause the relation to not yet exist.
You would might need to be creating the relationship differently.
I usually do something like:
customer = Customer.create
customer.contacts << Contact.create({first_name: 'John', last_name: 'Smith'})
which creates the associations and commits to the DB right away. Then, in the same request, your contact's customer will be set and accessible.
You could also do this, but it's a little redundant:
customer = Customer.create
customer.contacts << Contact.create({first_name: 'John', last_name: 'Smith', customer: customer})
EDIT
It seems that perhaps you need to be assigning a customer to the contact when it is created/updated. In your Contact form, you'll likely need something like this (seeing your existing forms would help here):
<%= select_tag :customer_id, options_for_select(Customer.all.collect {|c| [ c.name, c.id ] }) %>
or (using simple_form):
f.input :customer_id, collection: Customer.all, selected: #contact.customer_id
# or more simply
f.association :customer
Or maybe a hidden form field if you know the Customer when building the form:
f.input :customer_id, as: :hidden, value: #customer.id
Then in your controller, add customer_id to the contact_params like this:
def contact_params
params.require(:contact).permit(
:first_name, :last_name, :email, :address1,
:address2, :office_phone, :cell_phone,
:website, :city, :zip, :facebook, :twitter,
:customer_id
)
end
Try .try() activesupport method:
<td><%= contact.customer.try(:name) %></td>
If contact.customer is nil (Contact doesn't belongs_to Customer), :name method wouldn't be invoked.
UPDATED Another hints:
a. That's a bad thing that you have a contact without customer. Consider to add this to your model:
class Contact < ActiveRecord::Base
...
validates :customer, presence: true
b. You can replace code like that:
<tr class="<%= cycle("even pointer", "odd pointer") %>">
with
<%= content_tag :tr, class: cycle('event pointer', 'odd pointer') %>
c. Replace this:
<%= contact.email %>
with this:
<%= mail_to contact.email, contact.customer.try(:name) %>
Generally this can help
For example, you have 2 tables City and Post, each post has city_id and you want to display in the views the name of the city associated with the post, you should call in the views the bloc then the name of the table then the name of column like that:
<% #post.each do |post| %>
<%= post.city.name %
<% end %>

i use many to many relationship but it not save data in through table

i have 2 model book and wishlist.
and between this 2 model i use many to many relationship.
Below is my model.
class Book < ActiveRecord::Base
has_many :book_wishlist_customizations, dependent: :destroy
has_many :wish_lists ,through: :book_wishlist_customizations
end
class BookWishlistCustomization < ActiveRecord::Base # through table
belongs_to :wish_list
belongs_to :book
end
class WishList < ActiveRecord::Base
has_many :book_wishlist_customizations
has_many :books,through: :book_wishlist_customizations
end
when i delete book from admin side it raise following error.
PG::ForeignKeyViolation: ERROR: update or delete on table "books" violates foreign key constraint "fk_rails_7a6b92667b" on table "wish_lists" DETAIL: Key (id)=(1) is still referenced from table "wish_lists". : DELETE FROM "books" WHERE "books"."id" = $1.
below is my book form.
<%= form_for #book, url: books_path do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.label :stream_id %>
<%= f.collection_select :stream_id, Stream.all, :id, :stream_name, prompt: true %>
</div>
</br>
<div class="field">
<%= f.label :university_board_id, "University" %>
<%= f.collection_select :university_board_id, UniversityBoard.where(category_id: $college_id).all, :id, :name, prompt: true %>
</div>
</br>
<div class="field">
<%= f.label :course_standard_id, "Course & Year" %>
<%=f.select(:course_standard_id, :"Please select" => true)%>
<%=f.select(:year_semester, :"Please select" => true)%>
</div>
</br>
<div class="field">
<%= f.label :college_school_id, "College" %>
<%=f.select(:college_school_id, :"Please select" => true)%>
</div>
</br>
<div class="field">
<%= f.label :subject_id, "Subject" %>
<%=f.select(:subject_id, :"Please select" => true)%>
</div>
</br>
<div class="field">
<%= f.label :book_name, "Book name" %>
<%= f.text_field :book_name %>
</div>
</br>
<div class="actions">
<%= f.submit "Continue", class: "btn btn-primary" %>
</div>
<% end %>
below is my controller:-
class BooksController < ApplicationController
def new
#book = Book.new
$college_id = Category.where(category_name: ['college']).select(:id)
$school_id = Category.where(category_name: ['school']).select(:id)
end
def create
if user_signed_in?
#book = Book.new(book_params)
#last_commision = Commision.last
#book_status = BookStatus.find_by(status_name: "pending")
#book.update_attributes( commision_id: #last_commision.id, book_status_id: #book_status.id, user_id: current_user.id, quantity: "1")
if #book.save
# exit
session[:book_id] = #book.id
# session[:photo_id] = #book_photo.id
redirect_to multi_steps_path
else
respond_to do |format|
format.html { render :new }
format.json { render json: #book.errors, status: :unprocessable_entity }
end
end
else
session[:book_details] = book_params
redirect_to unauthenticated_root_path, notice: "For post your book first login in our system."
end
end
def stream_change_course
#stream_details=CourseStandard.where(stream_id: params[:stream], category_id: $college_id)
render :json => #stream_details
end
def course_change_college_subject
#year_semester=CourseStandard.where(id: params[:course])
#subject_details=Subject.where(course_standard_id: CourseStandard.where(category_id: $college_id))
#college_details=CollegeSchool.where(course_standard_id: CourseStandard.where(category_id: $college_id))
render :json => {:subject => #subject_details,:college => #college_details,:year =>#year_semester}
end
def price_calculate_commission
#final_amount=params[:price].to_i-(params[:price].to_i*Commision.last.percentage)/100
render :json => #final_amount
end
private
def book_params
params.require(:book).permit(:book_id, :user_id, :book_name, :book_condition_id,:price_for_sale, :mrp, :book_auther, :isbn, :description, :publish_year, :edition, :publication, :book_status_id, :quantity, :category_id, :university_board_id, :college_school_id, :course_standard_id, :subject_id, :commision_id, :stream_id, :medium_id,:year_semester) rescue {}
# params.require(:book).permit(:stream_id, :university_board_id, :course_standard_id, :subject_id, :id)
end
def book_photo_params
params.require(:book_photo).permit(:photo) rescue {}
end
end
I'm guessing from your error that your WishList model has a field for book ids. It shouldn't. Your BookWishListCustomization model's book id field and wishlist id field along with the belongs_to and has_many associations your models have now are enough.

RoR wrong number of arguments (0 for 1) form

Hi I'm trying to create a form, that at the same time, creates a list and associates products to it.
The problem is that the form keeps raising
wrong number of arguments (0 for 1)
Extracted source (around line #10):
7: <%= f.text_area :description, placeholder:
8: "Compose a description for it ..." %>
9: </div>
10: <%= l.fields_for :products do |builder| %>
11: <%= render 'shared/product_form', :l => builder %>
12: <% end %>
13: <%= l.submit "Create", class: "btn btn-large btn-primary" %>
App Trace is
app/views/shared/_list_form.html.erb:10:in `block in _app_views_shared__list_form_html_erb__184644094_33330696'
app/views/shared/_list_form.html.erb:1:in `_app_views_shared__list_form_html_erb__184644094_33330696'
app/views/lists/new.html.erb:7:in `_app_views_lists_new_html_erb__973495114_33282228'
The code is as follows:
---view----
--list_form--
<%= form_for(#list) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_field :name, placeholder:
"Come up with a name for your list" %>
<%= f.text_area :description, placeholder:
"Compose a description for it ..." %>
</div>
<%= f.fields_for :products do |builder| %>
<%= render 'shared/product_form', :f => builder %>
<% end %>
<%= f.submit "Create", class: "btn btn-large btn-primary" %>
<% end %>
--product_form--
<%= f.text_field :name, "Name:" %>
<%= f.text_area :description, :rows => 3 %>
---model---
--list--
class List < ActiveRecord::Base
attr_accessible :description, :name
belongs_to :user
has_many :products, :dependent => :destroy
accepts_nested_attributes_for :products, :reject_if => lambda { |a| a[:name].blank? }, :allow_destroy => true
has_many :list_categorization
has_many :category, :through => :list_categorization
validates :user_id, presence: true
validates :name, presence: true, length: {maximum: 10}
validates :description, length: {maximum: 140}
default_scope order: 'lists.created_at DESC'
def categorize!(category_id)
list_categorization.create!(category_id: category_id)
end
end
--product--
class Product < ActiveRecord::Base
attr_accessible :description, :donated, :name
validates :list_id, presence: true
belongs_to :list
end
---controllers---
--list_controller--
def new
#list = List.new
#products = #list.products.build
end
def create
#list = current_user.lists.build(params[:list]) if signed_in?
if #list.save
flash[:success] ="List " + #list.name + "created!"
render 'new'
end
--product_controller--
def new
#product = Product.new
end
def create
#product = #product.build(params[:product]) if signed_in?
if #product.save
flash[:success] ="Product " + #product.name + "created!"
end
You were right, I actually realized it after posting this, but now while trying to submit the form this happens:
The form contains 1 error.
* Name can't be blank
event tough I filled it correctly, this is what is getting passed
--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess
utf8: ✓
authenticity_token: 38CXjVORlj2RBgoTetIMoHomcVgOIlBU5rW3NTgkRkU=
list: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
name: list
description: this is a list
products_attributes: !ruby/hash:ActiveSupport::HashWithIndifferentAccess
'0': !ruby/hash:ActiveSupport::HashWithIndifferentAccess
name: p1
description: this is a product
commit: Create
action: create
controller: lists
Where did that l come from? I'm pretty sure you need to change it to f:
<%= form_for(#list) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<div class="field">
<%= f.text_field :name, placeholder: "Come up with a name for your list" %>
<%= f.text_area :description, placeholder: "Compose a description for it ..." %>
</div>
<%= f.fields_for :products do |builder| %>
<%= render 'shared/product_form', :l => builder %>
<% end %>
<%= f.submit "Create", class: "btn btn-large btn-primary" %>
<% end %>
Update
There are a few problems with your code. First of all when you call #list = current_user.lists.build(params[:list]) if signed_in? it means that if there is no user signed in that object won't be created at all. The proper way to do something like this would be with a before_filter in your controller.
Secondly #product = #product.build(params[:product]) won't work. You haven't initialized a Product object yet, and you haven't assigned it to #product yet. Also build is used for associations. You need to change this to #product = Product.new(params[:product]).
Lists controller:
before_filter :user_signed_in? # add to products controller as well
# if you need this filter only on certain actions then do:
# before_filter :user_signed_in?, only: [:new, :create]
def new
#list = current_user.lists.build
#products = #list.products.build
end
def create
#list = current_user.lists.build(params[:list])
if #list.save
flash[:success] = "List " + #list.name + " created!"
redirect_to lists_path # this part was missing!
else # this was also missing
render 'new'
end # you had an 'if' with no 'end'
end
private
# add the following to Products controller as well, or if you
# use it a lot then place it in your application controller
def user_signed_in?
unless signed_in?
flash[:notice] = "You must first sign in"
redirect_to sign_in_path
end
end
Products controller:
def new
#product = Product.new
end
def create
#product = Product.new(params[:product]
if #product.save
flash[:success] = "Product " + #product.name + " created!"
redirect_to #product
else
render 'new'
end
end
As far as I remember however, the products#create action won't be used when saving a product through a nested form, the lists#create action will be used for both.
To learn more about nested forms have a look at these railscasts.
Once you've updated your code and gone through those videos, if you're still getting errors I would recommend to create a new question since this one is getting long and messy already :)
you forgot to do this:
rails generate migration add_remember_token_to_users

Resources