Trigger Rails 7 form from JS (from Stimulus)
Rails
stimulus
hotwire
importmaps
JavaScript
Best approach - just click the button with Stimulus 🙂
Seriously, you will save so much hustle with JS. Just hide the submit button an tell JS to click it.
// app/javascript/controllers/foo_controller.js import { Controller } from "@hotwired/stimulus" export default class extends Controller { static targets = [ "btn" ] save(event) { this.btnTarget.click } }
<form method="post" url="/whatever"> <div class="w-5 h-5 bg-red" data-action="click->foo#save"> <input type="submit" class="hidden" data-foo-target="btn"> </form>
--------------------------------
hands down above ☝️ is the best approach - if this goes in hand wit Rails SSR convention + sprinkles of JS. It solves 99.9% cases. If you are really stubborn and think JS does better job here are few more approaches:
Request.js
@rails/request.js encapsulates the logic to send by default some headers that are required by rails applications like the X-CSRF-Token.
GET example
import {Controller} from "@hotwired/stimulus" import {put} from "@rails/request.js" //... change(event) { let params = new URLSearchParams() params.append(this.paramValue, event.target.selectedOptions[0].value) get(`${this.urlValue}?${params}`, { responseKind: "turbo-stream" }) }
PUT example
# a/js/controllers/ts/select-update.js import {Controller} from "@hotwired/stimulus" import {put} from "@rails/request.js" export default class extends Controller { static values = { url: String, } update(event) { put(this.urlValue, { body: JSON.stringify({ block: { [event.target.id] : event.target.value}}), responseKind: "turbo-stream" }); } }
<div class="flex" data-controller="ts--select-update" data-ts--select-update-url-value="<%= something_important_url %>"> <%= f.input :provider, as: :select, collection: [...], label: false, input_html: { data: {controller: "ts--select", action: "ts--select-update#update"}, id: :provider } %>
The url points to controller that will do a hotwire update of a content (so the update Stimulus method don't really do anything with response, stream updates content)
<%= turbo_stream.replace "modal" do %> whatever <% end %>
Use Rails UJS
setup
$ ./bin/importmap pin @rails/ujs
this will add this line to importmaps:
pin "@rails/ujs", to: "https://ga.jspm.io/npm:@rails/ujs@7.0.1/lib/assets/compiled/rails-ujs.js"
Now include @rails/ujs to your javascript. In file javascript/controllers/application.js add:
import Rails from '@rails/ujs'; Rails.start();
Now you will be able to call
submitForm = (e) => { Rails.fire(this.element, 'submit'); }
import { Controller } from "@hotwired/stimulus" import Rails from "@rails/ujs" export default class extends Controller { static values = { url: String, } refresh(event) { Rails.ajax({ url: this.urlValue, type: "POST", success: () => { // ..... }, error: () => { // ... } }) end end