“How do I turn FormData into a query string?” you ask. This typically involves what we would call “serializing” your data.
It’s likely that you want to end up with something like this:
fullname=Todd+Motto&pizza=pepperoni&size=large&quantity=2
Alongside some HTTP Headers of Content-Type
set to application/x-www-form-urlencoded
- this will pass along nicely to your backend application.
In this article, we’re going to look at the HTML for a basic form that will then have a submit event bound to it - and once it fires we’ll be using the FormData
API and converting it to a query string.
Free eBook
Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.
- Observables and Async Pipe
- Identity Checking and Performance
- Web Components <ng-template> syntax
- <ng-container> and Observable Composition
- Advanced Rendering Patterns
- Setters and Getters for Styles and Class Bindings
For this, we’ll be using the new URLSearchParams API to our advantage.
Let’s look at the HTML we have for our Pizza form:
<form name="order">
<label>
Your name
<input type="text" name="fullname">
</label>
<label>
What pizza would you like?
<select name="pizza">
<option value="pepperoni">Pepperoni</option>
<option value="meaty">Meaty</option>
<option value="cheesey">Cheesey</option>
</select>
</label>
<div>
What size?
<label>
Small
<input type="radio" name="size" value="small">
</label>
<label>
Medium
<input type="radio" name="size" value="medium">
</label>
<label>
Large
<input type="radio" name="size" value="large">
</label>
</div>
<label>
How many?
<input type="number" name="quantity" value="1">
</label>
<button type="submit">
Submit
</button>
</form>
So initially, we have a few name
properties on the form fields:
- fullname
- pizza
- size
- quantity
It makes sense that we’ll expect to capture these values with the FormData
API.
To do that, let’s setup an event listener and create a new instance of FormData
:
const form = document.forms.order;
function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
}
form.addEventListener('submit', handleSubmit);
What does const formData
now contain? As FormData
is a constructor, it will return us a new instance of FormData
- which means it has some useful methods on it.
If we try to console.log(formData)
directly, we’ll see this:
FormData {}
__proto__: FormData
This isn’t very helpful - I can’t see any properties or any of my data. Turns out the methods that do exist are on the FormData.prototype
, which has a few methods that allow us to have a look inside at our data, to ensure we’re constructing it properly.
Using FormData.entries
Let’s begin with .entries()
, which returns us an Iterable
object. Because it returns an Iterable
, we can either iterate over it (via a loop) or make use of a newer JavaScript feature - the Spread syntax:
function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const data = [...formData.entries()];
console.log(data);
}
Which then outputs a multi-dimensional array in the console
upon successful completion of the form:
[
["fullname", "Todd Motto"],
["pizza", "pepperoni"],
["size", "large"],
["quantity", "2"]
]
A multi-dimensional array can easily be converted across to simple key-value pair syntax.
Using encodeURIComponent
Evolving our function with the Array map operator, this is typically what we’d need to do to assemble a query string to be sent off to the server:
function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const data = [...formData.entries()];
const asString = data
.map(x => `${encodeURIComponent(x[0])}=${encodeURIComponent(x[1])}`)
.join('&');
console.log(asString);
}
This now gives us:
fullname=Todd%20Motto&pizza=pepperoni&size=large&quantity=2
And we could stop there, or perhaps you’re already there.
Note that this method uses
%20
to space my name (fullname=Todd%20Motto
).
Introducing a new addition to the JavaScript language, URLSearchParams. Browser support is Microsoft Edge and everyone else.
Using URLSearchParams
We can now introduce URLSearchParams
, which allows us to work with a query string of a URL.
This is great because if we pass our multi-dimensional FormData
array into it, we’ll get a perfectly formatted query string ready to send - and for minimal effort. That’s what I love about this new API.
Refactoring our function, we can advocate this new approach and create a much more readable line of code :
function handleSubmit(event) {
event.preventDefault();
const formData = new FormData(event.target);
const asString = new URLSearchParams(formData).toString();
console.log(asString);
}
This then gives us:
fullname=Todd+Motto&pizza=pepperoni&size=large&quantity=2
The only thing I noticed that’s different from
encodeURIComponent
and the result fromURLSearchParams
is the former uses the%20
approach versus the above+
approach to join words (seefullname=Todd+Motto
above).
Try the live StackBlitz demo:
So there you have it. The FormData
API is lovely to use and effortless to integrate, it’s an integral part of the toolkit. Alongside URLSearchParams
we can see it’s also a super efficient and readable solution to getting your data formatted and ready to send to your server.
Free eBook
Directives, simple right? Wrong! On the outside they look simple, but even skilled Angular devs haven’t grasped every concept in this eBook.
- Observables and Async Pipe
- Identity Checking and Performance
- Web Components <ng-template> syntax
- <ng-container> and Observable Composition
- Advanced Rendering Patterns
- Setters and Getters for Styles and Class Bindings
I hope you enjoyed the post, and if you’d love to learn more please check out my JavaScript courses, where you’ll learn everything you need to know to be extremely good and proficient. Enjoy and thanks for reading!