Overview
We will write a 'pure function' that can be used to handle form submissions for any semantically written HTML-CSS form ✨. We will utilize:
- 'One Line' Fat Arrow Functions
- Object Destructuring
filter
reduce
The end result 🥅 will be that upon a user clicking 'submit,' we will dynamically collect all of the form values, pair them with the corresponding input and wrap it all up into an object.
So, we're going for something like this:
{
name: "Mark Galloway",
// ... other values from the form paired with 'input ids'.
}
This could just be log
ged, as we do here, or sent to a server, whatever. The point 📝 is that we will have a pure function that we can use across any/all similar applications.
Listening 👂🏽 For Form Submissions
As per: <script src="./scripts.js"></script>
, let's open up 'scripts.js' and start with the following steps:
- Use the Document Object Model (DOM) to 'query' for the
form
. - Listen 👂🏽 for a
"submit"
event. - Prevent the browser from doing its 'default behavior' of submitting to a back-end server (which doesn't exit here).
Here's the code to cover all of those steps 👆🏽.
document.querySelector('form').addEventListener('submit', event => {
event.preventDefault();
})
This line: addEventListener("submit", (event) => {
is using a callback function written in ES2015/ES6 'fat arrow'/'lamda' syntax. For more information:
That parameter, event
is bound to the actual 'submission event' that occurs in the browser.
We are also chaining ⛓️ each step together with .
s as we move along.
Try doing console.log(event)
right below the preventDefault()
. You will see that it's nothing but another JS object. This is the result of the DOM API provided by the browser - it conveniently models most of the things as JS objects b/c that's all that JS really understands.
event.target.elements
Inside of the callback function, right underneath: event.preventDefault()
, do: console.log(event.target)
👈🏽 Again, the 'submission event' is modeled as an object, event.
Currently, we are accessing a 🔑, target
which will provide as an 'object model' of whatever Element 'triggered' this submission event - i.e. the form
itself.
Now, fill out the form and check your 'dev tools console.'
We get a: HTMLFormControlsCollection
- basically it's all of the stuff from inside the form
from whence this submission event occurred.
Turn HTMLFormControlsCollection
into our Object Literal via Array Methods - filter
and reduce
Array.from(event.target.elements)
.filter(({ id }) => id)
.reduce((accumulator, { id, value }) => ({
...accumulator,
...{ [id]: value },
}))
To quote from an EnVogue song, "And, now it's time for a breakdown..."
Array.from(event.target.elements)
To use filter
and reduce
we first need to have an array: Array.from(event.target.elements)
filter
Out All Elements That Have an id
To better understand, we can first write like this one: .filter(element => element.id)
filter
is a predicate callback function. This means that whenever it only returns elements that are 'truthy.' element => element.id
says, "Take in some element and return it if it's true that it has a 🔑, id."
Now, we can use object destructuring to shorten up that code. Instead of bringing in the whole element
and then using .
to try to access id
, we can just 'pull out' the id
🔑 in the parameter: ({id})
. The rest of it works the same way. "Is there an id? If so, send this element back out."
Yes, even though we are destructuring the id
, we still can return the entire element. We don't lose anything here: .filter(({ id }) => id)
reduce
All The Things Down to 1 Object
reduce
has a callback function that takes in 2 parameters. The first represents an 'accumulator' - here that means the 'object' that we are 'building up.' As we iterate over the filtered elements, we want to keep 'building' and returning this object, adding the next id
and value
(object restructuring again) each time.
// The callback function inside of 'reduce'
(accumulator, { id, value }) => ({
...accumulator,
...{ [id]: value },
})
Notice, => ({
. We are implicitly returning an object. But, we need the (
to clarify to JS that this is indeed what we are doing - otherwise, JS will be confused because we normally would have {
to indicate that we are opening up our function body.
...accumulator,
is 'spreading' - ...
- all of the 🔑/value pairs inside of accumulator
.
...{ [id]: value }
temporarily wraps up the id
and value
from the current form element (as we are iterating) into an object literal. This is immediately 'spread' and open.
Now, the 'spreads' of both accumulator
and {[id]: value}
are 'merged' together into our 'returned object.' This is what causes accumulator
to 'accumulate' or grow upon each _iteration.
// Spread all the things and return a new 'merged' object
=> ({
...accumulator,
...{ [id]: value },
})
Regarding, [
around id
- I leave it to you to determine the reason for that. Try taking it out and see what happens.
A Pure Function That Handles Any HTML Form Submission!
We have created a 'pure' function - one that can be 'copied/pasted' into any program anywhere and w/o making any changes to its code, tt will just work! Our only assumption is that we are writing semantic, accessible HTML where id
s are properly used for input
s.
Top comments (0)