0

I have 2 easy-autocomplete form fields that I want to act like nested dropdowns.

They each have listeners that invoke a call to the controller which in turn populates the dropdown. This is working.

When the controller call for the second field is triggered, I need to know how to access the value of the first field. I would think it would be something like form.PayTo.value, or it would show up in the params hash, but I haven't been able to find it by guessing or looking online.

Better yet, is there a way to dump all objects that are available in memory at the time and I can just search for what I need?

Here is the controller code for the 2 user entry fields:

  # search code for the quickentry payto box
  def search
    q = params[:q]
    @transactions = Transaction.select("distinct PayTo").where( "PayTo like ?", "#{q}%")
  end

  # search code for the quickentry Description box
  def DescSearch
    q = params[:q]
    #@transactions = Transaction.select("distinct Description").where( "PayTo = " + :PayTo.to_s + " and Description like ?", "#{q}%")
    @transactions = Transaction.select("distinct Description").where( "PayTo = ? and Description like ?", "#{:PayTo}", "#{q}%")
    puts params
  end

Here is what the console shows:

Started GET "/quickentry/DescSearch.json?q=g" for ::1 at 2022-06-04 10:01:23 -0700
Processing by TransactionsController#DescSearch as JSON
  Parameters: {"q"=>"g"}
{"q"=>"g", "controller"=>"transactions", "action"=>"DescSearch", "format"=>"json"}
  Rendering transactions/DescSearch.json.jbuilder
  Transaction Load (0.3ms)  SELECT distinct Description FROM "transactions" WHERE (PayTo = 'PayTo' and Description like 'g%')
  ↳ app/views/transactions/DescSearch.json.jbuilder:1
  Rendered transactions/DescSearch.json.jbuilder (Duration: 1.7ms | Allocations: 713)
Completed 200 OK in 3ms (Views: 2.1ms | ActiveRecord: 0.3ms | Allocations: 1304)

The query is using the literal 'PayTo' instead of the value from the PayTo field.

Here is quickentry.html.erb

<h1>Quick transaction entry form</h1>
<%= render 'quickentryform', transaction: @transaction %>
<%= link_to 'Back', transactions_path %>

And here is quickentryform.html.erb (with some irrelevant fields snipped out)

<%= form_with(model: transaction) do |form| %>
  <% if transaction.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(transaction.errors.count, "error") %> prohibited this transaction from being saved:</h2>

      <ul>
        <% transaction.errors.each do |error| %>
          <li><%= error.full_message %></li>
        <% end %>
      </ul>
    </div>
  <% end %>

  <div class="field">
    <%= form.label :PayTo %>
    <%= form.search_field :PayTo, placeholder:"Enter name...", "data-behavior":"autocomplete", name:"PayTo"  %>
  </div>

  <div class="field">
    <%= form.label :Description %>
    <%= form.search_field :Description, placeholder:"Enter Description...", name:"Description" %>
  </div>

  <div class="field">
    <%= form.label :Notes %>
    <%= form.text_field :Notes %>
  </div>

  <div class="actions">
    <%= form.submit %>
  </div>
<% end %>

Entry in Payto field

Entry in Description field

Jere Neal
  • 19
  • 6
  • What is it that you actually expect to happen here? You're passing `"#{:PayTo}"` which is literally `:PayTo.to_s` as the bound parameter to the query. There is also no "Payto" parameter in the passed parameters. – max Jun 05 '22 at 11:48
  • Also you most likely don't need a separate endpoint - just add an additional optional parameter to your existing `search` action that lets you control the order. – max Jun 05 '22 at 11:51
  • @max, this is what I'm trying to accomplish. When the user enters data in the PayTo field, the code from the easyautocomplete gem updates the dropdown list with available choices (from the query in Def Search in the transactions controller). After focus moves to the Description field, upon keystrokes, a similar query is run from the Def DescSearch function. This uses the 'q=' data from the parameters hash, but I also need to acquire the value of the PayTo field (the final user choice) on the form and I assume this has to be in Ruby's memory as a variable or hash. – Jere Neal Jun 05 '22 at 15:05
  • @JereNeal pro-tip: your non-standard capitalization will get you into trouble sooner or later. Column names (PayTo, Description) and methods (DescSearch) should not be capitalized. – Les Nightingill Jun 06 '22 at 01:07
  • @LesNightingill, duly noted - thanks for the tip. As you can probably surmise, I am new to Ruby and Rails, but I definitely want to adopt best practices early on so that they become habit. – Jere Neal Jun 06 '22 at 16:11

2 Answers2

1

I have found a solution for this which involves grabbing the value of the first search box (outside of Ruby) and passing it in as a parameter. I assume that this was what @max was alluding to about separate endpoints, but I don't know what an endpoint is.

I still don't know how to obtain a list of all objects that are in-scope within the controller. So for that reason, I don't know if the form data is in scope to begin with inside the controller.

Here is the revised working code that obtains the value of the first search box and uses it in the query that feeds the second search box.

First, the javascript that obtains the value to be added to the parameters string:

document.addEventListener("turbolinks:load", function() {
  
  $input = $('*[name="Description"]')
  var options = {
    url: function(phrase) {
      
      /* Get the value of the payto box */
      var payto = document.getElementById("transaction_PayTo").value;

      return "/quickentry/DescSearch.json?q=" + phrase + "&payto=" + payto ;
    },
    getValue: "Description",
  };

  $input.easyAutocomplete(options);
  console.log("Description function end");

});

And the code that uses the passed parameter:

  # search code for the quickentry Description box
  def DescSearch
    q = params[:q]
    payto = params[:payto]
    @transactions = Transaction.select("distinct Description").where( "PayTo = ? and Description like ?", "#{payto}", "#{q}%")
  end
Jere Neal
  • 19
  • 6
  • Can you [edit] this answer to include your code? You should not edit your question to include the working answer, but instead post it as part of your answer. Otherwise, it breaks the question and answer format, and makes it confusing for future readers as to what the question is, and especially if there are other answers not based on your edit. – Jeremy Caney Jun 08 '22 at 00:32
  • 1
    @JeremyCaney, certainly! I will correct that tomorrow morning. I was under the incorrect impression that you couldn't use the code wrapper {} in the answer section so I put it in the question section , but I must have been thinking about comments, so my apologies. – Jere Neal Jun 08 '22 at 04:00
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jun 08 '22 at 04:29
  • 1
    I have moved the solution down to the answers area. Let me know if that is clearer and would be helpful to others who wish to implement nested autocomplete boxes. – Jere Neal Jun 08 '22 at 17:39
0

I hope I'm adequately understanding your question. If you're looking to access the params sent to that controller, this should work.

q = params["q"]

The data sent to the controller is JSON and that means you need to access it differently then you would a regular params hash. If you look at the line in the console that says: Parameters: {"q"=>"g"}, whereas if it were a normal params hash it might look something like: Parameters: {:q=>"g"}

Heres some more info on JSON params, hope this helped: https://guides.rubyonrails.org/action_controller_overview.html#json-parameters

oonsk
  • 24
  • 3
  • Thanks @oonsk for replying. The code is correctly handling the data passed in the params hash. In this case, it narrows down the list of choices by ones that start with 'g'. The thing that the code needs access to is the value in the form field named 'PayTo', because that is also used in the query to limit the choices. I don't know how to get access to that. For example, if I enter "Stater Brothers" in the PayTo field, then the list of choices should only contain items from Stater Brothers, and each character typed should narrow it down further. I hope that makes it clearer. – Jere Neal Jun 05 '22 at 02:37
  • Update: I read through the parameter documentation that you linked to, and the very next section (5) was on sessions, so I thought I had a Eureka! moment where I could save the value in the 'Payto' controller and retrieve it in the 'Description' controller, but unfortunately, until the call is made to the 'Description' controller, the user's final selection is not known. Bummer, so I still need to find out how to access the form data. – Jere Neal Jun 05 '22 at 03:02
  • Of course, no worries! Can you please update your question @Jere Neal with the view you're using to send the form data to the controller? Are you using a submit form field to send this? eg. `<%= f.submit %>` – oonsk Jun 05 '22 at 05:06
  • 1
    "The data sent to the controller is JSON and that means you need to access it differently then you would a regular params hash." - This is incorrect. Both JSON objects and FormData have string keys but thats abstracted away by `ActionController::Parameters` which provides indifferent access. There is no real difference between dealing with JSON or FormData. – max Jun 05 '22 at 11:42
  • @oonsk - Due to the AJAX or jQuery code from the easyautocomplete gem, the 2 functions in the transaction controller, Search and DescSearch are called with each keystroke by the listeners attached to the fields. So this all happens before the form is submitted (with the final results of what the user has chosen). HTH. – Jere Neal Jun 05 '22 at 14:56