1

I have a simple has_many and belongs_to relationship in my rails app. I'm using simple_form and want to dynamically change the dropdown options based on the value chosen by the user.

Models

 class Processor < ApplicationRecord
   has_many :processor_bank_accounts
 end

 class ProcessorBankAccount < ApplicationRecord
   belongs_to :processor
 end

Form inputs

<%= simple_form_for [@customer, @transaction] do |f| %>
<%= f.error_notification %>

<div class="form-inputs">
  <%= f.input :status, :collection => ["payment request"], include_blank: false %>
  <%= f.input :processor, collection: @processors ,label_method: :name,value_method: :id,label: "Processor" , include_blank: false %>
  <%= f.input :processor_bank_account, collection: @bank_accounts , label_method: :bank_name, value_method: :id, label: "Processor Bank Account" , include_blank: true %>
  <%= f.input :tcurrency, collection: @currencies, include_blank: false, label: 'currency' %>
  <%= f.input :amount, as: :decimal, label: 'amount' %>
</div>

<div class="form-actions text-center">
  <%= f.button :submit, "Add transaction", class: "form-button"%>
</div>
<% end %>

So essentially, I need the processor_bank_account dropdown to populate based on the processor chosen by the user. In the console, this would just be: ProcessorBankAccount.where(processor: processor).

Need to load options using JS and think I need to use JSON but not sure where to go from here

3 Answers 3

1

One way to do this would be to use jQuery to make an AJAX call to a controller action and then let Rails handle the rest through an erb template.

So on your page, with the form, invoke the action via AJAX using something like:

<script>
    $(document).ready(function() {
        $('#processor_id').on('change', function() {
            $.ajax({
                url: '/transactions/get_processor_bank_accounts',
                type: 'GET',
                data: {
                    processor_id: this.value
                },
                dataType: 'script',
                error: function() {
                    alert('An error occurred retrieving bank accounts for the selected processor.');
                }
            });
        });
    });
</script>

NB, #processor_id is the id for your dropdown control.

Next, instantiate the bank accounts within your action in your controller:

def get_processor_bank_accounts
  @processor_bank_accounts = ProcessorBankAccount.where(processor_id: params[:processor_id])
end

And finally simply create a view that will be responsible for repopulating your dropdown:

$select_list = $('#processor_id');
$select_list.empty();

<% @processor_bank_accounts.each do |pba| %>
  $select_list.append($('<option value="<%= pba.id %>"><%= pba.name %></option>'));
<% end %>
Sign up to request clarification or add additional context in comments.

Comments

0

I came up with the following solution:

1) Add a new method to my processors controller to render the inputs for the second (dynamic) dropdown in JSON format:

def processor_bank_accounts
 render json: @processor.processor_bank_accounts.each do |bap|
 { id: bap.id, name: bap.name }
 end
end

2) Assign this new function to a new route in config/routes:

get 'api/bankaccounts', to: 'processors#processor_bank_accounts', as: 'bankaccounts'

3) Create a JS function to access the route with the id of the processor selected in the first dropdown and populate the second dropdown with items from the JSON array:

// select first dropdown
const processor = document.getElementById("transaction_processor");
// select second dropdown
const bapSelect = document.getElementById("transaction_processor_bank_account");

function update_baps(processor_id) {
 const url = `INSERTWEBURLHERE/api/bankaccounts?id=${processor_id}`;
 fetch(url)
 .then(response => response.json())
 .then((data) => {
  bapSelect.innerHTML = ""; // clear second dropdown
  data.forEach((bap) => { // go through all the BAPs
  const elem = `<option value="${bap.id}">${bap.bank_name}</option>`; // create option elements to insert into the second dropdown, bank_name is the chosen label method in the form
  bapSelect.insertAdjacentHTML("beforeend", elem); // insert options into the dropdown
  });
 });
}

4) Trigger the JS when the first dropdown field is changed:

processor.addEventListener('change', function () {
 update_baps(parseInt(processor.value));
});

Comments

0

You should add an id to the selects so you can identify them form the script.

$('select#processor').on('change', function() {
      var processor_id = this.value;
      var processor_bank_account = $('select#processor_bank_account')

      $.ajax({
        type: "GET", 
        url: <%= your_path %> ,
        data: { processor_id: processor_id },
        success: function(data, textStatus, jqXHR){
          processor_bank_account.empty();
          var option = new Option(data.bank_name, data.id, false, false);
          processor_bank_account.append(option);
        },
        error: function(jqXHR, textStatus, errorThrown){...}
      })
 });

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.