All threads / Populate dropdowns based on selection with Stimulus JS

Ask A Question

Notifications

You’re not receiving notifications from this thread.

Populate dropdowns based on selection with Stimulus JS

Jay Killeen asked in Javascript

This is a follow up question from Populate dropdowns based on selection that I asked like... 3 years ago... and still haven't reallly done it very well.

Now that StimulusJS is here. Things seem more structured.

I have a stimulus controller below. After I complete the AJAX request, I want to send that data to another function just to keep my code clean. How do I call the this.doThingWithData(data) in the Ajax success callback? All I get so far is a TypeError: this.doThingWithData is not a function

import { Controller } from "stimulus"

export default class extends Controller {

  static targets = [ "material_id", "to_unit", "result"]

  get material_id() {
    return this.targets.find("material_id").value
  }

  updateToUnitOptions() {
    const material_id = this.material_id

    Rails.ajax({
      type: "GET",
      url: "/alt_units.json",
      data: "material_id=" + material_id,
      success: function(data) {
        message()
        this.doThingWithData(data)
      }
    })
    this.resultTarget.innerHTML = "You have selected material: " + material_id
  }

  doThingWithData(data) {
    // update a Stimulus Target
    console.log(data)
  }
}

function message() {
  console.log('Alt Units were got!');
}

Simply, all I am doing here is taking the material_id from a select dropdown, and then eventually I want to update the next dropdown menu item with the filtered list of alt_units that comes back from the alt_units_controller#index from rails.

You need to have your callback using a fat arrow => instead so it keeps the scope.

      success: (data) => {
        this.message()
        this.doThingWithData(data)
      }

That will retain the scope so that this refers to the Stimulus controller. That will fix your method call error.

Wow. I was just deep diving in the console and wondering why this inside the callback was only referencing the Rails.ajax object... OK! Back on the road again!

I am now at the point where the dropdown menu 'toTarget' options need to be updated. Thanks for your help.

  updateToUnitOptions() {
    const material = this.material

    Rails.ajax({
      type: "GET",
      url: "/alt_units.json",
      data: "material=" + material,
      success: (data) => {
        console.log('Alt Units were got!')
        this.refreshDropdownValues(data)
      }
    })
  }

  refreshDropdownValues(data) {
    // update a Stimulus Target
    this.result = this.material
    this.toTarget <<<<< here is where I need to update selection option values.
    console.log(data)
  }
    ```

Did you ever finish this? I am looking to do the exact same thing.

I did and it worked pretty well. I'll hunt down a sample of the code and share back here soon. Might take me a while though as I am away for a few days. I'll dump a bit below but I don't have time to cut out the sensitive info.

It was a little more to it than I first thought it would be. Here is a bit of stimulus. Notice the stuff with the Rails.Ajax that is querying my rails controller and returning json. So a bit is needed to be done in the controller to respond to that ajax request and only return the json in a format for stimulus to use.

I'm running it all from a new.html.erb that is requesting to create and then returning a show partial which allows the dropdown to be updated based on the selection of the previous dropdown. It really needs a demo of the whole thing I put together in a more generic way than what I had done in my application (ie do a simple country / state / city selector from Rails api all the way through to stimulus).

import { Controller } from "stimulus"

export default class extends Controller {

  static targets = [ "material", "price", "from", "query", "result", "button"]

  initialize() {
    console.log("Stimulus at your service!")
    this.updateQueryParams()
    this.toggleLoading()
  }

  get from () {
    return this.targets.find("from").value
  }

  get material() {
    return this.targets.find("material").value
  }

  get price() {
    return this.targets.find("price").value
  }

  toggleLoading() {
    this.targets.find("button").classList.toggle("is-loading")
  }

  updateToUnitOptions() {
    this.clearResult()

    Rails.ajax({
      type: "GET",
      url: "/alt_units.json",
      data: "material=" + this.material,
      success: (data) => {
        console.log('Alt Units were got!')
        this.refreshDropdownValues(data)
      }
    })
  }

  refreshDropdownValues(data) {
    let fromBefore = this.from
    this.fromTarget.innerHTML = ""
    for(var i = 0; i < data.length; i++) {
      var opt = data[i]
      this.fromTarget.innerHTML += "<option value=\"" + opt.name + "\">" + opt.name + "</option>"
    }
    this.fromTarget.value = fromBefore
    this.updateQueryParams()
  }

  clearResult() {
    this.queryTarget.innerHTML = ""
  }
}

Hi, a favor, can you post the full solution, please?

Join the discussion

Want to stay up-to-date with Ruby on Rails?

Join 33,665+ developers who get early access to new tutorials, screencasts, articles, and more.

    We care about the protection of your data. Read our Privacy Policy.

    logo Created with Sketch.

    Ruby on Rails tutorials, guides, and screencasts for web developers learning Ruby, Rails, Javascript, Turbolinks, Stimulus.js, Vue.js, and more. Icons by Icons8

    © 2020 GoRails, LLC. All rights reserved.