Маркировка многоуровневых вложенных форм как «грязных» в Rails - PullRequest
5 голосов
/ 12 марта 2010

У меня есть многоуровневая многоуровневая форма в Rails. Установка выглядит следующим образом: проекты имеют много этапов, а этапы имеют много примечаний. Цель состоит в том, чтобы сделать все редактируемые на странице с помощью JavaScript, где мы можем добавить несколько новых этапов в проект на странице и добавить новые примечания к новым и существующим этапам.

Все работает, как и ожидалось, за исключением того, что когда я добавляю новые заметки в существующий Веха (новые вехи работают нормально при добавлении к ним заметок), новые заметки не сохранятся, пока я не отредактирую ни одну поля, которые на самом деле принадлежат Milestone, чтобы пометить форму как «грязную» /edited.

Есть ли способ пометить Веху, чтобы новые добавленные заметки сохранялись?

Редактировать: извините, трудно вставить весь код, потому что есть так много частей, но здесь идет:

Модель

class Project < ActiveRecord::Base
  has_many :notes, :dependent => :destroy
  has_many :milestones, :dependent => :destroy

  accepts_nested_attributes_for :milestones, :allow_destroy => true
  accepts_nested_attributes_for :notes, :allow_destroy => true, :reject_if => proc { |attributes| attributes['content'].blank? }
end

class Milestone < ActiveRecord::Base
  belongs_to :project
  has_many :notes, :dependent => :destroy

  accepts_nested_attributes_for :notes, :allow_destroy => true, :allow_destroy => true, :reject_if => proc { |attributes| attributes['content'].blank? }
end

class Note < ActiveRecord::Base
  belongs_to :milestone
  belongs_to :project

  scope :newest, lambda { |*args| order('created_at DESC').limit(*args.first || 3) }
end

Для этого я использую ненавязчивую версию комбинированного помощника Райана Бейтса / JS-кода на основе jQuery.

Помощник по применению

def add_fields_for_association(f, association, partial)
  new_object = f.object.class.reflect_on_association(association).klass.new
  fields = f.fields_for(association, new_object, :child_index => "new_#{association}") do |builder|
    render(partial, :f => builder)
  end
end

Я отображаю форму для ассоциации в скрытом div, а затем использую следующий JavaScript, чтобы найти его и добавить по мере необходимости.

JavaScript

function addFields(link, association, content, func) {
    var newID = new Date().getTime();
    var regexp = new RegExp("new_" + association, "g");
    var form = content.replace(regexp, newID);
    var link = $(link).parent().next().before(form).prev();
    if (func) {
        func.call();
    }
    return link;
}

Я предполагаю, что единственный другой соответствующий кусок кода, о котором я могу подумать, это метод create в NotesController:

def create
  respond_with(@note = @owner.notes.create(params[:note])) do |format|
    format.js   { render :json => @owner.notes.newest(3).all.to_json }
    format.html { redirect_to((@milestone ? [@project, @milestone, @note] : [@project, @note]), :notice => 'Note was successfully created.') }
  end
end

@owner ivar создается перед фильтром:

def load_milestone
  @milestone = @project.milestones.find(params[:milestone_id]) if params[:milestone_id]
end

def determine_owner
  @owner = load_milestone || @project
end

Дело в том, что все это работает нормально, за исключением случаев, когда я добавляю новые заметки к существующим вехам. Веху нужно «коснуться», чтобы сохранить новые заметки, иначе Rails не обратит внимания.

Ответы [ 2 ]

2 голосов
/ 26 июня 2010

Это ошибка # 4242 в Rails 2.3.5 и исправлено в Rails 2.3.8.

0 голосов
/ 13 марта 2010

Я думаю, что ваши модели не правы. примечания не имеют прямого отношения к проекту. они через вехи.

попробуйте эти

class Project < ActiveRecord::Base
  has_many :milestones, :dependent => :destroy
  has_many :notes, :through => :milestones
  accepts_nested_attr ibutes_for :milestones, :allow_destroy => true
end

class Milestone < ActiveRecord::Base
  belongs_to :project
  has_many :notes, :dependent => :destroy

  accepts_nested_attributes_for :notes, :allow_destroy => true, :reject_if => proc { |attributes| attributes['content'].blank? }
end

class Note < ActiveRecord::Base
  belongs_to :milestone
end

Обновление: вот код, который работал для меня на основе новой информации:

## project controller

# PUT /projects/1
def update
  @project = Project.find(params[:id])

  if @project.update_attributes(params[:project])
    redirect_to(@project)
  else
    render :action => "edit"
  end
end

# GET /projects/1/edit
def edit
  @project = Project.find(params[:id])
  @project.milestones.build
  for m in @project.milestones
    m.notes.build
  end
  @project.notes.build
end

## edit.html.erb
<% form_for(@project) do |f| %>
  <%= f.error_messages %>

  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <% f.fields_for :notes do |n| %>
      <p>
        <div>
          <%= n.label :content, 'Project Notes:' %>
          <%= n.text_area :content, :rows => 3 %>
        </div>
      </p>
  <% end %>
  <% f.fields_for :milestones do |m| %>
      <p>
        <div>
          <%= m.label :name, 'Milestone:' %>
          <%= m.text_field :name %>
        </div>
      </p>
      <% m.fields_for :notes do |n| %>
          <p>
            <div>
              <%= n.label :content, 'Milestone Notes:' %>
              <%= n.text_area :content, :rows => 3 %>
            </div>
          </p>
      <% end %>
  <% end %>
  <p>
    <%= f.submit 'Update' %>
  </p>
<% end %>
...