简体   繁体   English

提交前以Rails表单动态加载模型关联

[英]Load model association dynamically in Rails form before submit

So I'm not sure how to achieve this or if it's even possible and having a tough time searching for similar examples/answers. 因此,我不确定如何实现此目标,或者不确定是否有可能,并且很难寻找类似的示例/答案。 But is there a way to dynamically load a model association within a form before you submit it, like without knowing what the instance variable would be ahead of time? 但是,有没有一种方法可以提交表单之前在表单中动态加载模型关联,就像不知道实例变量会提前一样?

I have models as follows: 我的模型如下:

Property
  has_many :owners, inverse_of: :property
  has_many :orders

Owners
  belongs_to :property

Orders
  belongs_to :property
  has_many :owners, through: :property

In my orders.new form I have select boxes to choose a property to create the association for the new order (using Select2 plugin): 在我的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 %>

So when someone SELECTS a property for the new order, is there a way I can load what owners that property already has associated to it before the form is submitted? 因此,当有人为新订单选择property时,是否可以在提交表单之前加载owners属性已经与之关联的owners Even just being able to see what owners are already there is okay (being able to edit them would be even better but I realize thats probably more complex) 即使只是能够看到所有者已经存在也可以(可以对其进行编辑会更好,但我意识到那可能更复杂)

The following code of course doesn't work but looking for something along the lines of: 以下代码当然不起作用,但按照以下方式寻找东西:

<%= f.static_control label: "Property Owners" do %>
  <% :property_id.owners.each do |owner| %>
    <%= owner.name %>
  <% end %>
<% end %>

I've tried variations of fields_for but I don't know how to tell the nested fields to be based off what is chosen in the above select (of course loading different owners based on what property is chosen. The errors that I get are undefined method owners for nil:NilClass which are appropriate because I know I'm not telling rails where to look correctly. 我已经尝试过使用fields_for变体,但是我不知道如何告诉嵌套字段基于上述select (当然,根据选择的property来加载不同的所有者。我得到的错误是undefined method owners for nil:NilClass是合适的,因为我知道我没有告诉rails正确的位置。

So is this possible and if so, how would I achieve this? 那有可能吗?如果可以,我将如何实现呢?

(I use bootstrap forms gem incase anyone is wondering about form syntax. I also have cocoon loaded for other forms so if there's a way to use that then I'm not opposed.) (如果有人想知道表单语法,我会使用bootstrap表单gem。我也为其他表单加载了cocoon,因此,如果有使用方法,我就不会反对。)

Update Working code, slightly modified for extraneous conditions. 更新工作代码,对无关的条件进行了一些修改。

$("#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.')
    }

});

You need to make sure it matches your underlying routes & stuff, and probably handle the case where there aren't owners, so to hide the owners div. 您需要确保它与您的基础路线和内容相匹配,并可能处理没有所有者的情况,以便隐藏所有者div。 If you want to do more complex stuff you could instead of .pluck build a better array/hash with id's that you can then also use to build elements that you can interact with (eg remove them from the list) 如果您想做更复杂的事情,可以用.pluck代替构建带有ID的更好的数组/哈希,然后还可以用来构建可以与之交互的元素(例如,从列表中删除它们)

# 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);
    }
  });
}

UPDATE: 更新:

You can prevent the issue with no Owners by using find_by instead and making sure that you always return an [] , this way simplifying the logic on the front-side as well. 您可以改用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);
  }
});

Hope this helps 希望这可以帮助

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM