Работя върху въвеждането на вложена форма в моето приложение след епизод 196 на Railscast http://railscasts.com/episodes/196-nested-model-form-revised и ремастерираната версия за rails 4 https://github.com/dnewkerk/nested-model-form.
Да приемем, че имаме връзка 1 към много между разписки и статии.
Ето как изглеждат техните модели:
receipt.rb:
class Receipt < ActiveRecord::Base
has_many :articles, dependent: :destroy
accepts_nested_attributes_for :articles, allow_destroy: true, reject_if: :all_blank
belongs_to :shop
belongs_to :user
def display_name
self.name
end
end
article.rb:
class Article < ActiveRecord::Base
belongs_to :receipt
def name_with_brand
"#{name} #{brand}"
end
end
Ето как изглежда receipts_controller.rb:
class ReceiptsController < ApplicationController
before_action :set_shop, only: [:show, :edit, :update, :destroy]
respond_to :html, :xml, :json
def index
@receipts = current_user.receipts
respond_with(@receipts)
end
def show
respond_with(@receipt)
end
def new
@receipt = Receipt.new
2.times do
@receipt.articles.build
end
respond_with(@receipt)
end
def edit
end
def create
@receipt = Receipt.new(receipt_params)
user_id = current_user.id
@receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = @receipt.shopping_date.advance(months: warranty_time)
end
@receipt.user_id = user_id
@receipt.save
respond_with(@receipt)
end
def update
if @receipt.update(receipt_params)
redirect_to @receipt, notice: "Successfully updated receipt."
else
render :edit
end
end
def destroy
@receipt.destroy
respond_with(@receipt)
end
private
def set_shop
@receipt = Receipt.find(params[:id])
end
def receipt_params
params.require(:receipt).permit(:name, :shopping_date, :shop_id, :file,
articles_attributes: [:id, :name, :brand, :warranty_time, :warranty_expires,
:receipt_id, :_destroy])
end
end
Ето как изглежда моят receipts.js.coffee:
jQuery ->
$('#receipt_shopping_date').datepicker(dateFormat: 'yy-mm-dd')
$.datepicker.setDefaults($.datepicker.regional['PL']);
$('form').on 'click', '.remove_fields', (event) ->
$(this).prev('input[type=hidden]').val('1')
$(this).closest('fieldset').hide()
event.preventDefault()
$('form').on 'click', '.add_fields', (event) ->
time = new Date().getTime()
regexp = new RegExp($(this).data('id'), 'g')
$(this).before($(this).data('fields').replace(regexp, time))
event.preventDefault()
$(document).ready(jQuery)
$(document).on('page:load', jQuery)
И накрая ето моето виждане за добавяне на нова разписка и добавяне на статии към нея:
(other fields...)
<div class="large-12 columns">
<p>Add articles on the receipt:</p>
</div>
<div class="field">
<div class="large-12 columns">
<%= f.fields_for :articles do |builder| %>
<div class="article_fields">
<%= render "article_fields", :f => builder %>
</div>
<% end %>
<%= link_to_add_fields "Add another article", f, :articles %>
</div>
</div>
<div class="actions">
<div class="large-12 columns">
<%= f.submit "Sumbit Receipt" %>
</div>
</div>
<% end %>
Както можете да видите, използвам помощен метод link_to_add_fields, ето как изглежда:
def link_to_add_fields(name, f, association)
new_object = f.object.send(association).klass.new
id = new_object.object_id
fields = f.fields_for(association, new_object, child_index: id) do |builder|
render(association.to_s.singularize + "_fields", f: builder)
end
link_to(name, '#', class: "add_fields small button", data: {id: id, fields: fields.gsub("\n", "")})
end
И накрая, както можете да видите, генерирам частично, наречено _article_fields.html.erb, ето как изглежда:
<fieldset style="width:1400px">
<legend>new article</legend>
<div class="large-2 columns">
<%= f.text_field :name%>
</div>
<div class="large-2 columns">
<%= f.text_field :brand%>
</div>
<div class="large-2 columns">
<%= f.text_field :warranty_time, class: "warranty" %>
</div>
<div class="large-12 columns">
<%= link_to "delete article", '#', class: "remove_fields button small alert" %>
</div>
</fieldset>
А сега да се заемем с моя проблем. Когато създавам разписка за първи път, всичко е наред - виждам броя на артикулите в разписка в моя изглед на шоуто и warranty_expires във всяка статия.
Нещата се объркват, когато актуализирам или изтривам article_fields чрез разписки/редактиране:
1) Когато редактирам разписка и искам да премахна някоя от статиите (въпреки че визуално в моя изглед за редактиране те изчезват - JS изглежда работи), полетата не се премахват от моята БД, като по този начин изгледът за показване остава точно същият като беше преди.
Прост пример:
преди редакция: разписката ми има 6 артикула
по време на редактиране: натисна 3 пъти бутона „изтриване на статия“, така че разписката трябва да има 3 статии
след редакция: разписката все още има 6 статии
2) Когато редактирам касова бележка и искам да добавя поле за друга статия, стойността warranty_expires винаги е нула - как мога да я накарам да работи с действието за актуализиране в моя контролер за касови бележки? Опитах да използвам същия код като в моето действие за създаване:
@receipt.articles.each do |article|
warranty_time = article.warranty_time
article.warranty_expires = @receipt.shopping_date.advance(months: warranty_time)
end
но няма да работи. Някаква идея защо?
Прост пример:
Разписката вече има 2 артикула. Когато добавя третия, получавам следния резултат:
3 артикула - всички имат имена и полета warranty_time, но само 2 от тях имат стойност warranty_expires.
Цялата ви помощ ще бъде високо оценена. Благодаря ви предварително.