Недавно я попытался реализовать каскадное раскрывающееся меню в моем приложении с помощью этого руководства на веб- сайте petermac.com: http://www.petermac.com/rails-3-jquery-and-multi-select-dependencies/
В учебнике в основном говорится о создании каскадного выпадающего списка, где каждый раскрывающийся список является отдельным частичным и загружается с событием jQuery onChange при изменении родительского выбора.
Теперь я получил это, чтобы работать без проблем. Но на самом деле мои выборочные блоки довольно усложняют отношения между ними.
Итак, форма я принадлежит модели под названием AuditFunction, и, как сказано в названии, для аудита. Каждый аудит имеет источник и цель, которые можно сравнить с помощью нескольких команд. Источник, а также цель выбираются через 3 блока выбора. В первом окне выбирается тип базы данных, в которой находится поле. Второй блок выбирает таблицу, а затем третий поле выбирает поле. Поскольку полевое поле может содержать тысячи параметров, я попытался реализовать каскадное раскрывающееся меню, чтобы облегчить пользователю выбор поля.
Чтобы дать вам обзор, вот как выглядят мои действия:
# new.html.erb
<%= simple_form_for @audit_function do |f| %>
<%= f.input :database_1, :as => :select, :collection => @databases, :include_blank => true %>
<%= render :partial => 'databases_1' %>
<%= render :partial => 'fields_1' %>
<%= f.input :database_2, :as => :select, :collection => @databases, :include_blank => true %>
<%= render :partial => 'databases_2' %>
<%= render :partial => 'fields_2' %>
<% end %>
Javascript для этого выглядит так:
# jQuery
<script type='text/javascript' charset='utf-8'>
jQuery(function($) {
// when the #country field changes
$("#audit_function_database_1").change(function() {
var database_1 = $('select#audit_function_database_1 :selected').val();
if(database_1 == "") database_1="0";
jQuery.get('/audit_functions/update_database_1_id_select/' + database_1, function(data){
$("#database_1_id").html(data);
})
return false;
});
})
</script>
<script type='text/javascript' charset='utf-8'>
jQuery(function($) {
// when the #country field changes
$("#audit_function_database_2").change(function() {
var database_2 = $('select#audit_function_database_2 :selected').val();
if(database_2 == "") database_2="0";
jQuery.get('/audit_functions/update_database_2_id_select/' + database_2, function(data){
$("#database_2_id").html(data);
})
return false;
});
})
Теперь я только покажу вам частичные данные для database_1_id и field_1_id, но они выглядят так же, как база данных и поле 2.
# _databases_1.html.erb
<script type="text/javascript">
jQuery(function($) {
$("#audit_function_database_1_id").change(function() {
var database_1_id = $('select#audit_function_database_1_id :selected').val();
if(database_1_id == "") database_1_id="0";
jQuery.get("/audit_functions/update_field_1_id_select/" + ("<%= params[:id] %>_" + database_1_id), function(data){
$("#field_1_id").html(data);
})
return false;
});
})
</script>
<%= simple_form_for "audit_function" do |f| %>
<% if params[:id] %>
<% if params[:id] == "imp" %>
<%= f.input :database_1_id, collection: AdOriTbl.all.order(ori_filename: :asc).collect{ |a| [a.ori_filename,a.id]} %>
<% elsif params[:id] == "ori" %>
<%= f.input :database_1_id, collection: AdOriTbl.all.order(otb_filename: :asc).collect{ |a| [a.otb_filename,a.id]} %>
<% elsif params[:id] == "mod" %>
<%= f.input :database_1_id, collection: AdQryMod.all.order(qry_mod_text: :asc).collect{ |a| [a.qry_mod_text,a.id]} %>
<% end %>
<% end %>
<% end %>
И теперь файл, содержащий целевое поле.
# _fields_1.html.erb
<%= simple_form_for "audit_function" do |f| %>
<% if params[:id] %>
<% if params[:id].gsub(/_{1}\d{1,}\z/, "") == " mod " %>
<%= f.input :field_1_id, collection: AdQryFld.where(ad_qry_mod_id: params[:id].gsub(/\A\w{1,}_{1}/, "").to_i).order(order_id: :asc).collect{ |f| [f.qry_field_text,f.id]} %>
<% else %>
<%= f.input :field_1_id, collection: AdOriFld.where(ad_ori_tbl_id: params[:id].gsub(/\A\w{1,}_{1}/, "").to_i).order(id: :asc).collect{ |f| [f.otb_colhdg,f.id]} %>
<% end %>
<% end %>
<% end %>
Затем контроллер содержит все действия, инициированные в javascripts:
# audit_function_conroller.rb
def new
authorize! :new, :audit_functions
@audit_function = AuditFunction.new
@functions = [[I18n.t("text sum"),"sum"],[I18n.t("text quantity"),"quantity"],[I18n.t("text largest_value"),"largest_value"],[I18n.t("text smallest_value"),"smallest_value"]]
@databases = [[I18n.t("text original_database"),"imp"],[I18n.t("text archive_database"),"ori"],[I18n.t("text query_database"),"mod"]]
end
def update_database_1_id_select
if params[:id] == "mod"
type = "mod"
elsif params[:id] == "ori"
type = "ori"
elsif params[:id] == "imp"
type = "imp"
end
render partial: "databases_1", id: type
end
def update_field_1_id_select
type = params[:id]
render partial: "fields_1", id: type
end
Теперь, как грязно, как все это выглядит, хорошо, что он выполняет свою работу. И чтобы прояснить мой MVC, это отношения:
AdOriTbl has_many AdOriFlds
AdOriFld belongs_to AdOriTbl
AdQryMod has_many AdQryFlds
AdQryFld belongs_to AdQryMod
Надеюсь, что имена вас не слишком беспокоят, читая это.
Теперь вернемся к проблеме:
Как я уже сказал, этот код работает для создания нового объекта, и все выбрано отлично. Но когда я пытаюсь редактировать объект, заполняется только поле с типом базы данных (database_1 и database_2). Ящики выбора для идентификатора баз данных не отображаются, а поля для полей -. Но все четыре поля идентификатора пустые.
Теперь я уже пытался заполнить поля вручную jQuery, который в основном похож на те, которые у меня уже есть, но вместо того, чтобы запускать onChange, я запускаю его, когда моя функция audit_function имеет database_id и отображает поле выбора и заполняет его значение по значению database_id. Это тоже работает.
Проблема в том, что я не могу сделать это с помощью field_id, потому что в частичном файле database_1_id, где jQuery для полей запускается, у меня нет объекта @audit_function, а также, похоже, мешает другим javascripts,
Кроме того, мне также хотелось бы думать, что есть лучший способ сделать это, а затем мой путь. Но я уже пробовал другие учебники и способы, и они либо не работают, когда у вас нет прямых отношений с Country-State-City, либо они не работают при редактировании.
Таким образом, любая помощь будет действительно оценена. Благодарю!
Я взял следующий учебник в качестве шаблона для перезаписи моего каскадного раскрывающегося списка:
http://homeonrails.blogspot.de/2012/01/rails-31-linked-dropdown-cascading.html
Итак, теперь я бросаю все разные модели в один массив и фильтрую его, добавляя имена в класс, чтобы различать не только идентификатор, но и по имени. Также плагин jQuery clainedTo делает код более читаемым.
Итак, контроллер теперь выглядит следующим образом:
@types_for_dropdown = [[I18n.t("text archive_database"),"ori"],[I18n.t("text query_database"),"mod"]]
@tables_for_dropdown = []
@ad_qry_mods = AdQryMod.all
@ad_qry_mods.each do |i|
@tables_for_dropdown = @tables_for_dropdown << [i.qry_mod_text,"mod#{i.id}",{:class => "mod"}]
end
@ad_ori_tbls = AdOriTbl.all
@ad_ori_tbls.each do |i|
@tables_for_dropdown = @tables_for_dropdown << [i.otb_filename,"ori#{i.id}",{:class => "ori"}]
end
@fields_for_dropdown = []
@ad_qry_flds = AdQryFld.all
@ad_qry_flds.each do |i|
@fields_for_dropdown = @fields_for_dropdown << [i.qry_fieldname,i.id,{:class => "mod#{i.ad_qry_mod_id}"}]
end
@ad_ori_flds = AdOriFld.all
@ad_ori_flds.each do |i|
@fields_for_dropdown = @fields_for_dropdown << [i.otb_fieldname,i.id,{:class => "ori#{i.ad_ori_tbl_id}"}]
end
И форма выглядит так:
<%= content_for :head do %>
<script>
$(document).ready(function(){
$('select#audit_function_database_1_id').chainedTo('select#audit_function_database_1');
$('select#audit_function_field_1_id').chainedTo('select#audit_function_database_1_id');
$('select#audit_function_database_2_id').chainedTo('select#audit_function_database_2');
$('select#audit_function_field_2_id').chainedTo('select#audit_function_database_2_id');
});
</script>
<% end %>
<div class="grid-6-12">
<%= f.input :database_1, label: I18n.t("field_label audit_function database_1"), hint: I18n.t("field_hint audit_function database_1"), as: :select, collection: @types_for_dropdown, include_blank: true %>
</div>
<div class="grid-6-12">
<%= f.input :database_2, label: I18n.t("field_label audit_function database_2"), hint: I18n.t("field_hint audit_function database_2"), as: :select, collection: @types_for_dropdown, include_blank: true %>
</div>
<div class="grid-6-12">
<%= f.input :database_1_id, label: I18n.t("field_label audit_function database_1_id"), hint: I18n.t("field_hint audit_function database_1_id"), as: :select, collection: @tables_for_dropdown, include_blank: true %>
</div>
<div class="grid-6-12">
<%= f.input :database_2_id, label: I18n.t("field_label audit_function database_2_id"), hint: I18n.t("field_hint audit_function database_2_id"), as: :select, collection: @tables_for_dropdown, include_blank: true %>
</div>
<div class="grid-6-12">
<%= f.input :field_1_id, label: I18n.t("field_label audit_function field_1_id"), hint: I18n.t("field_hint audit_function field_1_id"), as: :select, collection: @fields_for_dropdown, include_blank: true %>
</div>
<div class="grid-6-12">
<%= f.input :field_2_id, label: I18n.t("field_label audit_function field_2_id"), hint: I18n.t("field_hint audit_function field_2_id"), as: :select, collection: @fields_for_dropdown, include_blank: true %>
</div>
Это действительно приятное решение, и я могу рекомендовать его всем!