forked from fixin.me/fixin.me
Replace fetch() calls with Turbo form submission via requestSubmit()
setDefaultUnit and drop previously made raw fetch() requests and called Turbo.renderStreamMessage() manually. Now both create a temporary <form>, append hidden inputs, and call form.requestSubmit() — Turbo intercepts the submission natively, handling CSRF, stream responses, and lifecycle. Also document this convention in CLAUDE.md. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
22
CLAUDE.md
22
CLAUDE.md
@@ -77,6 +77,28 @@ default/ namespace for default units import/export and admin panel
|
|||||||
root → /units (authenticated), /sign_in (unauthenticated)
|
root → /units (authenticated), /sign_in (unauthenticated)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## JavaScript Conventions
|
||||||
|
|
||||||
|
### No manual fetch() — use Turbo
|
||||||
|
|
||||||
|
Never make AJAX requests with `fetch()` in JavaScript. Use Turbo's built-in mechanisms instead:
|
||||||
|
|
||||||
|
- **Links/buttons that trigger server actions**: use `data: {turbo_stream: true}` on the element (link or button_to form).
|
||||||
|
- **Dynamic form submissions from JS** (where HTML alone isn't enough): create a form element, append hidden inputs, and call `form.requestSubmit()`. Turbo intercepts it automatically — no manual CSRF handling, no `Turbo.renderStreamMessage()`.
|
||||||
|
```javascript
|
||||||
|
var form = document.createElement('form');
|
||||||
|
form.action = url; form.method = 'post'; form.dataset.turboStream = 'true';
|
||||||
|
// append hidden inputs...
|
||||||
|
form.addEventListener('turbo:submit-end', function() { form.remove(); });
|
||||||
|
document.body.appendChild(form);
|
||||||
|
form.requestSubmit();
|
||||||
|
```
|
||||||
|
- **Server-rendered HTML**: use ERB partials and Turbo Stream views (`*.turbo_stream.erb`), never build HTML in JavaScript.
|
||||||
|
|
||||||
|
### No HTML generation in JavaScript
|
||||||
|
|
||||||
|
Never use JavaScript to build and insert HTML (no `innerHTML =`, no `createElement` trees for content). Render HTML server-side in ERB partials; update the DOM via Turbo Stream actions (`replace`, `update`, `append`, etc.).
|
||||||
|
|
||||||
## Database Requirements
|
## Database Requirements
|
||||||
|
|
||||||
The database must support:
|
The database must support:
|
||||||
|
|||||||
@@ -147,27 +147,25 @@ window.readoutUnitChanged = readoutUnitChanged
|
|||||||
|
|
||||||
function setDefaultUnit(button) {
|
function setDefaultUnit(button) {
|
||||||
var select = button.closest('tr').querySelector('select[data-default-unit-id]');
|
var select = button.closest('tr').querySelector('select[data-default-unit-id]');
|
||||||
var params = new URLSearchParams();
|
var form = document.createElement('form');
|
||||||
params.append('quantity[default_unit_id]', select.value);
|
form.action = button.dataset.path;
|
||||||
|
form.method = 'post';
|
||||||
fetch(button.dataset.path, {
|
form.dataset.turboStream = 'true';
|
||||||
body: params,
|
var methodInput = document.createElement('input');
|
||||||
headers: {
|
methodInput.type = 'hidden'; methodInput.name = '_method'; methodInput.value = 'patch';
|
||||||
'Accept': 'text/vnd.turbo-stream.html',
|
var unitInput = document.createElement('input');
|
||||||
'X-CSRF-Token': document.head.querySelector('meta[name=csrf-token]').content,
|
unitInput.type = 'hidden'; unitInput.name = 'quantity[default_unit_id]'; unitInput.value = select.value;
|
||||||
'X-Requested-With': 'XMLHttpRequest'
|
form.appendChild(methodInput);
|
||||||
},
|
form.appendChild(unitInput);
|
||||||
method: 'PATCH'
|
form.addEventListener('turbo:submit-end', function(event) {
|
||||||
})
|
if (event.detail.success) {
|
||||||
.then(response => {
|
|
||||||
if (response.ok) {
|
|
||||||
select.dataset.defaultUnitId = select.value;
|
select.dataset.defaultUnitId = select.value;
|
||||||
readoutUnitChanged(select);
|
readoutUnitChanged(select);
|
||||||
}
|
}
|
||||||
return response.text();
|
form.remove();
|
||||||
})
|
});
|
||||||
.then(html => Turbo.renderStreamMessage(html))
|
document.body.appendChild(form);
|
||||||
.catch(err => console.error('setDefaultUnit failed:', err));
|
form.requestSubmit();
|
||||||
}
|
}
|
||||||
window.setDefaultUnit = setDefaultUnit
|
window.setDefaultUnit = setDefaultUnit
|
||||||
|
|
||||||
@@ -340,22 +338,17 @@ window.dragEnd = dragEnd
|
|||||||
|
|
||||||
function drop(event) {
|
function drop(event) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
|
var idParam = event.currentTarget.getAttribute("data-drop-id-param")
|
||||||
var params = new URLSearchParams()
|
|
||||||
var id_param = event.currentTarget.getAttribute("data-drop-id-param")
|
|
||||||
var id = event.currentTarget.getAttribute("data-drop-id").split("_").pop()
|
var id = event.currentTarget.getAttribute("data-drop-id").split("_").pop()
|
||||||
params.append(id_param, id)
|
var form = document.createElement('form');
|
||||||
|
form.action = event.dataTransfer.getData("text/plain");
|
||||||
fetch(event.dataTransfer.getData("text/plain"), {
|
form.method = 'post';
|
||||||
body: params,
|
form.dataset.turboStream = 'true';
|
||||||
headers: {
|
var input = document.createElement('input');
|
||||||
"Accept": "text/vnd.turbo-stream.html",
|
input.type = 'hidden'; input.name = idParam; input.value = id;
|
||||||
"X-CSRF-Token": document.head.querySelector("meta[name=csrf-token]").content,
|
form.appendChild(input);
|
||||||
"X-Requested-With": "XMLHttpRequest"
|
form.addEventListener('turbo:submit-end', function() { form.remove(); });
|
||||||
},
|
document.body.appendChild(form);
|
||||||
method: "POST"
|
form.requestSubmit();
|
||||||
})
|
|
||||||
.then(response => response.text())
|
|
||||||
.then(html => Turbo.renderStreamMessage(html))
|
|
||||||
}
|
}
|
||||||
window.drop = drop
|
window.drop = drop
|
||||||
|
|||||||
Reference in New Issue
Block a user