1
\$\begingroup\$

After getting the code review I refactored the code and the updated version is below. I tried implementing the suggestions but because I am still learning I might refactor again after learning how to implement classes.

previous post: Created a todo list in TS

index.ts

import { v4 as uuidv4 } from 'uuid';

const addTaskBtn = document.querySelector('#add-btn') as HTMLButtonElement
const input = document.querySelector("#input") as HTMLInputElement
const ul =  document.querySelector("#todoList") as  HTMLUListElement


// array which stores the object 
let todos:Array<Todo> = []
let updatingObject: Todo;

interface Todo {
    id: string;
    todo: string; 
    completed: boolean;
    createdDate: Date;
    updatedDate?: Date;
} 

// creating objects that are stored in the array
const storeObject = (todo: string)=>{
    todos.push({
        id: uuidv4(),
        todo: todo,
        completed: false ,
        createdDate: new Date(),
    })
}

const renderTodos = ()=> {
    // iterating over every object in array 
    todos.forEach((obj)=>{      
        const div = document.createElement("div");
        div.classList.add("item")
        
        const li = document.createElement("li");
        li.classList.add("todo")

        const checkbox = document.createElement("input")
        checkbox.type = "checkbox"
        if(obj.completed === true) checkbox.checked = true

        const label = document.createElement("label") 
        label.textContent = obj.todo
        label.htmlFor = `${obj.id}`

        checkbox.addEventListener("click",()=>{
            todos = todos.map(item=>{
                if(item.id == obj.id ){
                    return {...obj,completed:!obj.completed}
                }
                return item
            }) 
            ul.innerText =""
            renderTodos()
        })

        const btns = document.createElement("div");
        
        const editBtn = document.createElement("button") as HTMLButtonElement
        editBtn.textContent = "Edit "
        editBtn.classList.add("editBtn")

        editBtn.addEventListener("click",()=> updateTodo(obj))
        
        const deleteBtn = document.createElement("button") as HTMLButtonElement
        deleteBtn.textContent = "Delete"
        deleteBtn.classList.add("deleteBtn")
        
        deleteBtn.addEventListener("click",(e)=>{
            // removing todo form array 
            deleteTodo(obj)
        })

        btns.appendChild(editBtn)
        btns.appendChild(deleteBtn)
        
        li.appendChild(checkbox)
        li.appendChild(label)
        
        div.appendChild(li);
        div.appendChild(btns);
        
        ul.appendChild(div);
    })
    
}

const deleteTodo = (obj: Todo)=>{
    //  removed from array
    todos = todos.filter((item)=>{
        if(item.id !== obj.id){
            return item
        }
    })
    ul.innerText =""
    renderTodos()
}

const updateArray = (updatingObject:Todo)=>{
    let newTodo:string  = input.value
    todos = todos.map(item=>{
        if(item.id === updatingObject.id){
            return {...updatingObject,todo: newTodo}
        }
        return item
    })
}

const updateTodo = (obj:Todo)=>{
    input.value = obj.todo;
    addTaskBtn.textContent = "Update"
    updatingObject = obj
}

addTaskBtn.addEventListener("click",(e)=>{
    
    if(input.value === "" || !(e.target instanceof HTMLElement) ) return 

    if(((e.target)as HTMLButtonElement).innerText == "Add Task"){
        // push the todo in the array 
        storeObject(input.value)
        input.value = ""
        ul.innerText = ""
        renderTodos()
    }else{
        updateArray(updatingObject)
        ul.innerText = ""
        input.value = ""
        addTaskBtn.innerText = "Add Task"
        renderTodos()
    }
});

index.html

<main>
  <div class="top">
    <input type="text" name="todo-input" id="input" value="" placeholder="TODO" >
    <button id="add-btn" class="add-btn">Add Task</button>
  </div>
  <ul id="todoList">
  </ul>
</main>
  1. I want to know if I have made any improvements from the previous version
  2. How can I improve it?

Live version: https://codesandbox.io/s/recursing-black-wtbhob?file=/index.html

\$\endgroup\$

1 Answer 1

0
\$\begingroup\$

This implements the basics of a class to wrap the code, and shows how event listeners can be targetted at the class instance.

Because the class constructs the necessary HTML framework into a container element, it can keep internal references to those elements so its much easier to make changes and there is no need to deal with failed querySelector calls or type assertions for specific HTML element types.

My recommendations on the original post still stand, there is still much that could be done to better align the code with the DOM/CSS.

Edit zealous-feather-b3h6ub

index.ts

import "./styles.css";

import { v4 as uuidv4 } from "uuid";

interface Todo {
  id: string;
  todo: string;
  completed: boolean;
  createdDate: Date;
  updatedDate?: Date;
}

class TodoApp {
  private todos: Todo[] = [];
  private updatingTodo: Todo | undefined;
  private readonly main: HTMLElement;
  private readonly top: HTMLElement;
  private readonly input: HTMLInputElement;
  private readonly button: HTMLButtonElement;
  private readonly ul: HTMLUListElement;

  constructor(container: HTMLElement) {
    this.main = document.createElement("main");
    this.top = document.createElement("div");
    this.top.classList.add("top");
    this.input = document.createElement("input");
    this.input.name = "todo-input";
    this.input.id = "input";
    this.input.type = "text";
    this.input.value = "";
    this.input.placeholder = "TODO";
    this.button = document.createElement("button");
    this.button.id = "add-btn";
    this.button.classList.add("add-btn");
    this.button.innerText = "Add Task";
    this.button.addEventListener("click", this.onButtonClick.bind(this));
    this.top.appendChild(this.input);
    this.top.appendChild(this.button);

    this.ul = document.createElement("ul");
    this.ul.classList.add("todoList");

    this.main.appendChild(this.top);
    this.main.appendChild(this.ul);

    container.appendChild(this.main);

    this.render();
  }

  render() {
    while (this.ul.firstChild) {
      this.ul.removeChild(this.ul.firstChild);
    }
    this.todos.forEach((todo) => {
      const div = document.createElement("div");
      div.classList.add("item");
      const li = document.createElement("li");
      li.classList.add("todo");
      const label = document.createElement("label");
      label.textContent = todo.todo;
      label.htmlFor = todo.id;
      const checkbox = document.createElement("input");
      checkbox.type = "checkbox";
      checkbox.checked = todo.completed;
      checkbox.id = todo.id;
      checkbox.addEventListener("click", this.onTodoChecked.bind(this, todo));

      const btns = document.createElement("div");

      const editBtn = document.createElement("button");
      editBtn.textContent = "Edit";
      editBtn.classList.add("editBtn");
      editBtn.addEventListener("click", this.onEditButtonClick.bind(this, todo));
      const deleteBtn = document.createElement("button");
      deleteBtn.textContent = "Delete";
      deleteBtn.classList.add("deleteBtn");
      deleteBtn.addEventListener("click", this.onDeleteButtonClick.bind(this, todo));

      btns.appendChild(editBtn);
      btns.appendChild(deleteBtn);

      li.appendChild(checkbox);
      li.appendChild(label);

      div.appendChild(li);
      div.appendChild(btns);

      this.ul.appendChild(div);
    });

    if (this.updatingTodo === undefined) {
      this.button.textContent = "Add Task";
    } else {
      this.button.textContent = "Update Task";
    }
  }

  onButtonClick(ev: Event) {
    if (this.input.value === "") {
      return;
    }

    if (this.updatingTodo) {
      this.updatingTodo.todo = this.input.value;
      this.updatingTodo = undefined;
    } else {
      this.appendTodo(this.input.value);
    }
    this.input.value = "";
    this.render();
  }

  onEditButtonClick(todo: Todo) {
    this.input.value = todo.todo;
    this.updatingTodo = todo;
    this.render();
  }

  onDeleteButtonClick(todo: Todo) {
    this.todos = this.todos.filter((existing) => existing.id !== todo.id);
    this.render();
  }

  onTodoChecked(todo: Todo) {
    todo.completed = !todo.completed;
    this.render();
  }

  appendTodo(todo: string) {
    this.todos.push({
      id: uuidv4(),
      todo: todo,
      completed: false,
      createdDate: new Date()
    });
  }
}

const container = document.querySelector("#app");
if (container && container instanceof HTMLElement) {
  new TodoApp(container);
}

index.html

<html>
  <head>
    <title>Parcel Sandbox</title>
    <meta charset="UTF-8" />
  </head>

  <body>
    <div id="app" />
    <script src="src/index.ts"></script>
  </body>
</html>

style.css

Remains as original post.

\$\endgroup\$
1
  • \$\begingroup\$ perfect. I will take some time to understand this and code and thanks for the write-up \$\endgroup\$ Jun 30 at 16:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.