This approach based on drifting ruby attaches stimulus controller automatically to the form that adds change events that allow the form to be save. Controller approach is slightly different in we add line for turbo stream check the video for difference. This is really for updates rather than creates.
Create a new folder apps\builder
then added AutoFormBuilder.rb
example can be seen in baccus
Add stimulus controller autosave_controller.js
register in index.js for stimulus components.
import { Controller } from "@hotwired/stimulus"
// Connects to data-controller="autosave"
export default class extends Controller {
connect() {
if (this.element.hasAttribute("data-action")) {
console.log("data-action already set", this.element.getAttribute("data-action"))
this.element.setAttribute("data-action",`${this.element.getAttribute("data-action")} change->autosave#submit`)
}
else {
this.element.setAttribute("data-action", "change->autosave#submit")
}
}
submit() {
let data = new FormData()
data.append(this.element.name, this.value())
// regular expression here to find the last value in [brackets] e.g. competition_entry[predictions_attributes][0][home_score] gives home_score
// we replace that with "id" to see if have nested attributes autosaving as we need to send the id of the nested object
let field_name_id = this.element.name.replace(/[^\[\]]+(?!.*\[)/, 'id');
if (document.getElementsByName(field_name_id).length == 1) {
console.log("appending-id for " + field_name_id)
data.append(field_name_id, document.getElementsByName(field_name_id)[0].value)
}
fetch(this.url(), {
method: "PUT",
headers: {
Accept: "text/vnd.turbo-stream.html",
"X-CSRF-Token": this.csrf_token()
},
//redirect: "manual",
body: data
})
}
value() {
// console.log(this.element.type)
switch (this.element.type) {
case "checkbox":
return this.element.checked == true ? 1 : 0
default:
return this.element.value
}
}
url() {
return this.element.form.action
}
csrf_token() {
return document.head.querySelector('meta[name="csrf-token"]').getAttribute("content")
}
}
Tell form to use the AutoSaveBuilder and give unique dom_id for example:
<%= form_with(model: time_log, builder: AutoSaveBuilder, id: "#{dom_id(time_log)}_form") do |form| %>
In the controller responde to turbostream for example:
if @time_log.update(time_log_params)
respond_to do |format|
format.html { redirect_to time_logs_path, notice: "Time log was successfully updated." }
format.turbo_stream { head :ok }
end
else
respond_to do |format|
format.html { render :edit, status: :unprocessable_entity }
format.json { render json: @time_log.errors, status: :unprocessable_entity }
format.turbo_stream { head :unprocessable_entity }
end
end