【问题标题】:AJAX update of accepts_nested_attributes_for partialsAccepts_nested_attributes_for partials 的 AJAX 更新
【发布时间】:2010-11-17 15:36:29
【问题描述】:

我目前的工作环境是 Rails 2.3.8(我的公司没有迁移到 Rails 3 的各种原因)。 我正在尝试通过 AJAX 调用更新多模型表单的元素 - 想法是根据用户选择或填写其他字段的方式替换某些下拉列表。

我之前设法通过使用非基于表单的部分来实现此工作 - 我现在遇到的问题是当部分基于 form_for 和 fields_for 时,重现选择下拉列表的 AJAX 更新。

很抱歉下面的文字墙 - 我已尝试尽可能减少它(代码本身在我的测试站点上确实有效)。

如何在爆发控制器中生成表单构建器元素,然后将其传递给部分类别以代替事件表单?

任何指针都会很棒:D

型号

class Outbreak < ActiveRecord::Base
        has_many :incidents, :dependent => :destroy
        has_many :locations, :through => :incidents

     accepts_nested_attributes_for :locations, :allow_destroy => true, :reject_if => :all_blank
     accepts_nested_attributes_for :incidents, :allow_destroy => true, :reject_if => :all_blank
end

class Incident < ActiveRecord::Base
    belongs_to :outbreak
    belongs_to :location
    belongs_to :category
    belongs_to :subcategory
    belongs_to :subtype

end

class Location < ActiveRecord::Base
    has_many :incidents, :dependent => :destroy
     has_many :outbreaks, :thorugh => incidents
end

观看次数

_form

<% form_for(@outbreak, :html => {:multipart => true}) do |form| %>

  <%= render :partial => 'outbreak_type_select', :locals => {:outbreak_types => @outbreak_types, :f => form } %>
   <% form.fields_for :incidents do |incident_form| %>
      <%= render :partial => 'category_select', :locals => {:categories => @categories, :incident_form => incident_form} %>
      <%= render :partial => 'subcategory_select', :locals => { :subcategories => @subcategories, :incident_form => incident_form } %>

   <% end %>
<% end %>

_outbreak_type_select

<% with_str = "'outbreak_type=' + value " %>
<% if @outbreak.id %>
<% with_str << "+ '&id=' + #{outbreak.id}" %>
<% end %>
<%= f.collection_select(:outbreak_type, @outbreak_types, :property_value, :property_value, {}, {:onchange => "#{remote_function(:url  => { :action => "update_select_menus"}, :with => with_str)}"}  ) %>

_category_select

调用update_select_menus后如何生成event_form

<%= incident_form.collection_select( :category_id, @categories, :id, :name, {:prompt   => "Select a category"}, {:onchange => "#{remote_function(:url  => { :action => "update_subcategory"}, :with => "'category_id='+value")}"}) %>

RJS

   begin
    page.replace_html 'outbreak_transmission_div', :partial => 'outbreaks/transmission_mode_select', :locals => {:transmission_modes => @transmission_modes }
   rescue
    page.insert_html :bottom, 'ajax_error', '<p>Error :: transmission modes update select</p>'
    page.show 'ajax_error'
   end
   begin
    page.replace_html 'incident_category_select', :partial => 'outbreaks/category_select', :locals => { :categories => @categories }  
   rescue
    page.insert_html :bottom, 'ajax_error', '<p>Error :: incident category update select</p>'
    page.show 'ajax_error'
   end

控制器

爆发

    def new
        @outbreak = Outbreak.new

        @outbreak.incidents.build
        @outbreak.locations.build

        #just the contents for the dropdowns
        @categories = Category.find(:all, :conditions => {:outbreak_type => "FOODBORNE"}, :order => "outbreak_type ASC")
        @subcategories = Subcategory.find(:all, :order => "category_id ASC")

    end

   def update_select_menus
      @outbreak_type = params[:outbreak_type].strip
      if params[:id]
        @outbreak = Outbreak.find(params[:id])
      else
        @outbreak = Outbreak.new
        @outbreak.incidents.build
              @outbreak.locations.build       
      end

      if @outbreak_type == "FOODBORNE"
          ob_type_query = "OUTBREAKS:TRANSMISSION_MODE:" << @outbreak_type
          @transmission_modes = Property.find(:all, :conditions => {:field => ob_type_query})

          ob_type_query = "INVESTIGATIONS:CATEGORY:" << @outbreak_type
          @sample_types = Property.find(:all, :conditions => {:field => ob_type_query})
          @categories = Category.find(:all, :conditions => { :outbreak_type => "FOODBORNE"})
          @subcategories = Subcategory.find(:all, :conditions => { :category_id => @categories.first.id})
          @subtypes = Subtype.find(:all, :conditions => { :subcategory_id => @subcategories.first.id})
      elsif @outbreak_type == "NON-FOODBORNE"
          ob_type_query = "OUTBREAKS:TRANSMISSION_MODE:" << @outbreak_type
          @transmission_modes = Property.find(:all, :conditions => {:field => ob_type_query})

          ob_type_query = "INVESTIGATIONS:CATEGORY:" << @outbreak_type
          @sample_types = Property.find(:all, :conditions => {:field => ob_type_query})
          @categories = Category.find(:all, :conditions => { :outbreak_type => "NON-FOODBORNE"})
          @subcategories = Subcategory.find(:all, :conditions => { :category_id => @categories.first.id})
          @subtypes = Subtype.find(:all, :conditions => { :subcategory_id => @subcategories.first.id})
     end

     respond_to do |format|
          format.html
          format.js
      end

  end

【问题讨论】:

    标签: ruby-on-rails ajax partial nested-attributes multi-model-forms


    【解决方案1】:

    找到了基于http://www.treibstofff.de/2009/07/12/ruby-on-rails-23-nested-attributes-with-ajax-support/的变通方法

    这可能应该放在爆发助手中(在爆发控制器 atm 中)

        def update_select_menus
          @outbreak_type = params[:outbreak_type].strip
          #next_child_index will only be used if 
          @next_child_index ? params[:next_child_index] : 0
          if params[:id]
            @outbreak = Outbreak.find(params[:id])
          else
            @outbreak = Outbreak.new
            @outbreak.risks.build
            @outbreak.incidents.build
            @outbreak.locations.build
    
          end
    
          if @outbreak_type == "FOODBORNE"
              ob_type_query = "OUTBREAKS:TRANSMISSION_MODE:" << @outbreak_type
              @transmission_modes = Property.find(:all, :conditions => {:field => ob_type_query})
    
              ob_type_query = "INVESTIGATIONS:CATEGORY:" << @outbreak_type
    
              @sample_types = Property.find(:all, :conditions => {:field => ob_type_query})
              @categories = Category.find(:all, :conditions => { :outbreak_type => "FOODBORNE"})
              @subcategories = Subcategory.find(:all, :conditions => { :category_id => @categories.first.id})
              @subtypes = Subtype.find(:all, :conditions => { :subcategory_id => @subcategories.first.id})
    
    
          elsif @outbreak_type == "NON-FOODBORNE"
              ob_type_query = "OUTBREAKS:TRANSMISSION_MODE:" << @outbreak_type
              @transmission_modes = Property.find(:all, :conditions => {:field => ob_type_query})
    
              ob_type_query = "INVESTIGATIONS:CATEGORY:" << @outbreak_type
    
              @sample_types = Property.find(:all, :conditions => {:field => ob_type_query})
              @categories = Category.find(:all, :conditions => { :outbreak_type => "NON-FOODBORNE"})
              @subcategories = Subcategory.find(:all, :conditions => { :category_id => @categories.first.id})
              @subtypes = Subtype.find(:all, :conditions => { :subcategory_id => @subcategories.first.id})
         end
    
         @pathogen_types = Property.find(:all, :conditions => {:field => "PATHOGENS:CATEGORY"})
         @outbreak_types = Property.find(:all, :conditions => {:field => "OUTBREAKS:OUTBREAK_TYPE"} )
    
    
         render :update do |page|
             page.replace 'outbreak_transmission_div', :partial => 'transmission_mode_select_update'
             page.replace 'incident_category_select', :partial => 'incident_category_select_update'
             page.replace 'incident_subcategory_select', :partial => 'incident_subcategory_select_update'
             page.replace 'incident_subtype_select', :partial => 'incident_subtype_select_update'
         end      
    
      end
    

    在爆发视图中(尽管由于此部分与事件相关,它可能应该进入该视图)

        <% ActionView::Helpers::FormBuilder.new(:outbreak, @outbreak, @template, {}, proc{}).fields_for :incidents,{:child_index => @next_child_index} do |this_form| %>
    <div id="incident_category_select">
    <%= render :partial => 'category_select', :locals => {:incident_form => this_form } %>
    </div>
    <% end %>
    

    ActionView::Helpers::FormBuilder 用于生成所需的 fields_for 表单 - 网站文章对此进行了更详细的介绍。

    生成的索引由 @next_child_index 变量设置,该变量可以通过原始 AJAX 调用传递给控制器​​(例如 @next_child_index = 1,那么生成的表单元素名称将为 outbreak [incidents_attributes] [1 ] [category_id] ) - 我没有在这个例子中使用它,因为虽然在未来我希望表单在这个初始运行中支持每个爆发的多个位置,但它只会接受一个位置 - 事件每次爆发。

    _category_select.erb 部分(在爆发视图 atm 中)

    <% with_str = "'category_id=' + value " %>
    <% if @outbreak.id %>
    <% with_str << "+ '&id=' + #{@outbreak.id}" %>
    <% end %>
    <%= incident_form.collection_select( :category_id, @categories, :id, :name, {:prompt   => "Select a category"}, {:onchange => "#{remote_function(:url  => { :action => "update_subcategory"}, :with => with_str)}"}) %>
    

    with_str 只是可选地将爆发 id(如果存在)传递给控制器​​以查找爆发记录以生成表单,如果不存在则构建新的爆发和关联的嵌套属性(如事件和位置)。

    必须以更简洁的方式执行此操作 - 尤其是 FormHelper 并通过可选的字符串传递爆发 id。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多