yeller.js - auto submit form with turbo stream
stimulus
Rails
Note to myself true "yeller" should be able to call different path as the form defined. e.g. autosafe to different endpoint.
The scenarios here for yeller that submit the form on event should be called "submitter"
The scenarios here for yeller that submit the form on event should be called "submitter"
Click button version
//app/javascript/controllers/yeller_controller.js import { Controller } from "@hotwired/stimulus" // <div data-controller="yeller"> // <form> // <input type="submit" data-yeller-target="submit"> // <input type="checkbox" name="some_field" value="some_value" data-action="yeller#call"> // </form> export default class extends Controller { static targets = ["submit"] call() { this.submitTarget.click() } }
JS version without button
Using requestSubmit() which triggers Turbo events
//app/javascript/controllers/yeller_controller.js import { Controller } from "@hotwired/stimulus" // <form data-controller="yeller"> // <input type="checkbox" name="some_field" value="some_value" data-action="yeller#call"> export default class extends Controller { call() { this.element.requestSubmit() } }
JS version using turbo navigator
//app/javascript/controllers/yeller_controller.js import { Controller } from "@hotwired/stimulus" import { Turbo } from "@hotwired/turbo-rails" // <form data-controller="yeller"> // <input type="checkbox" name="some_field" value="some_value" data-action="yeller#call"> export default class extends Controller { call() { Turbo.navigator.submitForm(this.element); } }
JS version using request.js
make sure you pin
pin "@rails/request.js"
stimulus
//app/javascript/controllers/yeller_controller.js import { Controller } from "@hotwired/stimulus" import { FetchRequest } from "@rails/request.js" // <div data-controller="yeller" // ...or // <div data-controller="yeller" data-yeller-url-value="/some/different/path/than/form", data-yeller-method-value="post"> export default class extends Controller { static values = { url: String, method: String } async call() { let form = this.element.querySelector('form') const formData = new FormData(form); formData.delete('_method') let url = this.hasUrlValue ? this.urlValue : form.action; let httpMethod = this.hasMethodValue ? this.methodValue : form.method; const request = new FetchRequest(httpMethod, url, {responseKind: "turbo-stream", body: formData}) request.perform() } }
use
to autosubmit to same endpoint where <form> points to (with same HTTP method)
div data-controller="yeller" = simple_form_for @model, url: some_path do |f| = f.input :whatever, as: :radio_buttons, input_html: { data: { action: "change->yeller#call" } }
to autosubmit form to different controller than where form points to (with same HTTP method)
div data-controller="yeller" data-yeller-url-value="#{different_path}" data-yeller-method-value="post" = simple_form_for @model, url: some_path do |f| = f.input :whatever, as: :radio_buttons, input_html: { data: { action: "change->yeller#call" } }
rails controller
def create @cq_form.update(cq_property_params) respond_to do |format| format.turbo_stream end end
DEbounce JS version
(not tested yet but should work)
import { Controller } from "@hotwired/stimulus" import { FetchRequest } from "@rails/request.js" // Connects to data-controller="yeller" // <div data-controller="yeller" data-yeller-url-value="/draft_save/2/path", data-yeller-method-value="patch"> // <form data-yeller-target="form" action="/real_save/2" method="post"> // <input type="text" name="message" data-action="keyup->yeller#debounceCall" /> // <button type="button" data-action="click->yeller#call">Save Draft</button> // <button type="submit"">Real save</button> // </form> export default class extends Controller { static values = { url: String, method: { type: String, default: 'post' } } static targets = [ "form" ] connect() { this.debounceCall = this._debounce(this.call.bind(this), 300); // this should create #debounceCall method } async call(event) { const formData = new FormData(this.formTarget); formData.delete('_method'); const request = new FetchRequest(this.methodValue, this.urlValue, { responseKind: "turbo-stream", body: formData }); request.perform(); } _debounce(fn, delay) { let timeout; return (...args) => { clearTimeout(timeout); timeout = setTimeout(() => fn(...args), delay); }; } }