Ruby On Rails, dynamic table’s & add button using nested_attributes


So here’s the problem, you want to create several items that belong to one item, normally you would just the the has_many and belongs_to (OneToMany) relation in rails. Using this model you create the primary model (such as purchase order) then create several sub items (such as item) and attach them to the primary model. The problem with this is its time consuming and does not always make sense to do things this way, take the Purchase order and Items scenario, it makes much more sense to create the Purchase order and assciated Items in the same form. This is possible in rails uing nested_attributes.

For this tutorial I’m going to be adding the sub-model Items to the Primary model Purchase Order, this will allow the Purchase order to have several items attached to it. To start with I created a simple Purchase order scaffold

Rails generate scaffold PurchaseOrder name:string

Now we need to add create the Items model:

Rails generate scaffold Item part_no:string description:string qty:string price:string

After we have created both models its time for the nested_attributes asscoitain, this will allow us to creat several items at the same time and the purchase order, to do this edit your Purchase order model to look like this:

“app/models/purchase.rb”

class Purchase < ActiveRecord::Base
  has_many :items
  
  accepts_nested_attributes_for :items, :allow_destroy => true
end

Now we need to edit the controller to manage the nested_attributes and dynamic table, add the folowing code the the new, create and update actions in the Purchase controller




“app/controllers/purchases_controller.rb”

def new
@purchase = Purchase.new

@purchase.items.build

respond_to do |format|
format.html # new.html.erb
format.json { render json: @purchase }
end
end

def create
@purchase = Purchase.new(params[:purchase])

if params[:add_item]=="Add item"
@purchase.items.build
render :action => 'edit'
else
respond_to do |format|
if @purchase.update_attributes(params[:purchase])
format.html { redirect_to @purchase, notice: 'Purchase was successfully updated.' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: @purchase.errors, status: :unprocessable_entity }
end
end

def update
@purchase = Purchase.find(params[:id])
if params[:add_item]=="Add item"
@purchase.items.build
render :action => 'edit'
else
respond_to do |format|
if @purchase.update_attributes(params[:purchase])
format.html { redirect_to @purchase, notice: 'Purchase was successfully updated.' }
format.json { head :ok }
else
format.html { render action: "edit" }
format.json { render json: @purchase.errors, status: :unprocessable_entity }
end
end
end
end

Now add the following to the form view:

“app/views/purchases/_form.html.erb”

       <%= f.fields_for :items do |builder| %>
        
        <%= f.text_field :part_no, :style=>"width:70px;", :disabled=>@disabled %>
        <%= f.text_field :description, :disabled=>@disabled %>
        <%= f.text_field :quantity, :style=>"width:30px;", :disabled=>@disabled %>
        <%= f.text_field :price,:style=>"width:50px;", :disabled=>@disabled %>
        
     <% end %>

No restart your application and navigate to your Purchases model and create a new item (http://127.0.0.1:3000/purchases/new), now you will see your form with a dynamic sub from where you can create severl of the sub model, i.e. Items.

If you have an any problems or comments please leave them below

, , , , ,

Comments are closed.