In the following article, I’m going to outline the notes I used when learning how to build a form-based JS app using ES6. This particular example is a “Recipe Box” (h/t Flatiron School for the excellent, in-depth review session this was based on!).
Remember: always have a console.log(something) in the body of each function as your are building to make sure things are connected! Console logging as I build out a program is one of the best things I’ve learned as a developer. When I was first starting out, it seemed useless, but the more work and life experience I have, the more I realize that being meticulous matters. Folks who work the line in a kitchen have mise en place. Writers use outlines and notes. Programmers debug somehow as they go (whether it’s using debugger, console log, TDD, or any other tool, the point is really just that you’re doing something.)
Do a HTML mockup (static) of the first portion that will be the first screen that the user will be interacting with:
First, create a simple event handler connected to the form. Start with something such as an event triggered upon submission. When you submit the form, console log something to see if the event handler is working.
Note: if all of a sudden there more than one event happening, or one event is triggering too many or incorrect responses, you have a problem with your event handler
Now, on form submission, write to the console the name of the ingredient in this case (or whatever the input for your form is).
This ensures that the event handler works and gets triggered at the right time. This means you can get the VALUE of the form and write it to the console. Make sure it’s the value from the form input field (it’s not the same as the form id value from the event listener!) Then clear the value after it has been logged to the console.
Since the connection has been tested, and you can be sure of the correct information, we want to dynamically generate a line item and append it to the unordered list in the HTML form on page submission.
First, we will do this in a way that is NOT keeping track of ingredients. There are no objects, etc. The first step is just getting it to appear on the page.
Make a new ingredient by creating an element of the desired type. In this example, that’s an <li>.
Then set the new instance into the value of this. Append the new ingredient to the type of element.
Essentially, the above is three steps: you’ve made the element, you have the value, now attach the value to the element.
Then, find the UL from the HTML that you created and add an id tag on the index.html file. Then, using getElementByID(), add the new element that you’ve just created, with value, into this UL.
Now, we are going to separate this out into its own function, and pass that function in as an argument into the event listener at the top
This is solving the problem in a stateless way — we add an LI element, and then that element essentially is no longer accessible. But if we want to track this, we will have to add something to keep track of state and context — we want to maintain state and the runtime to have some kind of context of how many ingredients we have…
Here is the stateless way:
What is the difference between an object and an instance of a class? The class is more like a factory. It can make many objects of that class following the same specifications over and over again.
Alternatively, you can use an array to keep track of objects.
This example is about what is the most minimal approach, the simplest way that we can access, store, and work with data. Other more complicated data structures would be appropriate for larger applications.
So in this case, make an object with a key of “name”, and store all of these objects in an array. So start by assigning the array to a constant.
How do we determine let or const? It depends on reassignment. Whenever we say var x = that is an assignment. So if we change this, and we change where this is pointing, we should use LET. If it will never change, then we can use CONST.
We’re also going to make a new function that will take state into consideration. Remember always to preventDefault(). Remove the reference to the previous function (addAnIngredient) and replace it with a new function that we’re making now:
Then, work on the object. This new function should create a new function, with a name, and we should log the object to the console. Create a new object, add the name property, and set it to the input value.
Here is the new refactored function using objects:
Here is an example of the object the function prints to the console using Chrome developer tools.
Now, each time an element is added, it simply creates an object. But, a new element is created, the other one kind of gets thrown away. And that’s not great! So, now, as each object is created, add it to the array. Have the function console log the entire array each time it updates, to be sure that the objects are adding correctly.
Now, there is a JS version of ingredients, but there needs to be an <li> that appends to the HTML for the objects to render as text on a page. To do this, iterate over that array and generate a string that represents all of the <li> in the same function.
Then, find the div in the index.html file where the text should go and replace its contents. Why replace? It is marginally less efficient, and could become extremely inefficient if the array became huge, but by re-rendering every time, it’s easier to have a single source of truth, or one variable that is managing our state.
<li> tags always surround whatever interpolated value that is passed through, and then joined. The entire string is being passed to the inner HTML and then the HTML is reading this as an HTML file.
What would that really look like?
This string is then passed to the index.html file, where it is read just like any other HTML. How does it get passed? Set the innerHTML of the div (as mentioned above) to the value of that string.
This is a point to take a step back and refactor the code, because this big function is doing many different types of activities:
— creating an object
— adding the object to a collection
— rendering something
These are three different types of functionality! Get the most basic version running (as is all here above), and then we can worry about separation of responsibilities — why is this an easier method? All the necessary code is here, doing the right thing — splitting it it up now is quite simple.
Now, move on to selecting ingredients from the pantry, adding them to a recipe (checkboxes), and adding notes (a big text field) to the recipe to make the full recipe!
Start again with a static model to make sure the checkboxes are working, a text area to describe the steps or notes, and a save recipe. This should all go in a div below the ingredients container, which you can think of as a separate section of the application.
Now that this is mocked up, iterate over the array, and add the ingredients to the checkbox list, and then, determine whether or not they are checked off.
How to do this with code? .checked() returns a boolean value!
First, dynamically generate the checkboxes HTML string.
Just like before, a function that returns a long string of text (from all of the ingredients array) with the proper html tags is all that is needed in order to generate those checkboxes.
Now pass the outcome of this function to the HTML. When adding an ingredient, make the list and make a checkbox entry.
Now, iterate through the array and find all of the checkboxes that are checked and find the matching ingredient (that is connected to the checked checkbox)
One good CSS Selector to use in this instance is document.querySelectorAll(“input[type=’checkbox’]”)) to select all of the checkboxes.
NOTE: This is not an array! it looks like an array. It is a node list — all the elements that match that query selector. We can use forEach() here, but then we will have to do something differently.
For each → we want to pass in a callback function, see if the boxes are checked, find the matching ingredient, and add the name of the matching ingredient object to an array.
See below, using anonymous arrow functions (one of the many lovely perks of ES6 in this example!)
All of this comes from the input field, which allows us to return an object that represents a recipe — you might want to have a recipe or an object array of recipes.
Again, as done previously, iterate over the array to generate the HTML to generate the recipe. Set container.innerHTML = the string from the array.
This adds a recipe to the recipe collection!