所以我不确定如何实现这一点,或者它是否可能并且很难找到类似的例子/答案。但是有没有办法在提交之前在之前的形式中动态加载模型关联,就像不知道实例变量会提前是什么一样?
我的模型如下:
Property
has_many :owners, inverse_of: :property
has_many :orders
Owners
belongs_to :property
Orders
belongs_to :property
has_many :owners, through: :property
在我的 orders.new
表单中,我选择了一个框来选择一个属性来为新订单创建关联(使用Select2插件):
<%= bootstrap_form_for(@orders, ...) do |f| %>
...
<%= f.select :property_id, options_from_collection_for_select(Property.all, "id", "full_address"),
{ label: "Property:", include_blank: true }, { id: "orderPropSelect", data: { placeholder: "Select a Property"} } %>
...
<% end %>
因此,当有人为新订单选择property
时,是否可以在提交表单之前加载已与其关联的owners
属性?即使只是能够看到已经存在的所有者是可以的(能够编辑它们会更好但我意识到这可能更复杂)
以下代码当然不起作用,但需要寻找以下内容:
<%= f.static_control label: "Property Owners" do %>
<% :property_id.owners.each do |owner| %>
<%= owner.name %>
<% end %>
<% end %>
我尝试了fields_for
的变体,但我不知道如何告诉嵌套字段是基于上面select
中选择的内容(当然根据什么来加载不同的所有者)我选择了property
。我得到的错误是undefined method owners for nil:NilClass
这是合适的,因为我知道我不知道哪里可以正确看待。
这是可能的,如果是这样,我将如何实现这一目标?
(我使用bootstrap表单gem,任何人都想知道表单语法。我也有cocoon加载其他表单所以,如果有一种方法使用它,那么我不反对。)
更新工作代码,针对无关条件稍作修改。
$("#orderPropSelect").off().on('change', function() {
var id = $(this).val();
console.log(id);
if (id !== '') {
$.ajax({
url: '/properties/' + id + '/owners',
dataType: "json",
success: function (data) {
owners_html = '';
$.each(data['owners'], function () {
owners_html += '<p>' + this + '</p>';
});
if (owners_html === '') {
$("#propOwnersShow").html('<p>No owner added yet.</p>');
} else {
$("#propOwnersShow").html($(owners_html));
}
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log(errorThrown);
}
});
} else {
$('#propOwnersShow').html('Please select a Property below to view its associated owners.')
}
});
答案 0 :(得分:1)
您需要确保它与您的基础路线相匹配。东西,并且可能处理没有所有者的情况,所以隐藏所有者div。如果你想做更复杂的事情,你可以代替.pluck
构建一个更好的数组/哈希值,你可以用它来构建你可以与之交互的元素(例如从列表中删除它们) )
# your property controller
before_action :allowed, only [:owners]
def owners
owners = Property.find(params[:id]).owners.pluck(:name)
respond_to |format|
format.json { render json: { owners: owners, success: true } }
end
end
def allowed
# logic to define if this user is allowed to request this data, if you have devise, it could be
user_signed_in? && request.xhr?
end
# your routes.rb
get "properties/:id/owners", to: "properties#owners", as: "property_owners"
# or if you have resources
resources :properties do
member do
get :owners
end
end
# js file
$("#property_id").off().on('change', function() {
var id = $(this).val();
$.ajax({
url: '/properties/'+id+'/owners',
dataType: "json",
success: function(data) {
owners_html = '';
$.each(data['owners'], function() {
owners_html += '<p>'+this+'</p>';
});
$("selector_where_you_want_to_show_owners").html($(owners_html));
},
error: function(XMLHttpRequest, textStatus, errorThrown) {
console.log(errorThrown);
}
});
}
<强>更新强>
您可以通过使用find_by并确保始终返回[]
来防止没有所有者的问题,这样也可以简化前端的逻辑。
# on your controller instead use:
def owners
# .find_by will return "nil" if no properties are found, which
# then we can leverage with .try. .try will "try" calling those
# chained methods without raising an error. It usually returns
# "nil" if the parent is "nil" but .try with .pluck though will
# always return an [] no matter what the ancestors returned.
owners = Property.find_by(id: params[:id]).try(:owners).try(:pluck, :name]
# actually looking at your relationships it seems you could instead
# .where, since that will return an empty array, like so:
# owners = Owner.where(property_id: params[:id]).pluck(:name)
# this only does one database query where the other one does 2
# here we're simply making sure we add something to the array so
# that then on the front-end you can always deal with an array
# without worrying with the "no properties found". .blank? will
# return "true" if the value is nil, empty string, empty hash or
# empty array which works fine for this. So if there's no value in
# the array we add the "no owners" string.
owners << 'No owner added yet.' if owners.blank?
respond_to |format|
format.json { render json: { owners: owners, success: true } }
end
end
# And then since you'll always be returning an array you no longer
# have to worry about an empty array and your ajax function will
# look like this:
$.ajax({
url: '/properties/' + id + '/owners',
dataType: "json",
success: function (data) {
owners_html = '';
$.each(data['owners'], function () {
owners_html += '<p>' + this + '</p>';
});
$("#propOwnersShow").html($(owners_html));
},
error: function (XMLHttpRequest, textStatus, errorThrown) {
console.log(errorThrown);
}
});
希望这有帮助