For example have form with select box which has list of reasons and when user selects the reason we want to populate an amount field on the form. The reason table has the amount as one of its columns. In this example we have adjusments and adjustment_reasons when user creates a new adjustment they pick reason it populates the amount with default value from the adjustment reason.
We will create reason_controller.js in app/javascript/controllers
.
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static targets = [ "amountField" ]
change(event) {
let reason_id = event.target.selectedOptions[0].value
let amountField = this.amountFieldTarget
const fetchPromise = fetch(`/adjustment_reasons/${reason_id}.json`);
fetchPromise.then((response) => {
const jsonPromise = response.json();
jsonPromise.then((data) => {
amountField.value = data.amount
});
});
}
}
The register it in app/javascript/controllers/index.js
import ReasonController from "./reason_controller"
application.register("reason", ReasonController)
We are using standard show method on the adjustments_reasons getting it to return JSON. It just uses before action callback
before_action :set_adjustment_reason, only: %i[ show edit update destroy ]
to get the reason.
Then use JSON partials app/views/adjustment_reasons/show.json.jbuilder:
json.partial! "adjustment_reasons/adjustment_reason", adjustment_reason: @adjustment_reason
And app/views/adjustment_reasons/_adjustment_reason.json.jbuilder:
json.extract! adjustment_reason, :id, :description, :amount, :adjustment_type_id, :created_at, :updated_at
json.url adjustment_reason_url(adjustment_reason, format: :json)
Make sure both the select drop down and the target field in enclosed in div with data-controller="reason"
(other fields and layout will be on form just showing the main changes).
Using ViewComponents the main thing with select is to add the action to fire on change data: {action: "change->reason#change"}
And the main the thing with the input field is to set target data: {"reason_target": "amountField"}
<div data-controller="reason">
<%= render Forms::FieldComponent.new(form, :adjustment_reason_id, label: "Reason") do |field| %>
<% field.with_input_select(options: adjustment_reasons_for_select(form.object.adjustment_reason_id), include_blank: true, required: true, data: {action: "change->reason#change"}) %>
<% end %>
<%= render Forms::FieldComponent.new(form, :amount) do |field| %>
<% field.with_input_currency(data: {"reason_target": "amountField"}) %>
<% end %>
</div>