The logic in the exercise is create a Bill where a Bill have a list of Products with an amount of them.
I have the next 3 models:
Bill
Product
BillItem (This is the intermediate model between Bill and Product for the many to many relationship)
Schema for more clarity:
create_table "bill_items", force: :cascade do |t|
t.integer "amount"
t.integer "product_id"
t.integer "bill_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["bill_id"], name: "index_bill_items_on_bill_id"
t.index ["product_id"], name: "index_bill_items_on_product_id"
end
create_table "bills", force: :cascade do |t|
t.string "user_name"
t.string "dni"
t.date "expiration"
t.float "sub_total"
t.float "grand_total"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "products", force: :cascade do |t|
t.string "name"
t.string "description"
t.float "price"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
First i have a list of created products in my database.
For my BillController i have a _form.html.erb where i want be capable to create dynamic selects for choose a product and set a amount.
My view is the next:
<%= form_for bill do |f| %>
<div>
<%= f.label :user_name %>
<%= f.text_field :user_name %>
</div>
<div>
<%= f.label :dni %>
<%= f.text_field :dni %>
</div>
<div>
<%= f.label :product %>
<%= f.collection_select :product_ids, Product.all, :id, :name %>
</div>
# Here i should add more selects if the user want to add more products.
<div><%= f.submit %></div>
<% end %>
My problem or my question is: how to "groupe" the ids of the products and the amount of them. And the another question is, i can create the other selects dynamically with JS?
You need to be adding multiple BillItem to your form to account for the amount and the product. This is done through accepts_nested_attributes_for on the model. For example:
class Bill < ActiveRecord::Base
has_many :bill_items
accepts_nested_attributes_for :bill_items
end
Initialize a BillItem on the Bill in your controller:
class BillsController < ActionController::Base
def new
#bill = Bill.new
#bill.bill_items.build
end
end
Then in your form, use the fields_for helper to create sub-forms:
<%= form_for #bill do |f| %>
<div>
<%= f.label :user_name %>
<%= f.text_field :user_name %>
</div>
<div>
<%= f.label :dni %>
<%= f.text_field :dni %>
</div>
<%= f.fields_for :bill_items do |f| %>
<div>
<%= f.label :product %>
<%= f.collection_select :product_id, Product.all, :id, :name %>
</div>
<div>
<%= f.label :amount %>
<%= f.number_field :amount %>
</div>
<% end %>
<%= f.submit %></div>
<% end %>
And yes, you can use javascript to create new nested forms.
Related
now i doing a form that can add product to outlet.This form is create at the outletproduct page
So when i click to a product info page have a button (add product to outlet) then go to the form which located at outletproduct page. Now i need to set these things
1.I have create the form out but at the product name field there i need to display product name i choose to be fixed and cannot be change.(Example, click on the product fish then click button then the product name should be fish.)
2.The selling_price and last_cost will need to show the product price and cost in the input field there but this can be change(Example, at product page price is $2 and cost is $1, then here the input field will be selling_price $2 and last_cost $1.)
i have try do but it won't work.
I have update some pic about UI.
OutletProduct Controller
class OutletProductsController < ApplicationController
def new
#outlet_product = OutletProduct.new
#product = Product.all
#outlet = Outlet.all
#category = Category.all
end
def index
end
def show
end
def create
#outlet_product = OutletProduct.new(outlet_product_params)
#category_id = Category.all
#outlet_id = Outlet.all
#product_id = Product.all
if #outlet_product.save
flash[:success] = "Succesful create!"
redirect_to #outlet_product
else
render 'new'
end
end
def edit
end
def outlet_product_params
params.require(:outlet_product).permit(:product_id, :outlet_id, :quantity,
:selling_price ,:last_cost)
end
end
new.html.erb
<h1>Add product to outlet</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_with(model: #outlet_product, local: true) do |f| %>
<%= render 'shared/error_messages', object: f.object %>
<%= f.label :product_name %>
<%= f.text_field :#product.name ,class: "form-select" %>
<%= f.label :quantity %>
<%= f.number_field :quantity%>
<%= f.label :selling_price %>
<%= f.number_field :selling_price, #product.price , class: 'form-control' %>
<%= f.label :last_cost %>
<%= f.number_field :last_cost,#product.cost, class: 'form-control' %>
<%= f.label :outlet_id %>
<%= f.select(:outlet_id, Outlet.all.collect { |l| [ l.name, l.id] }, {class: "form-select"}) %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
</div>
OutletProduct migration in schema
create_table "outlet_products", force: :cascade do |t|
t.integer "outlet_id"
t.integer "product_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.decimal "selling_price"
t.decimal "last_cost"
t.decimal "quantity"
end
product migration in schema
create_table "products", force: :cascade do |t|
t.string "name"
t.integer "quantity"
t.integer "price"
t.integer "category_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.decimal "cost"
end
Product page
Product info page]
form page
Hi you can define all the default values in view like below
<%= f.label :selling_price %>
<%= f.number_field :selling_price, 0 , class: 'form-control' %>
or if you wanted to keep it by default in the database, then you can make changes in migration files as below:
create_table "products", force: :cascade do |t|
t.string "name", default: 'fish'
t.integer "quantity", default: 0
t.integer "price"
t.integer "category_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.decimal "cost"
end
I hope this is what you are trying to achieve.
Using Rails 4 and Ruby 2.2,
I have book as model which should have image upload functionality in the show page of book. So User can create the book first and from show page upload the multiple images for book. I have used carrierwave as gem and have separate model created for Image.
image.rb
class Image < ActiveRecord::Base
belongs_to :book
mount_uploader :avatar, AvatarUploader
end
book.rb
class Book < ActiveRecord::Base
belongs_to :subject, dependent: :destroy
belongs_to :user, dependent: :destroy
has_many :images, dependent: :destroy
end
books/show.html.erb
<%= form_for(Image.new, :html => { :multipart => true }) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
<%= hidden_field_tag "image[book_id]", #book.id %>
</div>
<%= f.fields_for :images do |p| %>
<div class="field">
<%= p.label :avatar %><br>
<%= p.file_field :avatar, :multiple => true, name: "images[avatar]" %>
</div>
<%end%>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
schema.rb
create_table "images", force: :cascade do |t|
t.datetime "avatar_updated_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "book_id", limit: 4
t.string "avatar", limit: 255
t.string "name", limit: 255
end
create_table "books", force: :cascade do |t|
t.string "title", limit: 255
t.integer "page_number", limit: 4
t.text "description", limit: 65535
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id", limit: 4
t.integer "subject_id", limit: 4
t.boolean "active", default: false
end
Now I am kind of unable to proceed with this, can someone guide me on this because I am having form in books/show page and I need to show the image on the same page after successful updation of images.(Multiple images can be uploaded)
Thanks in advance
let me know if I need to provide any more information.
You can use the association itself and list all the images which have been uploaded.
<%= form_for(Image.new, :html => { :multipart => true }) do |f| %>
<div class="field">
<%= f.label :name %><br>
<%= f.text_field :name %>
<%= hidden_field_tag "image[book_id]", #book.id %>
</div>
<%= f.fields_for :images do |p| %>
<div class="field">
<%= p.label :avatar %><br>
<%= p.file_field :avatar, :multiple => true, name: "images[avatar]" %>
</div>
<% end %>
<!-- Listing all the images for a book -->
<% if #book.images.any? %>
<% #book.images.each do |img| %>
<%= image_tag(img.avatar.url) %>
<% end %>
<% end %>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
I have model Category:
class Category < ActiveRecord::Base
has_many :projects
end
Model project:
class Project < ActiveRecord::Base
belongs_to :category
validates :title, :description, :category, :skill, :payment, presence: true
validates :price, numericality: { only_integer: true }
PRICE_CATEGORIES = ['cat_price1', 'cat_price2', 'cat_price3', 'cat_price4']
end
projects/new
<%= form_for #project, url: {action: 'create'} do |f| %>
<p>
<%= f.label 'Titel' %>
<%= f.text_field :title %>
</p>
<P>
<%= f.label 'Budget' %>
<%= f.text_field :price %>
für
<%= f.select :price_category, Project::PRICE_CATEGORIES %>
</P>
<p>
<%= f.label 'Beschreibung' %>
<%= f.text_area :description %>
</p>
<p>
<%= f.label 'Kategorie' %>
<%= f.collection_select :category_id, Category.all, :id, :name %>
</p>
<P>
<%= f.submit 'erstellen' %>
</P>
<% end %>
schema.rb:
create_table "categories", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "projects", force: :cascade do |t|
t.string "title"
t.text "description"
t.integer "price"
t.string "skill"
t.string "location"
t.string "payment"
t.boolean "anonymity"
t.string "file"
t.integer "category_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "price_category"
end
When calling <%= f.collection_select :category_id, Category.all, :id, :name %>
all items in dropdown list displaying twice. Cant undestand, why.
For example:
cat1
cat2
cat3
cat1
cat2
cat3
Thank for you help.
I am trying to achieve this:
It has a model called Customer that has details of the customers name
(first, middle and last) and telephone numbers (mobile and landline).
It has a model called Address that is linked to Customer. This model
will have details for a customers address (a number of address lines,
a country and a post/zip code).
A customers controller that supports two pages. The first will list
all customers in the database along with their associated address.
The second will display a form for the entry of a new customer along
with their address and allow for the creation of a new customer and
address record. It should be possible to move from either page to the
other without having to enter an URL in the browser navigation bar.
A number of points of note about this are as follows...
This is my code:
Migration:
class CreateAddresses < ActiveRecord::Migration
def change
create_table :addresses do |t|
t.string :address_line1
t.string :address_line2
t.string :address_line3
t.string :city
t.string :county_province
t.string :zip_or_postcode
t.string :country
t.string :customer_id
t.timestamps null: false
end
create_table :customers do |t|
t.string :first_name
t.string :middle_name
t.string :last_name
t.integer :mobile_number
t.integer :landline_number
t.timestamps null: false
end
end
end
Model:
class Address < ActiveRecord::Base
end
class Customer < ActiveRecord::Base
has_one :address
end
Form:
<%= form_for([#address,#customer]) do |f| %>
<% if #customer.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(#customer.errors.count, "error") %> prohibited this customer from being saved:</h2>
<ul>
<% #customer.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :first_name %><br>
<%= f.text_field :first_name %>
</div>
<div class="field">
<%= f.label :middle_name %><br>
<%= f.text_field :middle_name %>
</div>
<div class="field">
<%= f.label :last_name %><br>
<%= f.text_field :last_name %>
</div>
<div class="field">
<%= f.label :mobile_number %><br>
<%= f.number_field :mobile_number %>
</div>
<div class="field">
<%= f.label :landline_number %><br>
<%= f.number_field :landline_number %>
</div>
<div class="field">
<%= f.label :address_line1 %><br>
<%= f.text_field :address_line1 %>
</div>
<div class="field">
<%= f.label :address_line2 %><br>
<%= f.text_field :address_line2 %>
</div>
<div class="field">
<%= f.label :address_line3 %><br>
<%= f.text_field :address_line3 %>
</div>
<div class="field">
<%= f.label :city %><br>
<%= f.text_field :city %>
</div>
<div class="field">
<%= f.label :county_province %><br>
<%= f.text_field :county_province %>
</div>
<div class="field">
<%= f.label :zip_or_postcode %><br>
<%= f.text_field :zip_or_postcode %>
</div>
<div class="field">
<%= f.label :country %><br>
<%= f.text_field :country %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
Routes:
resources :addresses do
resources :customers
end
resources :customers do
resources :addresses
end
I haven't changed anything in my both controllers.
I am getting this error: undefined method `address_line1' for #
I don't understand where i am wrong. Any help? :( please
Schema:
ActiveRecord::Schema.define(version: 20150722160725) do
create_table "addresses", force: :cascade do |t|
t.string "customer_id"
t.string "address_line1"
t.string "address_line2"
t.string "address_line3"
t.string "city"
t.string "county_province"
t.string "zip_or_postcode"
t.string "country"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "addresses", ["customer_id"], name: "index_addresses_on_customer_id"
create_table "customers", force: :cascade do |t|
t.string "first_name"
t.string "middle_name"
t.string "last_name"
t.integer "mobile_number"
t.integer "landline_number"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
Rails shows that an instance of the Comment model exists, even though the database is empty. My code to show the Comment is
<% #post.comments.each do |c| %>
<%= c %>
<% end %>
And from that, I get #<Comment:0x007fe971c02800> also if i do c.attributes I get
{"id"=>nil, "body"=>nil, "author"=>nil, "post_id"=>37, "deleted"=>nil, "locked"=>nil, "created_at"=>nil, "updated_at"=>nil}, but the table has no records. If I change that code to
<% #post.comments.each do |c| %>
<%= c.body %>
<% end %>
I get nothing.
Maybe it has something to me using closure_tree.
If it helps, here is the schema for the table:
create_table "comment_hierarchies", id: false, force: true do |t|
t.integer "ancestor_id", null: false
t.integer "descendant_id", null: false
t.integer "generations", null: false
end
add_index "comment_hierarchies", ["ancestor_id", "descendant_id", "generations"], name: "comment_anc_desc_udx", unique: true
add_index "comment_hierarchies", ["descendant_id"], name: "comment_desc_idx"
create_table "comments", force: true do |t|
t.string "body"
t.string "author"
t.string "post_id"
t.boolean "deleted"
t.boolean "locked"
t.datetime "created_at"
t.datetime "updated_at"
end
EDIT: I fixed it by changing the form on the page to make a new comment from
<%= form_for([#post, #post.comments.build], :html => {:class => 'ui form'}) do |f| %>
<div class="field">
<%= f.text_area :body, placeholder: "Comment", style: "max-height: 100px;"%>
</div>
<p>
<%= f.submit "Add Comment", class: "blue ui button" %>
</p>
<% end %>
to
<%= form_for([#post, Comment.new], :html => {:class => 'ui form'}) do |f| %>
<div class="field">
<%= f.text_area :body, placeholder: "Comment", style: "max-height: 100px;"%>
</div>
<p>
<%= f.submit "Add Comment", class: "blue ui button" %>
</p>
<% end %>
Could you be calling #post.comments.new somewhere before this to render the comment form? This could be adding the new, non-persisted record in the association_cache.
To avoid this, you should be able to instantiate the comment as Comment.new(post_id: #post.id). This shouldn't add the comment to the association_cache. If it does, you really don't need the post_id on the new Comment. You probably have it in a hidden field anyway.
It seems like there is a comment persisted in your database for this post.
c.body may return nil, it alone doesn't define that there is no comment record in your database. Maybe you had no validation or manually skipped it on the console a while ago.
c.inspect will give you a more verbose breakdown of what data is in the object.
How are you confirming your database is empty? Does #post.comments.count equal 0?