Populate dropdowns based on selection with Stimulus JS
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)
}
```
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 = ""
}
}
The dynamic dropdown with Stimulus JS was a feature I spend a lot of time trying to solve, eventually got it working like this.
import { Controller } from "stimulus";
export default class extends Controller {
static targets = [
"property_id",
"roomId",
"listRooms",
];
updateRooms() {
const property_id = this.targets.find("property_id").value;
Rails.ajax({
type: "GET",
url: "/get_rooms.json",
data: "property_id=" + property_id,
success: (data) => {
this.updateDropdown(data);
},
});
}
updateDropdown(data) {
this.roomIdTarget.innerHTML = "";
const num_rooms = data;
if (num_rooms == 0) {
const option = document.createElement("option");
option.innerHTML = "Entire place";
this.roomIdTarget.appendChild(option);
} else {
data.forEach((room) => {
const option = document.createElement("option");
option.value = room.id;
option.innerHTML = "Room " + room.name;
this.roomIdTarget.appendChild(option);
});
}
}
Thomas or Jay, do you have a repo of your solution(s)? I've been struggling with this same thing and would love to take a look. Thanks!
I worked this out for Stimulus Reflex. I know when I searched for this it landed me here initially. Here's a link to the repo i made demonstrating this: https://github.com/Vielhammer/stimulus_reflex_geo_selector_demo