A little trick for grouping fields in an HTML form
Imagine this scenario: You have a group of fields that represent a person. Maybe the form you're building allows you to create a company, but you also want to be able to add employees of the company.
You don't know how many employees your users will add, so you add a little JavaScript button that makes it possible to add more.
How do you name your fields?
How we used to do it
You probably know this is the wrong way:
Person 1:
<label>First name</label>
<input name="first_name1">
<label>Last name</label>
<input name="last_name1">
<label>Email</label>
<input name="email1">
To pull those out in the backend will require string manipulation—and imagine parsing out the number from that string field name when some numbers have one digit, but then all of a sudden you have 10 employees and now some of the fields have two digits at the end instead. Fail. Don't do it.
Here's the more common suggestion: use the field name array syntax:
Person 1:
<label>First name</label>
<input name="first_name[]">
<label>Last name</label>
<input name="last_name[]">
<label>Email</label>
<input name="email[]">
Person 2:
<label>First name</label>
<input name="first_name[]">
<label>Last name</label>
<input name="last_name[]">
<label>Email</label>
<input name="email[]">
This seems like a great idea, and it is—but when you parse the input on the other end, you're probably expecting something like this:
person1 = ['Jim', 'Barber', 'jim@barber.com'];
person2 = ['Amira', 'Sayegh', 'amira@sayegh.com'];
Sadly, that's not what you get. Instead, you get this:
first_name = ['Jim', 'Amira'];
last_name = ['Barber', 'Sayegh'];
email_name = ['jim@barber.com', 'amira@sayegh.com'];
Parsing that together is not awful, but it can get really clumsy—especially as you add more fields.
What's this all about then
Fear not! There is a better solution!
If you set your fields to be grouped by "children" of a parent field, and give each "child" a numeric index, they'll all returned grouped, and then they'll return the way you're expecting. So people
is our "parent field", people[1]
is our first "child", and people[1][first_name]
is that child's first property.
Person 1:
<label>First name</label>
<input name="people[1][first_name]">
<label>Last name</label>
<input name="people[1][last_name]">
<label>Email</label>
<input name="people[1][email]">
Person 2:
<label>First name</label>
<input name="people[2][first_name]">
<label>Last name</label>
<input name="people[2][last_name]">
<label>Email</label>
<input name="people[2][email]">
And take a look at what we get now:
people = [
[
'first_name' => 'Jim',
'last_name' => 'Barber',
'email' => 'jim@barber.com
],
[
'first_name' => 'Amira',
'last_name' => 'Sayegh',
'email' => 'amira@sayegh.com
]
]
Boom, baby.
Bonus
Here's a quick bit of ES6 JavaScript to show one way you might want to do this:
<form method="post">
<div id="people-container">
<h3>Person 1:</h3>
<p>
<label>First name</label><br>
<input name="people[1][first_name]">
</p>
<p>
<label>Last name</label><br>
<input name="people[1][last_name]">
</p>
<p>
<label>Email</label><br>
<input name="people[1][email]">
</p>
<h3>Person 2:</h3>
<p>
<label>First name</label><br>
<input name="people[2][first_name]">
</p>
<p>
<label>Last name</label><br>
<input name="people[2][last_name]">
</p>
<p>
<label>Email</label><br>
<input name="people[2][email]">
</p>
</div>
<a href="javascript:;" id="add-new-person">Add new person</a>
<p>
<input type="submit">
</p>
</form>
<script>
let i = 3;
document.getElementById('add-new-person').onclick = function () {
let template = `
<h3>Person ${i}:</h3>
<p>
<label>First name</label><br>
<input name="people[${i}][first_name]">
</p>
<p>
<label>Last name</label><br>
<input name="people[${i}][last_name]">
</p>
<p>
<label>Email</label><br>
<input name="people[${i}][email]">
</p>`;
let container = document.getElementById('people-container');
let div = document.createElement('div');
div.innerHTML = template;
container.appendChild(div);
i++;
}
</script>
On CodePen:
See the Pen HTML form submission with multiple sub items by Matt Stauffer (@mattstauffer) on CodePen.
Bonus
I remembered that I wanted to write this article as I was listening to a great Full-Stack Radio episode with Jonathan Reinink where they talk about forms for an hour. It's good stuff. Take a listen.
Also, Adam has written a little about this same problem on his blog—but he chose to solve it on the server side instead. Take a look: Cleaning up form input with transpose
Comments? I'm @stauffermatt on Twitter
Tags: javascript • html • forms