We've moved discussions to Discord

Pointers on a Stimulus conditional select

Dan Tappin
I have a model where you select two related model attributes.  Once you select these two I want the third select to be active and the select values from a DB query based on the id's chosen in the first two selects.

Anyone seen a good screencast etc. on anything close to this?
Brandon B.
Dan Tappin
I am walking through this (https://stimulusjs.org/handbook/hello-stimulus) and I am seeing how powerful Stimulus is.  I have a basic controller built and can tigger events (basic alerts etc.) to test drive this.

Intermediate questions for the experts:

  • Can you add nested folders to organize your controllers? I can see this getting messy.
  • Can you use CoffeScript and JQuery here also?  Plain JS just seem so clunky after using them.

I see where this is going what I don't get is how I pull the query from the DB and update the target select. I know there are the Rails reponds_to JS controllers that do similar results like    does here https://gorails.com/episodes/nested-comment-threads-in-rails-part-3?autoplay=1.

I just don't get yet how you combine Stimulus with the Rails Active Record calls etc.
Brandon B.
So,  - when are we going to build an app together?  I see us plastering these message boards with questions and answering each others questions.  I figure lets cut through the crap and get some of these ideas that we have implemented, haha.
Dan Tappin
Dan Tappin
Soooooooooo close.  It works except that my dynamic select populates twice like this:

  • item 1
  • item 2
  • item 3
  • item 1
  • item 2
  • item 3

Can anyone see what I am missing?  The whole controller is not being run twice because the existing contents are wiped clean on the first line.  It's in that 'request.onload' function that seems to be looping twice somehow.

I have a custom controller action that does the JSON via jbuilder:


json.array!(@related) do |exp|
  json.extract! exp, :id
  json.extract! exp, :related_select_label

custom related controller action

  def related
    if params[:vendor_id].nil? && params[:budget_id].nil?
      @related = [ { } ]
      @related = Expenditure.includes(:budget, :vendor, :project).where(flag_active: true).where( vendor_id: params[:vendor_id], budget_id: params[:budget_id] )
    respond_to do |format|
      format.json  #{ render :json => @related.to_json }

I added some logic to confirm the input has values


<div data-controller="expenditure">
<%= form_with(model: expenditure) do |form| %>
  <div class="form-group">
    <%= form.label :category %>
    <%= select(:expenditure,
               [ [ "Purchase Order" , :purchase_order ],[ "Work Order", :work_order], [ "Invoice", :invoice ], ["Accrual", :accrual ], ["Field Ticket", :ticket ] ],
               {class: "form-control"} , { data: { target: "expenditure.category", action: "change->expenditure#update_related"} } )  %>

  <div class="form-group">
    <%= form.label :budget_id %>
    <%= collection_select(:expenditure, :budget_id, @budgets, :id, :select_label_client, {include_blank: 'Select a budget...'}, {class: "form-control", data: { target: "expenditure.budget" ,action: "change->expenditure#update_related"} } )  %>

  <div class="form-group">
    <%= form.label :vendor_id %>
    <%= collection_select(:expenditure, :vendor_id, @vendors, :id, :name, {include_blank: 'Select a vendor...'}, {class: "form-control", data: { target: "expenditure.vendor" ,action: "change->expenditure#update_related"} } )  %>

  <div class="form-group">
    <%= form.label :parent_id %>
    <%= collection_select(:expenditure, :parent_id, @related, :id, :related_select_label, {include_blank: 'Select a parent expenditure...'}, {class: "form-control", data: { target: "expenditure.related"} } )  %>

now the magic!... HT to https://www.codebyamir.com/blog/populate-a-select-dropdown-list-with-json


import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "category", "budget", "vendor", "related" ]
  update_related() {

    let dropdown = this.relatedTarget;
    dropdown.length = 0;

    let defaultOption = document.createElement('option');
    defaultOption.text = 'Select parent expenditure...';

    dropdown.selectedIndex = 0;

    const url = '/related_expenditures.json?vendor_id=' + this.vendorTarget.value + '&budget_id=' + this.budgetTarget.value;

    const request = new XMLHttpRequest();
    request.open('GET', url, true);
    request.onload = function() {
      if (request.status === 200) {
        const data = JSON.parse(request.responseText);
        let option;
        for (let i = 0; i < data.length; i++) {
          option = document.createElement('option');
          option.value = data[i].id;
          option.text = data[i].related_select_label;
       } else {
        // Reached the server, but it returned an error
    request.onerror = function() {
      console.error('An error occurred fetching the JSON from ' + url);


Murray Bryant
Don't have time to replicate your whole code, but I am doing something similar with json and using this

            url: "/notifications.json",
            type: "get",
            data: "",
            success: function(data) {}
            error:function() {})

And it is working well, maybe this way could help with the double unload event
You’re not receiving notifications from this thread.