Front-end song – Lesson 9 – About the original typescript implementation of todolist

Foreword

I am a ballad. The best time to plant trees was ten years ago, followed by now. Today, I will continue to bring you an explanation of original typescript.

Environment configuration

npm init -y
yarn add vite -D

Modify page.json configuration port

{
  "name": "demo1",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "dev": "vite --port 3002"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "vite": "^4.4.9"
  }
}

Directory structure

index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Ts</title>
</head>
<body>
    <div class="app">
        <div class="todo-input">
            <input type="text" placeholder="Please enter to-do items">
            <button>Add</button>
        </div>
        <div class="todo-list"></div>
    </div>
     <script type="module" src="./src/app.ts"></script>
</body>
</html>

app.ts

import { ITodoData } from "./typings";
import TodoEvent from "./TodoEvent";
; ((doc) => {
    const oInput: HTMLInputElement = document.querySelector('input') as HTMLInputElement
    const oAddBtn: HTMLElement = document.querySelector('button') as HTMLElement
    const oTodoList: HTMLElement = document.querySelector('.todo-list') as HTMLElement

    const todoData: ITodoData[] = [
        {
            id: 1,
            content: 'geyao',
            completed: false
        },
        {
            id: 2,
            content: 'fangfang',
            completed: false
        },
        {
            id: 3,
            content: 'kang',
            completed: false
        }
    ]

    const init = (): void => {
        bindEvent()
    }

    function bindEvent(): void {
        oAddBtn.addEventListener("click", handleAddBtnClick, false)
        oTodoList.addEventListener("click", handleListClick, false)
    }
    function handleAddBtnClick(): void {
        const val:string=oInput.value.trim()
        console.log(val,"val is")
        if(val.length){
           const ret= todoEvent.addTodo(<ITodoData>{
                id: 4,
                content: val,
                completed: false
            })
            if(ret & amp; & amp;ret===1001){
                alert("The list already exists")
                return
            }
            oInput.value=''
        }
       
   
    }
    function handleListClick(e: MouseEvent): void {
        const tar = e.target as HTMLElement
        const tagName = tar.tagName.toLowerCase()
        if (tagName === 'input' || tagName === 'button') {
            const id=parseInt(tar.dataset.id as string)
            switch (tagName) {
                case 'input':
                    todoEvent.toggleComplete(tar,id)
                    break;
                case 'button':
                    todoEvent.removeTodo(tar,id)
                    break;
                default:
                    break;
            }
        }
    }
    const todoEvent: TodoEvent = new TodoEvent(todoData,oTodoList)
    init()

})(document)

TodoDom.ts

import TodoTemplate from "./TodoTemplate";
import { ITodoData } from "./typings";
import { createItem, findParentNode } from "./utils";

class TodoDom extends TodoTemplate{
    private todoWarpper:HTMLElement;
    constructor(todoWarpper:HTMLElement){
        super()
        this.todoWarpper=todoWarpper
    }
    protected initList(todoData:ITodoData[]){
        if(todoData.length){
            const oFrag:DocumentFragment=document.createDocumentFragment()
            todoData.map((todo:ITodoData)=>{
               const oItem:HTMLElement= createItem("div","todo-item",this.todoView(todo))
                // const oItem:HTMLElement=document.createElement("div")
                // oItem.className='todo-item'
                // oItem.innerHTML=this.todoView(todo)
                 oFrag.appendChild(oItem)
            })
            this.todoWarpper.appendChild(oFrag)
        }
    }
    protected addItem(todo:ITodoData){
        const oItem:HTMLElement= createItem("div","todo-item",this.todoView(todo))
    // const oItem:HTMLElement=document.createElement('div');
    // oItem.className='todo-item'
    // oItem.innerHTML=this.todoView(todo)
       this.todoWarpper.appendChild(oItem)
    }
    protected removeItem(target:HTMLElement){
        const oParentNode:HTMLElement=findParentNode(target,"todo-item");
        oParentNode.remove()
    }
    protected changeCompleted(target:HTMLElement,completed:boolean){
        const oParentNode:HTMLElement=findParentNode(target,"todo-item");
        const oContent:HTMLElement=oParentNode.getElementsByTagName("span")[0]
        oContent.style.textDecoration=completed?'line-through':"none"
    }
}


export default TodoDom

todoEvent.ts

import TodoDom from "./TodoDom";
import { ITodoData } from "./typings";

 class TodoEvent extends TodoDom{
    private todoData: ITodoData[]

    constructor(todoData: ITodoData[],todoWarpper:HTMLElement) {
        super(todoWarpper)
        this.todoData = todoData
        this.init()
    }
    public addTodo(todo: ITodoData): undefined | number {
        const _todo: null | ITodoData | undefined = this.todoData.find((item: ITodoData) => item.content===todo.content)
        console.log(_todo,"_todo is")
        if (!_todo) {
            this.todoData.push(todo)
            this.addItem(todo)
            return
        }
        return 1001
    }
    protected init(){
        this.initList(this.todoData)
    }
    public removeTodo(target:HTMLElement,id:number):void {
        this.todoData=this.todoData.filter((todo:ITodoData)=>todo.id!==id)
        this.removeItem(target)
    }

    public toggleComplete(target:HTMLElement,id:number):void {
        this.todoData=this.todoData.map((todo:ITodoData)=>{
            if(todo.id===id){
                todo.completed=!todo.completed
                this.changeCompleted(target,todo.completed)
            }
            return todo;
        })
    }
}

export default TodoEvent

todoTemplate.ts

import { ITodoData } from "./typings";

class TodoTemplate{
   protected todoView({id,content,completed}:ITodoData):string{
      return `
      <input type="checkbox" ${completed}?'checked':'' data-id="${id}"/>
      <span style="text-decoration":${completed}?'line-through':'none'>${content}</span>
      <button data-id='${id}'>Delete</button>
      `
   }
}

export default TodoTemplate

typing.ts

export interface ITodoData{
    id: number,
    content: string,
    completed:boolean
}

utils.ts

import { callbackify } from "util"

export function findParentNode(target:HTMLElement,className:string):any{
    while(target=target.parentNode as HTMLElement){
        if(target.className===className){
            return target
        }
    }
}

export function createItem(tagName:string,className:string,todoItem:string):HTMLElement{
    const oItem:HTMLElement=document.createElement(tagName)
    oItem.className=className
    oItem.innerHTML=todoItem
    return oItem
}

Run results