Chad Woo
Chad Woo

Reputation: 43

Rails filtering with ajax and render partial

I'm trying to make filtering function with ajax and render partial. The reason why I need these two is because there is an element I should not reload. Because of this I'm trying to get a filtered data by ajax and render partial the filtered data. If there is anything I should let you know more, please tell me.

Making index page work is fine.

controllers/item_controller.rb

def index
    @items = Item.all
end

views/item/index.html.erb

<div>
    An element that should not be reloaded
</div>
<div id="item_list_wrapper">
    <%= render partial: 'item/item_list' %>
</div>

views/item/item_list.erb

<% @items.each do |i| %>
    something
<% end %>

When I click a button, ajax is called and item/item_list.erb is reloaded.

controllers/item_controller.rb

def item_filter
    filtered_items = Item.where(filtered item)
    render json: filtered_items
end

javascript

$.ajax({
    method: 'get',
    url: '/item/filter',
    dataType: 'json',
    data: {
      price: $('#price').val()
    },
    success: function(data) {
        console.log(data);
        $("#item_list_wrapper").html("<%= escape_javascript(render partial: 'item/item_list') %>");
    },
    error: function(){
        alert('error');
    }
});

I can see the ajax called data in my console. It took me several days but I really cannot find out a way to use the ajax called data as @items in the reloaded item/item_list.erb. Thanks in advance for all comments.

Upvotes: 2

Views: 2478

Answers (2)

max
max

Reputation: 102036

You can't.

The ERB interpolation (anything in ERB tags <% %> or <%= %>) happens on the server before the the javascript is sent to the client. If your script is placed in app/assets/javascripts this done when the scripts are compiled at deploy time.

The Ajax request happens later on the client.

You either need to do the templating on the client side:

var $template = $('<li></li>');
var promise = $.getJSON(
  url: '/item/filter',
  data: {
      price: $('#price').val()
  },
  success: function(data) {
    $.each(data, function( index, value ) {
      $("#item_list_wrapper").append( $template.clone().text(value));
    };
  },
  error: function(){
    alert('error');
  }
)

Or use a js.erb template:

var promise = $.ajax(
  url: '/item/filter',
  data: {
      price: $('#price').val()
  },
  dataType: 'application/javascript',
  error: function(){
    alert('error');
  }
)

You can also use Rails UJS to send the ajax request by adding the data-remote="true" to the form (usually done by adding remote: true to the form helper options).

# defaults to the js format
# set "data-type" => :json to send JSON instead
<%= form_tag('/item/filter', remote: true) %>
  <%= number_field_tag(:price) %>
  <%= submit_tag 'Filter items' %>
<% end %>

You need to setup your controller to respond to the js format:

class ItemsController
  def filter
    # ...
    respond_to do |format|
      format.js
    end
  end
end

// app/items/views/filter.js.erb
$("#item_list_wrapper").html("<%= escape_javascript(render partial: 'item/item_list') %>");

Upvotes: 1

abax
abax

Reputation: 787

I think it may have to do with not passing in the data to your partial. Try changing views/item/item_list.erb to

<% items.each do |i| %> something <% end %>

then call it in your view:

<%= render partial: 'item/item_list', locals: { items: @items } %>

then finally pass in data to the partial using JS:

$("#item_list_wrapper").html("<%= escape_javascript(render partial: 'item/item_list', locals: { items: data }) %>");

Having said all that, I'd also recommend learning to use remote => true to simplify your js. I've found this guide straightforward: https://coderwall.com/p/kqb3xq/rails-4-how-to-partials-ajax-dead-easy

Upvotes: 0

Related Questions