在提交

时间:2017-08-08 18:52:06

标签: ruby-on-rails ruby forms associations ruby-on-rails-5

所以我不确定如何实现这一点,或者它是否可能并且很难找到类似的例子/答案。但是有没有办法在提交之前在之前的形式中动态加载模型关联,就像不知道实例变量会提前是什么一样?

我的模型如下:

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.')
    }

});

1 个答案:

答案 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);
  }
});

希望这有帮助