How to Build a to-do list with typescript & local-storage 😊
Tags: programming, melbite, typescript, javascript
Published: 2022-06-04 13:06:23

Hello my felow software developers, I Love Typescript that's why I created this project for you.

Why I love typescript? [Read this article here](https://melbite.com/melbite/ta09TVNJKWCJbdfXrYOa)

### Enough talk 😊 , lets do this...

Install typescript if not installed. 
```javascript 
sudo npm i -g typescript
```
In this tutorial we are going to make use of snowpack bundler template to set up our environment. You can make use of webpack or any other bundler.

#### 1. How to set-up/install snowpack

``` javascript
npx create-snowpack-app to-do-app --template @snowpack/app-template-blank-typescript

```
#### 2. To start the server run 
```javascript
npm start
```
#### 3. Navigate to `public` folder as set up your `index.html` file like this,
``` html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <script src="/dist/index.js" type="module" defer></script>
    <title>ToDo List - Typescript</title>
    <style>
      #list{
        list-style: none;
        padding: 0;
      }
    </style>
  </head>
  <body>
    <li id="list"></li>
    <form id="new-task-form">
      <input type="text" id="new-task-title">
      <button type="submit">Add</button>
    </form>
  </body>
</html>
```
In your html file Import your script file in your html file like
```javascript
<script src="/dist/index.js" defer></script>
```
#### 4. Lets Do the magic with Typescript 😊
Navigate to your `index.tsx` file in your `src` folder.

Query the selectors in the `index.ts` file
``` javascript
const list = document.querySelector("#list")
const form = document.getElementById("new-task-form")
const input = document.querySelector("#new-task-input")
```
While using typescript you have to specify the type like this
``` javascript 
const list = document.querySelector<HTMLUListElement>("#list")
const form = document.getElementById("new-task-form") as HTMLFormElement | null
const input = document.querySelector<HTMLInputElement>("#new-task-input")

```
Let's query and accept form input and prevent default form behaviors like submission
```javascript
form?.addEventListener("submit", e => {
  e.preventDefault()

  // validate the input and if empty return null
  if(input?.value == "" || input?.value == null) return
  // create a new task with the following properties
  const newTask: Task ={
    id: uuidV4(),
    title: input.value,
    completed: false,
    createdAt: new Date()
  }
  // call the function that add an item to the list
 //Note: this function is not yet created, we shall create it bellow
  addListItem(newTask)
  // clear input field after submit
  input.value = ""
})
```
In order to assign every todo task to a unique id as used in the above code, we have to install the uuid package like this

```javascript
npm install uuid
```
Note: not every library build has all the packages needed, Whenever we run this app, typescript will throw an error stating
``` text
Could not find a declaration file for module 'uuid'. '/home/evans/JAVASCRIPT/ToDo-ts/node_modules/uuid/dist/index.js' implicitly has an 'any' type.
  Try `npm i --save-dev @types/uuid` if it exists or add a new declaration (.d.ts) file containing `declare module 'uuid';`ts(7016)
```
To fix this problem we have to istall the `types` package
``` javascript
npm i --save-dev @types/uuid
```
To use the `uuid` library import it like this,
```javascript
import {v4 as uuidV4} from 'uuid'
```
Now let's create a function that will add a new item to the list.
In order to pass the types and reduce file size for clean code we define types independently and pass them as parameters in a function.
This is how you define the types

```javascript 
type Task = {
  id: string
  title: string,
  completed: boolean,
  createdAt: Date
}
```
And then pass the type as parameter in the function below 
``` javascript
const addListItem = (task: Task) => {
  const item = document.createElement("li")
  const label = document.createElement('label')
  const checkbox = document.createElement('input')
  checkbox.type = "checkbox"
  checkbox.checked = task.completed

  // compine all the field/ elements into one using the array append method
  label.append(checkbox, task.title)
  item.append(label)
  list?.append(item)
}

```
### Storing the to-do's in the browser local storage
For future reference we have to store our list to the local storage. Whenever you refresh your browser the list still exist until you clear the storage.

```javascript
// create local storage
const tasks: Task[] = loadTasks()
//loop through the tasks in the list and append them in the array
tasks.forEach(addListItem)
```
Create a function to save the task to local storage.
``` javascript
const saveTasks = () => {
  localStorage.setItem("TASKS", JSON.stringify(tasks))
}
```
Create a function that retrieves all the data from the local storage and return to the front-end
``` javascript
function loadTasks():Task[] {
  const taskJson = localStorage.getItem('TASKS')
  if(taskJson == null) return []
  return JSON.parse(taskJson)
}
```
### 4. Update the local storage
Your final `index.ts` file should look like this.
``` javascript
import {v4 as uuidV4} from 'uuid'
console.log("Hello world")

type Task = {
  id: string
  title: string,
  completed: boolean,
  createdAt: Date
}
const list = document.querySelector<HTMLUListElement>("#list")
const form = document.getElementById("new-task-form") as HTMLFormElement | null
const input = document.querySelector<HTMLInputElement>("#new-task-title")

// create local storage
const tasks: Task[] = loadTasks()
tasks.forEach(addListItem)

form?.addEventListener("submit", e => {
  e.preventDefault()

  // validate the input and if empty return null
  if(input?.value == "" || input?.value == null) return

  // create a new task with the following properties
  const newTask: Task ={
    id: uuidV4(),
    title: input.value,
    completed: false,
    createdAt: new Date()
  }

  tasks.push(newTask)
  // call the function that add an item to the list
  addListItem(newTask)
  // clear input field after submit
  input.value = ""
})

// in order to pass the types and reduce file size for clean code we define types independendly and pass them as parameters in the function bellow

function addListItem  (task: Task){
  const item = document.createElement("li")
  const label = document.createElement('label')
  const checkbox = document.createElement('input')
  checkbox.addEventListener("change", () => {
    task.completed =checkbox.checked
    saveTasks()
  })
  checkbox.type = "checkbox"
  checkbox.checked = task.completed

  // compine all the field/ elements into one using the array append method
  label.append(checkbox, task.title)
  item.append(label)
  list?.append(item)

}

const saveTasks = () => {
  localStorage.setItem("TASKS", JSON.stringify(tasks))
}

function loadTasks():Task[] {
  const taskJson = localStorage.getItem('TASKS')
  if(taskJson == null) return []
  return JSON.parse(taskJson)
}

```
## Recap
Making use of Typescript enables one to catch bugs in development mode. This prevents shipping bugs to production.

Was this tutorial helpful? Leave a comment below.

Article originally published at [melbite.com/to-do-app-with-typescript](https://melbite.com/melbite/JqTOuoP1GofWNuT5Vfaa)

Get the source code [here](https://github.com/Evans-mutuku/To-Do-App-with-typescript.git)