Question

I'm trying to implement a search and sort form on all of my model's index pages using Ransack. I would like to do this as DRY'ly as possible so I created three partials in my views/application folder:

  • views/application/_table_search_and_sort_form.html.erb
  • views/application/_condition_fields.html.erb
  • views/application/_sort_fields.html.erb

The code for each is as follows:

views/application/_table_search_and_sort_form.html.erb

<div>
  <%= search_form_for table_search_and_sort_form do |f| %>
    <%= f.condition_fields do |c| %> 
      <%= render partial: "condition_fields", locals: { f: c, associations: associations } %>
    <% end %>
    <p><%= link_to_add_fields "Add Conditions", f, :condition %></p>
    <%= f.sort_fields do |s| %>
      <%= render "sort_fields", f: s %>
    <% end %>
    <p><%= link_to_add_fields "Add Sort", f, :sort %></p>
    <div class="actions"><%= f.submit "Search" %></div>
  <% end %>
</div>

views/application/_condition_fields.html.erb

<div class="field" data-object-name="<%= f.object_name %>">
  <%= f.attribute_fields do |a| %>
    <%= a.attribute_select associations: associations %>
  <% end %>
  <%= f.predicate_select %>
  <%= f.value_fields do |v| %>
    <%= v.text_field :value %>
  <% end %>
  <%= link_to "remove", '#', class: "remove_fields" %>
</div>

views/application/_sort_fields.html.erb

<div class="field" data-object-name="<%= f.object_name %>">
    <%= f.sort_select %>
    <%= link_to "remove", '#', class: "remove_fields" %>
</div>

In each of my controllers I have something like:

def index
    @search = Document.search(params[:q])
    @documents = @search.result.paginate(:page => params[:page])
    @search.build_condition if @search.conditions.empty?
    @search.build_sort if @search.sorts.empty?
end

and then I add the form to each index.html.erb by adding:

<%= render partial: "table_search_and_sort_form", object: @search, locals: { associations:  [:status, :job, :project, :language, :data_files] } %>

Where the :associations array is different for each model depending on the associations I would like to be accessible from the condition form. When I don't try to pass the :associations array down through two partials and just hard code the array into _condition_fields.html.erb such as:

<%= a.attribute_select associations: [:status, :job, :project, :language, :data_files] %>

Everything works just fine (for that one model, Document in this case). When I try to extend this to multiple models by passing the :associations array down from index to _table_search_and_sort_form and on to _conditional_form I get the following error:

undefined local variable or method `associations'


Extracted source (around line #3):

1  <div class="field" data-object-name="<%= f.object_name %>">
2  <%= f.attribute_fields do |a| %>
3  <%= a.attribute_select associations: associations %>
4  <% end %>
5  <%= f.predicate_select %>
6  <%= f.value_fields do |v| %>

How come the associations array doesn't get passed down beyond the first partial even when it's give to render's locals hash? I thought Rails didn't care how many partials you call from a partial? I tried to rename associations to avoid confusion with *attribute_select*'s associations parameter but this makes no difference.

Was it helpful?

Solution

The problem was that I have an application helper defined as

  def link_to_add_fields(name, f, type)
    new_object = f.object.send "build_#{type}"
    id = "new_#{type}"
    fields = f.send("#{type}_fields", new_object, child_index: id) do |builder|
      render(type.to_s + "_fields", f: builder)
    end
    link_to(name, '#', class: "add_fields", data: {id: id, fields: fields.gsub("\n", "")})
  end

This helper is called in _table_search_and_sort_form and as you can see the 'associations' array does not get passed to it and so that's where the error gets thrown, not on line 4 as I initially suspected. I guess I could hack this method to pass in the optional array but I'd rather not do that; it works fine as it is for the _condition_fields and _sort_fields partials and I think I'd be creating a headache for myself if I hack or overload it. Instead, I'm going to look for another way to add some of the associations to the search form, perhaps by overriding self.ransackable_attributes or self.ransackable_associations in the model. I haven't find much solid documentation about this but I'll experiment a bit. Hope this helps someone else too.

OTHER TIPS

May not be an answer but seemed far to long to post in the comments

I am not sure you can use locals and object together although I could be wrong. Maybe try

<%= render "table_search_and_sort_form", locals: { search: @search, associations:  [:status, :job, :project, :language, :data_files] } %>

Then modify the partial

<div>
<%= search_form_for search do |f| %>
  <%= f.condition_fields do |c| %> 
    <%= render partial: "condition_fields", locals: { f: c, associations: associations } %>
  <% end %>
  <p><%= link_to_add_fields "Add Conditions", f, :condition %></p>
  <%= f.sort_fields do |s| %>
    <%= render "sort_fields", f: s %>
  <% end %>
  <p><%= link_to_add_fields "Add Sort", f, :sort %></p>
  <div class="actions"><%= f.submit "Search" %></div>
<% end %>
</div>
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top