DEV Community

loading...

Handling Form Submissions

codefinity profile image Manav Misra ・4 min read

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:

  1. 'One Line' Fat Arrow Functions
  2. Object Destructuring
  3. filter
  4. 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'.
}
Enter fullscreen mode Exit fullscreen mode

Alt Text

This could just be logged, 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:

  1. Use the Document Object Model (DOM) to 'query' for the form.
  2. Listen 👂🏽 for a "submit" event.
  3. 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();
})
Enter fullscreen mode Exit fullscreen mode

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.'

Logging form elements after submission

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

'Hold On To Your Butts'

Array.from(event.target.elements)
      .filter(({ id }) => id)
      .reduce((accumulator, { id, value }) => ({
        ...accumulator,
        ...{ [id]: value },
      }))
Enter fullscreen mode Exit fullscreen mode

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 },
        })
Enter fullscreen mode Exit fullscreen mode

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 },
})
Enter fullscreen mode Exit fullscreen mode

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 ids are properly used for inputs.

Discussion

pic
Editor guide