If you’re not completely turned off by the idea of building yet another todolist app, and you’re relatively new to JavaScript and Front-end development,this tutorial is for you. Here’s a live demoof what you’ll be building.
This article on building a todo app with Angular CLI is the first in a four-part series on how to write a todo application in Angular 2: Part 0 — The Ultimate Angular CLI Reference Guide.
Join 25 million people and teams that organize, plan, and collaborate on tasks and projects with Todoist. 'The best to-do list' by The Verge. Download Simple Todo: Quick and Easy and enjoy it on your iPhone, iPad, and iPod touch. Simple Todo app with two categories. One category is for tasks that need to be tackled immediately and the other one is for tasks that can wait.
Prerequisites
This tutorial assumes that you have a basic knowledge of JavaScript.Essentially, you need to know what variables, arrays, functions and objects are,but you do not need to have prior experience with building JavaScriptapplications.
Get started
The todo list app we’ll build in this tutorial will be pretty basic. A user canadd a task, mark a task as completed and delete an already added task. I’llexplain how to build each feature, but you must follow along by typing thecode and running it on your end to get the most out of this tutorial.
I recommend using JSFiddle while working through thistutorial, but feel free to use other code playgrounds or your local text editorif you prefer. Without further ado, grab the markup and styles for the appon JSFiddle. If you’re usingJSFiddle, you can hit the Fork button to create a new fiddle of your own.
Add a todo
The first thing we need to do is set up an array where we’ll place the todo listitems. Each todo item will be an object with three properties:
text
, a stringwhich holds whatever the user types into the text input, checked
, a booleanwhich helps us know if a task has been marked completed or not, and id
, aunique identifier for the item.Once a new task is added, we’ll create a new todo object, push it into thearray and render the value of the
text
property on the screen. When a todo ismarked as completed, we’ll toggle the checked
property to true
, and when theuser deletes a todo, we’ll locate the todo item in the array using its id
andremove it.Let’s start by adding a todo item to our list. To do so, we need to listen forthe
submit
event on the form element, and then invoke a new addTodo()
function when the form is submitted.Update the JavaScript pane on JSFiddle to look like this:
By default, when a form is submitted, the browser will attempt to submit it to aserver which will cause a page refresh. To prevent that from happening, we canstop the default behaviour by listening for the
submit
event on the form, andusing event.preventDefault()
.Next, we select the text input andtrimits value to remove whitespace from the beginning and end of the string, andthen save it in a new variable called
text
. If the text
variable is notequal to an empty string, we pass the text to the addTodo()
function which isdefined above the event listener.Within the function, we create a new object for the task and add the propertiesI mentioned earlier. The
text
property is set to the function argument,checked
is initialised to false
, and id
is initialised to the number ofmilliseconds elapsed since January 1, 1970. This id
will be unique for eachtodo item unless you can add more than one task in a millisecond, which I don’tthink is possible.Finally, the task is pushed to the
todoItems
array, and the array is logged tothe console. In the form event listener after addTodo(text)
, the value of thetext input is cleared by setting it to an empty string, and it’s also focused sothat the user can add multiple items to the list without having to focus theinput over and over again.Add a few todo items and view the
Take a breather and see the complete code at the end of this step.todoItems
array in your browser console. Youwill see that each todo item is represented by an object in the array. If you’reusing JSFiddle, you may need to check the built-in console provided by JSFiddle.Render the todo items
Once a new todo item is added to the
todoItems
array, we want the app to beupdated with the item rendered on the screen. We can do this pretty easily byappending a new li
element for each item to the .js-todo-list
element in theDOM.To achieve this, add a new
renderTodo()
function above addTodo()
:The
renderTodo()
function takes a todo
object as its only parameter. Itconstructs a li
DOM node using thedocument.createElement
method. On the next line, the class
attribute is setto todo-item ${isChecked}
. The value of isChecked
will be an empty string ifthe checked
property in the todo
object is false
. Otherwise, it will be‘done’. You will see the effect of this ‘done’ class in the next section.Next, a
data-key
attribute is also set on the li
element. It is set tothe id
property of the todo
object and will be used to locate a specifictodo item in the DOM later in the tutorial. Followingthat, the contents of the li
element are set using the innerHTML
method andfinally, the li element is inserted as the last child of the .js-todo-list
element.Change the
console.log(todoItems)
line in addTodo()
to renderTodo(todo)
asshown below so that the renderTodo()
function is invoked each time a new todoitem is added.Try it out by adding a few todo items. They should all render on the page.
Take a breather and see the complete code at the end of this step.Mark a task as completed
Let’s add the ability to mark a task as completed. To do so, we need to listenfor the click event on the checkbox and toggle the
checked
property onthe corresponding todo item.Add the following code at the bottom of the JavaScript pane to detect the todoitem that is being checked off:
Instead of listening for clicks on individual checkbox elements, we arelistening for clicks on the entire list container. When a click event occurs onthe list, a check is done to ensure that the element that was clicked is acheckbox. If so, the value of
data-key
on the checkbox’s parent element isextracted and passed to a new toggleDone()
function (shown below) which shouldbe placed below the addTodo()
function.This function receives the key of the list item that was checked or uncheckedand finds the corresponding entry in the
todoItems
array using thefindIndexmethod. Once we have the index of the todo item, we need to locate it in thetodoItems
array using bracket notation. The value of the checked
property onthe todo item is then set to the opposite value.Finally, the
renderTodo()
function is called with the todo object passed in.If you run the code now and try checking off an item, it will duplicate the todoitem instead of checking off the existing one.To fix this, we need to check if the current todo item exists in the DOM first, and replace it with the updated node if it does.Change your
renderTodo()
function as shown below:First, the current todo item is selected. If it exists in the DOM, the element will be returned and subsequently replaced. Ifthe item does not exist (as is the case for new todo items), it will be addedat the end of the list.
Take a breather and see the complete code at the end of this step.Delete todo items
Similar to the way we implemented the last feature, we’ll listen for clicks onthe
.js-delete-todo
element, then grab the key of the parent and pass it offto a new deleteTodo
function which will remove the corresponding todo objectin todoItems
array send the todo item to renderTodo()
to be removed from theDOM.First, let’s detect when the delete button is clicked:
Next, create the
deleteTodo()
function below toggleDone()
as shown below:The
renderTodo()
function also needs to be updated as follows:Now, you should be able to delete tasks by clicking the delete button.
Take a breather and see the complete code at the end of this step.Add an empty state prompt
An empty state occurs when there is no data to show in the app. For example,when the user hasn’t added a todo yet (first use) or when the user has clearedthe list. It is important to account for this state when designing anapplication.
Many apps use the empty state to show a prompt that tells the user what to do.Here is a real-world example of what a good empty state prompt looks like:
Once there are no tasks to display, we’ll add a prompt that encourages the userto add a new task. This feature can be implemented with just HTML and CSS.
We will take advantage of the
:empty
CSS selector todisplay the prompt conditionally only if no items exist in the list.Add the following code for the empty state prompt in the HTML pane as shownbelow:
Then add some styles for the empty state in your CSS:
While this looks just fine, the problem is that the message persists even when atask has been added to the list. The intended behaviour is for the prompt todisappear once a todo has been added and only reappear when there are no moretasks to display.
This bit of CSS will give us what we want:
The
Take a breather and see the complete code at the end of this step..empty-state
element is hidden from view by default with display: none
and only comes into view when .todo-list
is empty. We’re using the:empty selector todetect when .todo-list
is empty, and the sibling selector (+
) to target.empty-state
and apply display: flex
to it only when .todo-list
is empty.A subtle bug
One issue I encountered while working on this tutorial is that the empty statewouldn’t return into view when all existing tasks are deleted.
Apparently, some whitespace persists in the
.todo-list
element even after allits child li
elements have been removed, so it’s not considered to be emptyand the styles defined with the :empty
selector does not kick in. To fix thisissue, we need to clear any whitespace from the element in our JavaScriptcode. Modify the renderTodo()
function as follows:The above code solves the problem, and the app now works as expected.
Simple Todo App In React
Take a breather and see the complete code at the end of this step.Persist the application state
Our todo list app is pretty much complete at this point, but let’s add one morefeature to make things a bit more interesting and realistic. At the moment, oncethe page is refreshed, all the todo items are cleared. Let’s prevent this bypersisting the application state to the browser’slocalstorage.
Add this line at the top of your
JSFiddle denies access to the window’s localStorage so you must run the code locally to test out this part of the tutorial.renderTodo()
function:Only strings may be stored in the localStorage so we need to convert our
todoItems
array to a JSON string first before passing it to the setItem
method which adds a new data item under the specified key.Each time the
renderTodo()
function is invoked, the value of todoItemsRef
inthe localStorage will be replaced with the current value of the todoItems
array. This way, the array and the corresponding localStorage reference is keptin sync.You can test this out by opening your browser dev tools, navigate to theApplication tab and monitor the Local Storage section. If you’re notusing Chrome, the dev tools may be organised differently.
The final step is to render any existing todo list items when the page isloaded. Add the following code snippet at the bottom of the JavaScript pane:
When the
DOMContentLoaded
event is fired, we proceed to retrieve the value oftodoItemsRef
from the localStorage. If it exists, the JSON.parse
method isused to convert the JSON string back to its original array form and save it intodoItems
.Following that,
Take a breather and see the complete code at the end of this step.renderTodo()
is invoked for each todo object present in thearray. This causes any saved todo items to be rendered as soon as the pageloads.Conclusion
In this tutorial, we successfully built a todo list app that allows auser to add new tasks, mark a task as completed and delete old ones. We alsodiscussed the importance of accounting for empty states when designing anapplication, then proceeded to talk about a potential problem when using the
:empty
selector and how to fix it.Finally, we discussed persisting the application state to the browser’slocalStorage and how to get around its limitations using
JSON.stringify
andJSON.parse
. If a section or piece of code is not clear to you, feel free toleave a comment below and I’ll get back to you as soon as possible.Thanks for reading, and happy coding!
![Simple to do Simple to do](https://www.any.do/v4/images/translations/en/to-do-list/main-image@2x.jpg)
If creating the Hello, World! example was a celebration of you getting your feet wet with React, creating the quintessential Todo List app is a celebration of you approaching React mastery! In this tutorial, we are going to tie together a lot of the concepts and techniques you've learned to create something that works as follows:
The way this Todo List app works is pretty simple. Type in a task or item or whatever you want into the input field and press Add (or hit Enter/Return). Once you've submitted your item, you will see it appear as an entry. You can keep adding item to add additional entries and have them all show up:
To remove an item, just click on an existing entry. That entry will be removed. Pretty simple, right? In the following sections, we will build this app from scratch using a lot of the adrenaline-inducing techniques we've learned so far:
This is going to be a fun exercise where we build each part of the app and learn (in awesomely painstaking detail) how the various little things work along the way.
Onwards!
Getting Started
The first step is to create a new React app. From your command line, navigate to the folder you want to create your new project, and enter the following:
Press Enter/Return to run that command. A few moments later, a brand new React project will get created. Since we want to start from a blank slate, we are going to delete everything contained in our public folder and in our src folder.
By now, you know the drill. We need a starting point, so go ahead and create a new HTML document inside our public folder called index.html. Inside it, add the following content:
This page is pretty basic..as you can tell. The real magic is going to be happening in our src directory where our JavaScript and CSS files will live. In our src directory, create a new file called index.css and add the following style rules into it:
Once you have done this, let's add the JavaScript that rounds out our starting page. Within the same src directory, add a new file called index.js. Inside this file, add the following content:
Take a moment to look at what we've just added. By now, you should be fully familiar with what is going with the HTML, CSS, and JavaScript at this point. What we really have is the foundation. In the following sections, we'll build on top of this all the various pieces that make up the rest of our Todo List app.
Creating the Initial UI
Right now, our app doesn't do a whole lot. It doesn't look like much either. We'll deal with the functionality in a little bit, but first let's get the various UI elements up and running. That isn't very complicated for our app! The first thing we are going to do to is get our input field and button to appear. This is all done by using the div, form, input, and button elements!
All of that will live inside a component we are going to call TodoList. In your src folder, add a file called TodoList.js. Inside this file, add the following things:
Take a moment to glance at what we've added. There is a bunch of JSX that gets our form elements up and running. To use our newly created TodoList component, let's go back to index.js and reference it to see what our app looks like right now. Go ahead and make the following two changes:
Once you've done this, save all of your changes and preview in your browser. If everything worked, you'll see something that looks as follows:
Right now, we have our input field and submit button showing up. These two UI elements neither work nor look all that visually appealing. We'll fix that up in a little bit, but first let's talk about how we are going to add the rest of the app's functionality.
Building the Rest of the App
As you can imagine, getting the initial UI elements to show up is the easy part. Tying up all of the various visuals with the underlying data is where the real work lies. This work can roughly be divided into five parts:
- Adding items
- Displaying items
- Styling
- Removing items
- Animating items as they are added or removed
Individually, all of these little implementation details are easy to wrap our brain around. When we put them together, there are a few things to watch out for. We will look at all that and more in the following sections.
Simple Todos
Adding Items
The first major thing we'll tackle is setting up the event handlers and default form handling behavior to allow us to add an item. Go back to our form element and make the following highlighted change:
We listen for the submit event on the form itself, and we call the addItem method when that event is overheard. Notice that we aren't listening for any event on the button itself. The reason is that our button has a type attribute set to submit. This is one of those HTML trickeries where clicking on the button whose type is submit is the equivalent of the submit event on the form being fired.
Now that we've done this, it is time to create our addItem event handler that will get called when our form gets submitted. Add the following highlighted lines just above where we have our render function defined:
Looking at what we just added, all we did was define our addItem event handler and ensure the this keyword resolves properly. We still haven't done anything remotely close to actually adding a task, so let's start that by first defining our state object in the constructor:
Our state object isn't very complicated. We are just defining an items array/property that will be responsible for storing all of the various items that you can enter. All that is left to do is read the entered value from our input element and store it in our items array when the user submits it. The only complication here is actually reading the value from a DOM element. As you know, React puts up a gate between us and the DOM. It doesn't like it when we access DOM elements and fiddle with properties on them, but it does give us a loophole via refsthat we can use.
In our render function, make the following highlighted change:
What we are doing here is storing a reference to our input element in the appropriately named _inputElement property. To state this differently, anywhere inside this component we want to access our input element, we can do so by accessing _inputElement. Now that we have done this, it's time to fill out our addItem function with the following content:
Take a moment to look through what we are doing. We first create a variable called newItem that will store an object:
This object contains both the entered text as well as a unique key value set by the current time (Date.now()). Now, if it isn't clear why we are specifying the key, that's OK. You'll totally see why in a few moments.
Next, we are setting our state's items property with the following lines:
Note that we are ensuring our state object isn't modified. We are instead giving it an entirely new array made up of both the existing items data along with the newly entered data.
Lastly, we are clearing the value of our input element to make room for the next todo item. What may be less boring is this line here:
We are overriding this event's default behavior. The reason has to do with how form submission works. By default, when you submit a form, the page reloads and clears everything out. We definitely don't want that. By calling preventDefault, we block the default behavior. That's a good thing!
It's time to take stock of where we are right now. If you preview your app and check the browser console, you'll see our state object correctly populating with each new todo item we added:
I realize all of this might not seem like much, but we are making great progress. Seriously!
Displaying the Items
Having our todo items only show up in the console might be exciting for some of your users, but I am pretty certain most would probably want to see these items displayed directly on the page. To do this, we are going to rely on another component. To get started, let's call this component TodoItems, specify it in our TodoList component's render method, and pass in our items array as a prop.
All of that translated into markup and code will look as follows:
Once you've done this, add the import statement to the top of the document as well:
These two changes wrap up the work we want to do in TodoList.js for now. Next, let's go ahead and actually create our TodoItems component. In our src directory, create a new file called TodoItems.js and add the following content into it:
This might look like a lot of code to add in one giant swoop, but take a moment to look at what exactly you are adding. In our render function, we are taking the list of todo items (passed in as entries) and turning them into JSX/HTML-ish elements. We do that by calling map on our items and relying on the createTasks function:
The value stored by our listItems variable is an array of li elements containing the appropriate content to print. Notice that we are setting the key attribute (whose value, if you recall, we set earlier using Date.now()) on each element to make it easier for React to keep track of each element.
We turn this list of element into something we can show on screen with the following:
Once you have made this change, save all of the changes and preview the app in its current state (npm start if it isn't already running). If everything worked properly, you will not only be able to add items, you will be able to see them as well:
If what you see looks similar to what is shown in the screenshot, that's awesome! To celebrate, we are going to take a little break from looking at JS and JSX for a few seconds.
Styling our App
Right now, our app's awesome functionality isn't reflected in how the app currently looks. We are going to fix this easily by just adding one style sheet and putting all of the relevant style rules into it. In the src folder, create a new style sheet called TodoList.css and add the following style rules into it:
Once you've created this style sheet, we need to reference it. In TodoList.js, add a reference to this style sheet at the top:
If you preview your app after this change, it will look as follows:
As you can see, our app looks much nicer. All we did is just add some CSS, so from a functionality point of view, nothing has changed. We will make more progress on the functionality front next.
Removing Items
At this point, we can add items and see them appear. What we can't do is remove items once they've been added. The way we are going to allow users to remove items is by clicking on them directly. This seems straightforward to implement, right? The only thing to watch out for revolves around where to put all of our code. The items we click on are defined in TodoItems.js. The actual logic for populating the items lives in our state object in TodoList.js. To give you a preview of what to expect, we will be partaking in some shenanigans as we pass things between both of those components.
The first thing we will do is set up the event handler for dealing with the click event. Change the return statement under createTasks to look as follows:
![Simple todo list Simple todo list](https://zappy.zapier.com/CFDD10F7-7553-49C9-8BDC-03FCCEFA7101.png)
All we are doing is listening to the click event and associating it with an event handler called delete. Now, what may be new is our approach for passing arguments to the event handler. Because of how event arguments and event handlers deal with scope, we work around all of those issues by using an arrow function that allows us to maintain both the default event argument while allowing us to pass in our own arguments as well. If this seems bizarre, you may feel better knowing that this is a JavaScript quirk and has nothing to do with React :P
Anyway, after you've made this change, what we need to define next is our delete event handler. Make the following highlighted changes:
We define a function called delete that takes our argument for the item key. To ensure this resolves properly, we explicitly bind this in the constructor so that createTasks can properly resolve the delete function. Notice that our delete function doesn't actually do any deleting. It just calls another delete function passed in to this component via props. We'll work backwards from here and deal with that next.
In TodoList.js, take a look at our render function. When calling TodoItems, let's specify a prop called delete and set it to the value of a function called deleteItem: Bookreader 4 14 – reader for non drm e books.
This change ensures our TodoItems component now has knowledge of a prop called delete. This also means our delete function we added in TodoList actually connects. All that remains is actually defining our deleteItem function so that it can deal with deleting an item.
First, go ahead and add the deleteItem function to your TodoList component:
You can add it anywhere, but my preference is to put it just below where our addItem function lives. Take a look at what this code does. We are passing the key from our clicked item all the way here, and we check this key against all of the items we are storing currently via the filter method:
The result of this code running is simple. We create a new array called filteredItems that contains everything except the item we are removing. This filtered array is then set as our new items property on our state object:
This results in our UI updating with the removed item disappearing..forever. The last thing we need to do is deal with the usual shenanigans around this. Make the following change in the constructor:
This will ensure that all references to this inside deleteItem will reference the correct thing. There is just one more thing to do before we can call victory on deleting items. Open TodoList.css and make the following highlighted change and style rule addition:
This will provide the hover effect when you move the mouse cursor over the item you wish to remove. With this change done, our remove item functionality should be complete. If you preview your app now, try adding some items and removing them. It should work well. There is just one more thing..
Animation! Animation! Animation!
The very last thing we will do is add some animations to make adding and removing items look more natural! Now, there are many ways to animate something in React. You can use your traditional approaches like CSS animations, CSS transitions, requestAnimationFrame, Web Animations API, or even a popular animation library. All of these approaches will take you far..very far!
When it comes to animating the existence of an element, though, the traditional approaches we outlined run into some limitations. That is because the lifecycle of an element as it is about to be deleted from the DOM is entirely handled by React. We can definitely override some of the lifecycle methods to intercept an element deletion and interject our own animation logic, but that gets us a bit too far into the weeds. We don't want to deal with that right now.
Fortunately, the React community has come up with a handful of lightweight animation libraries that make animating adding and deleting elements really easy. One such library is Flip Move. Among many things, this library makes animating the addition and removal of list elements really simple.
To use this library, we need to first add it to our project. From your command line, make sure you are still in the same location as our todolist project and run the following command:
Hit Enter/Return to copy all the necessary things locally into our project's node_modules folder. That's all the setup required. Once you have done this, in TodoItems.js, add the following import statement at the top:
All that is left is to just tell our FlipMove component to animate our list of items. In our render function, make the following highlighted change:
All we are doing is wrapping our listItems (just prior to them getting printed) inside a FlipMove component and specifying the animation duration and the type of easing function to use. That's it. If you preview your app now, you'll now see that adding and removing items doesn't just suddenly happen. These items are smoothly animated instead.
Uncontrolled Components vs. Controlled Components
Form elements are interesting. These are elements that contain some state on their own. For example, your text element might have some content in it, you may have some items already selected in a drop-down, and so on. React is all about centralizing all state into its own little world, so it doesn't like that form elements have their own internal mechanism for storing state. The guidance is to synchronize all of the form data inside a React component by using events like onChange. These components that let React deal with form elements are known as Controlled Components.
While that is the guidance, it is a hassle to have every form element deal with keeping state in sync. The React developers get that as well. The workaround is to do nothing. We simply let form elements deal with their own state and use refs to access the values when needed. That is what we did in this example. When we have components that defer all state management to the form DOM element, these components are known as Uncontrolled Components.
Conclusion
Our Todo app is pretty simple in what it does, but by building it from scratch, we covered almost every little interesting detail React brings to the table. Not only that, we combined all of those interesting details together to create something that works. Now, here is a quick question for you: does everything we've done in this tutorial make sense?
If everything we've done in this tutorial makes sense then you are in good shape to tell your friends and family that you are close to mastering React! If there are areas that you find confusing, I suggest you go back and re-read the article(s) that address your confusion. If you still are stuck, feel free to leave a comment below and I'll be happy to personally help you out.
Next tutorial:Working with External Data
Got a question or just want to chat? Comment below or drop by our forums (they are actually the same thing!) where a bunch of the friendliest people you'll ever run into will be happy to help you out!
Simple Todo List Javascript
When Kirupa isn’t busy writing about himself in 3rd person, he is practicing social distancing…even on his Twitter, Facebook, and LinkedIn profiles.
Capto 1 2 download free. Hit Subscribe to get cool tips, tricks, selfies, and more personally hand-delivered to your inbox.