The frontend has a table with hundreds of rows. Each row consists of different inputs (text
, file
, checkbox
etc.).
The user can change any row and any input he wants before clicking the "Save Changes" button. It then needs to update the data in the database.
I want to only send the modified data, or at least only the modified rows entirely, instead of all the rows to prevent sending of too much data (I've seen some code where the developer just wrapped the entire table in a form element and sent everything).
This is how I currently do it: I create an empty form with just the submit button ("Save Changes"), and whenever a row is modified, I add a "form" attribute with the form id, "connecting" between the modified rows and the form (source: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#form).
Also, while creating the table on page load, I construct the inputs name attributes in a way that will connect each row to the DB row's id, for example:
name="data[{{$row->id}}][email]"
Then, when I send it to the backend, the data is organized in arrays by the id
s of which I need to update in the database. Then it's simply iterating it and updating the DB:
<form id="my-form">
<input type="submit" value="Save Changes">
</form>
<table id="my-table">
@foreach($rows as $row)
<tr>
<td><input type="text" name="data[{{$row->id}}][username]" value="Foo"></td>
<td><input type="email" name="data[{{$row->id}}][email]" value="[email protected]"></td>
<td><input type="file" name="data[{{$row->id}}][picture]"></td>
</tr>
@endforeach
</table>
<script>
document.querySelector('#my-table').addEventListener("change", (event) => {
let row = event.target.closest('tr');
let rowInputs = row.querySelectorAll('input');
rowInputs.forEach((input) => {
input.setAttribute('form', 'my-form');
});
});
document.querySelector('#my-form').addEventListener("submit", (event) => {
event.preventDefault();
let formData = new FormData(event.target);
for (let pair of formData.entries()) {
console.log(`${pair[0]}: ${pair[1]}`);
}
});
</script>
Working example: I made a simple working example with only 2 rows and fake id
s to illustrate (because the @foreach
may contain hundreds of rows):
document.querySelector('#my-table').addEventListener("change", (event) => {
let row = event.target.closest('tr');
let rowInputs = row.querySelectorAll('input');
rowInputs.forEach((input) => {
input.setAttribute('form', 'my-form');
});
});
document.querySelector('#my-form').addEventListener("submit", (event) => {
event.preventDefault();
let formData = new FormData(event.target);
for (let pair of formData.entries()) {
console.log(`${pair[0]}: ${pair[1]}`);
}
});
<form id="my-form">
<input type="submit" value="Save Changes">
</form>
<table id="my-table">
<tr>
<td><input type="text" name="data[24][username]" value="Foo"></td>
<td><input type="email" name="data[24][email]" value="[email protected]"></td>
<td><input type="file" name="data[24][picture]"></td>
</tr>
<tr>
<td><input type="text" name="data[500][username]" value="Bar"></td>
<td><input type="email" name="data[500][email]" value="[email protected]"></td>
<td><input type="file" name="data[500][picture]"></td>
</tr>
</table>
Then in the backend I can save the changes to the DB:
Note: The below code is just a partial code for better understanding of the above. Because I used Laravel syntax, I wanted to give a more "full picture" of the process.
public function save_changes(Request $request)
{
// User is authenticated, and inputs are validated before the update
foreach($request->all() as $id => $data)
{
DB::table('db_table')
->where('id', $id)
->update($data);
}
}