Search code examples
ruby-on-railsrubyhaml

Rails/AJAX: Partial not displaying?


Background (changed after correspondance with @TomLord):

I am building a simple site with only a index page and no subpages. The index page has a "search" field that takes input (id) and sends a GET request to a RESTful API running in the background. The response from the API is JSON {"market price": "4306057.0", "retail price": "4995000.0"}.

I want to display this result on the index page, together with the search field. However, when I press the button the result is not displayed anywhere.


Code:

index.html.erb

<section id="search">
  <div class="container">
    <%= form_tag({controller: "model_request", action: "result"}, method: "get", remote: true) do %>
      <%= label_tag(:q, "Search for") %>
      <%= text_field_tag(:q, "913384637") %>
      <%= submit_tag("Get the price!") %>
    <% end %>
  </div>
</section>

<section id="display_result">
  <div class="container">
  </div>
</section>

And the modelrequest_controller.rb looks like this:

class ModelrequestController < ApplicationController

  def result
    id = params['q'].capitalize
    response = RestClient::Request.execute(
      method:  :get,
      url:     "http://0.0.0.0:80/?id=#{id}")
    @result = JSON.parse response

    respond_to do |format|
      format.js {render layout: false}
    end
  end

  end

My routes.rb

Rails.application.routes.draw do

  root 'index#index'

  match "/modelrequest", to: "modelrequest#result", via: 'get'

end

The javascript for results looks like this:

result.js.erb

$("#display_result").html("<%= escape_javascript(render partial: 'modelrequest/result', locals: { result: @result } ) %>");

And the simple _result.html.erb for displaying the partial is

<div id="display_result">
  <%= @result %>
</div>

Output:

Started GET "/model_request?utf8=%E2%9C%93&q=913384637&commit=Get%20the%20price!" for 127.0.0.1 at 2018-12-19 20:55:33 +0100
Processing by ModelRequestController#result as JS
  Parameters: {"utf8"=>"✓", "q"=>"913384637", "commit"=>"Get the price!"}
  Rendering model_request/result.js.erb
  Rendered model_request/_result.html.erb (0.8ms)
  Rendered model_request/result.js.erb (6.8ms)
Completed 200 OK in 383ms (Views: 11.6ms | ActiveRecord: 0.0ms)

Solution

  • In a "normal" rails site, a search form works as follows -

    You define a form in the view. This will, by default, POST data to an endpoint - but you can also do this via a GET request. In your case, you originally chose:

    form_tag("/search", method: "get")
    

    This will send the form data to GET /search, synchronously, and therefore perform a full page reload.

    This is a perfectly valid thing to do. However, you wanted to remain on the index page - therefore the request needs to be asynchronous:

    form_tag("/search", method: "get", remote: true)
    

    ...But now, that endpoint needs to do something different. Instead of rendering a new page, it needs to partially update the current page.

    The standard approach for this in a rails application is to do something along the lines of:

    # In the controller action, e.g. SearchController
    def show
      @results = # ...
    
      respond_to do |format|
        format.js {render layout: false}
      end
    end
    
    # In the view, e.g. `search/show.js.erb`
    $("#display_result").html("<%= escape_javascript(render partial: 'results', locals: { results: @results } ) %>");
    
    # In an HTML partial, e.g. `search/_results.html.erb`
    <% results.each do %>
      ...
    

    The key idea is that on your main page (index.html.erb?), you must have a container to display the results. Then, rather than rendering a new page, you are merely rendering an HTML update inside that container.


    This is, of course, not the only way of doing it. Modern websites will often instead fetch JSON data for a search result and then determine how to display that via JavaScript.

    However, the above approach is the most "minimal" change from your current design - without needing to add additional design patterns, you can leave most of your existing haml/erb templates as-is, and just add a little code to render the results as a partial.