0

I'm building a widget that a client can display on their third party website. The widget accepts input from consumers about what kind of house they are looking for. On submit, the widget makes a POST request on a search model within the endpoint API (my database). Then I want to display the results of the search on the client's website.

So far I've successfully posted the search form on a third party website, and on submit a new search object is created and the search results are ready for view on my website. But I want the search results to be sent back to the client's website.

On the client's website, they have a js script that looks like this:

<script type="text/javascript" src="http://localhost:3000/searchapis/search_form.js"></script>

My routes.rb:

get 'searchapis/show'
get '/searchapis/:template', to: "searchapis#show"
post 'search/create', to: "searches#create"

I created a searchesapi controller that looks like this:

class SearchapisController < ApplicationController
  protect_from_forgery :except => :show
  
  def show
    @search = Search.new
    
    respond_to do |format|
      format.html { render params[:template], layout: 'searchapis' }
      format.js   { render js: js_constructor }
    end
  end
  
  private
    def js_constructor
      content = render_to_string(params[:template], layout: false)
      "document.write(#{content.to_json})"
    end
end

I created a search_form.html.erb view that looks like this:

<%= bootstrap_form_for @search, :html => {}, :url => create_search_url(@search), remote: true do |form| %>

<%=form.select :beds, (select_options), {prompt: "Min # of Bedrooms", hide_label: true}, {class: "form-control form-control-sm"}%>

<%=form.select :max_price, (100000..2000000).step(10000).map{|x| number_to_currency(x, precision: 0)}, {hide_label: true, prompt: "Select Max Price"}, {class: "form-control form-control-sm", id: "sale_price"}%>

<input type="submit" name="commit" value="SEARCH" class="btn btn-danger" data-disable-with="Finding Schools...">

And finally the searches_controller.rb:

class SearchesController < ApplicationController
  skip_before_action :verify_authenticity_token
  before_action :set_search, only: [:show, :edit, :update, :destroy]
  before_action :require_admin, only: [:index, :destroy]

  def create
      @search = Search.new(search_params)

      respond_to do |format|
        if @search.save

          format.html { redirect_to @search, notice: 'Search was successfully created.'}
          results = @search.listings
          p results #this is what I want to send to the third party site
          format.json { render json: results.to_json, remote: true, notice: "Search results below"}
        end
      end
  end
end

Now the form is displayed, and on submit, a new search is created, but I'm not sure how to proceed so the search results can be sent via a JSON response to the third party website.

I'm thinking I'll need to produce an additional javascript src for the client's website =(<script type="text/javascript" src="http://localhost:3000/searchapis/api_search_results.js"></script>) linked to a new api_search_results page, and I'll need to rails g migration AddNewSttributeToSearch api:boolean so I can catch an api search in the controller and route to the new api_search_results page. Then, when a consumer hits submit on the third party website, the 2nd javascript src would display the results, but I think perhaps there is a better way. Any thoughts out there?

tomb
  • 1,374
  • 1
  • 13
  • 23
  • Why not use an iframe? Render that form in an iframe and you will have full control of the things inside it. – razvans Nov 06 '21 at 20:18
  • Thanks for the comment @razvans, I'll give it a try. But once the iframe is in place, and the consumer enters their data, and the data is passed to my db, and the search is conducted and results are ready, how do I then display those results on the consumer's screen? Theoretically there could be hundreds of people on the client's website, so the results have to match each query. – tomb Nov 07 '21 at 22:07

1 Answers1

0

I finally figured it out. On the same view that has the form, I created a table to hold the data and some javascript to display the json response. A new action "search_results" handles accepting the form data, creating the search, and returning json.

Updated routes:

  get 'searchapis/search_results'
  post 'searchapis/search_results'
  get 'searchapis/show'
  get '/searchapis/:template', to: "searchapis#show"

Updated search_form view:

<%= bootstrap_form_for @search, :html => {id: "search_api"}, :url => searchapis_search_results_url(@search), remote: true do |form| %>

<%=form.select :beds, (select_options), {prompt: "Min # of Bedrooms", hide_label: true}, {class: "form-control form-control-sm"}%>

<%=form.select :max_price, (100000..2000000).step(10000).map{|x| number_to_currency(x, precision: 0)}, {hide_label: true, prompt: "Select Max Price"}, {class: "form-control form-control-sm", id: "sale_price"}%>

<input type="submit" name="commit" value="SEARCH" class="btn btn-danger" data-disable-with="Finding Schools...">

<% end %>

The updated controller to display the form, accept the form data, create a search, and return results via json:

class SearchapisController < ApplicationController
  protect_from_forgery :except => [:show, :search_results]
  
  def show
      @search = Search.new
      @search.api_search = true
    
    respond_to do |format|
      format.html { render params[:template], layout: 'searchapis' }
      format.js   { render js: js_constructor }
    end
  end
  
  def search_results
    @search = Search.new(search_params)
    @search.save
    @results = @search.listings
    response = @results.map{|x| [x[0].name,x[0].address,x[0].city,x[0].performance_stats.find_by(year: "1819").rating,x[1].count]}
    render json: response.to_json
  end

  
  private
    def js_constructor
      content = render_to_string(params[:template], layout: false)
      "document.write(#{content.to_json})"
    end
    
    #note, I had to copy search_params from the SearchesController, so perhaps the search_results method could be added to the Searches Controller to avoid this

    def search_params
      params.require(:search).permit(:name, :address, :city)
    end
end

SearchesController Create action:

respond_to do |format|
      if @search.save
        if @search.api_search == true
          return
        end
       *additional routing*
      end
end

The search_form view now looks like this:

<style>
  #hide_table {
     display:none
  }
</style>

<%= bootstrap_form_for @search, :html => {id: "search_api"}, :url => searchapis_search_results_url(@search), remote: true do |form| %>

<%=form.select :beds, (select_options), {prompt: "Min # of Bedrooms", hide_label: true}, {class: "form-control form-control-sm"}%>

<%=form.select :max_price, (100000..2000000).step(10000).map{|x| number_to_currency(x, precision: 0)}, {hide_label: true, prompt: "Select Max Price"}, {class: "form-control form-control-sm", id: "sale_price"}%>

<input type="submit" name="commit" value="SEARCH" class="btn btn-danger" data-disable-with="Finding Schools...">

<% end %>



<div class="container-fluid">
    <div class="row">
        <div class="col">
            <div id='hide_table'>
                <table class="table table-sm" id="schools_api">
                    <thead>
                        <tr>
                            <th>Name</th>
                            <th>Address</th>
                            <th>City</th>
                            <th>Score</th>
                            <th># Homes</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </div>
        </div>
    </div>
</div>

<script>
document.body.addEventListener("ajax:success", (event) => {
        var response = event.detail[0];
        $("#schools_api tbody tr").remove(); 
        $('#hide_table').css("display","block");
        $.each(response, function(i, item) {
            $('<tr>').html("<td>" + item[0] + "</td><td>" + item[1] + "</td><td>" + item[2] + "</td><td>" + item[3] + "</td><td>" + item[4] + "</td>").appendTo('#schools_api');
        });
    });

</script>
tomb
  • 1,374
  • 1
  • 13
  • 23