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
Enough talk π , lets do this...
Install typescript if not installed.
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
npx create-snowpack-app to-do-app --template @snowpack/app-template-blank-typescript
2. To start the server run
npm start
3. Navigate to public
folder as set up your index.html
file like this,
<!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
<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
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
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
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
npm install uuid
Note: not every library build has all the packages needed, Whenever we run this app, typescript will throw an error stating
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
npm i --save-dev @types/uuid
To use the uuid
library import it like this,
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
type Task = {
id: string
title: string,
completed: boolean,
createdAt: Date
}
And then pass the type as parameter in the function below
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.
// 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.
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
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.
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
Get the source code here
Top comments (0)