Lista To-Do z użyciem klas w vanilla JS

0

Na początku uprzedzam, że jestem w bardzo początkowej fazie nauki programowania. W ramach ćwiczeń klas w JavaScript chciałem zrobić sobie podstawową toDoList w JavaScript z użyciem klas. Wedle moich założeń ma się składać z 4 klas: Main.js - do koordynowania pozostałych klas i kolejno klasy pełniący funkcję zgodnie z ich nazewnictwem: AddTask.js, RemoveTask.js, SearchTask.js. Na razie pracuje nad klasami Main.js oraz AddTask.js i tu natknąłem się na problemy. Pytanie pod kodem.

Kod w html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<body>
  <h1>ToDoList</h1>
  <section>
    <input type="text">
    <button id="start">Add task</button>
  </section>
  <input type="text" class="search">
  <h2>Number of tasks: <span>0</span></h2>
  <ul></ul>
  <script src="AddTask.js"></script>
  <script src="RemoveTask.js"></script>
  <script src="SearchTask.js"></script>
  <script src="Main.js"></script>
</body>
</html>

Kod w Main.js

class Main {
  constructor(){  
    this.addTask = new AddTask()
    this.button=document.getElementById('start')
    this.button.addEventListener('click', this.addTask.startGame.bind(this))
    this.taskNumber = document.querySelector('h2 span')
    this.ul = document.querySelector('body ul')
  }         
}
const start = new Main()

Kod AddTask.js

class AddTask {
    
  constructor() {
    this.toDoList = []
  }
    
  startGame() {
    const input     = document.querySelector('section input')
    const titleTask = input.value;
    if (titleTask === "") return alert ('You have to write a task!');
    const task      = document.createElement('li');
    task.innerHTML  = titleTask + "<button>Delete</button>";
    this.toDoList.push(task)
    console.log(this.toDoList)
    input.value     = ""
    return toDoList;
  }
}

Nie wiem, na tą chwilę, jak zrobić aby:

  1. Alert 'You have to write a task!' pokazywał się dopiero po kliknięciu, a nie jak teraz po otwarciu html już się pojawia.
  2. W konsoli w ogóle nie pojawia mi się toDoList którą chciałbym wyświetlić za pomocą console.log.
  3. Zadania nie są dodawane do toDoList.
  4. Czy w ogóle w takiej formie ma prawo to działać?

Z góry dzięki za wszelkie wskazówki i odpowiedzi.

3

this.button=document.getElementById('start')

Poczytaj o czymś takim jak decoupling (taka zasada w OOP). Bo w ten sposób to łamiesz. Wyobraź sobie, że swoją klasę będziesz chciał użyć w kilku różnych miejscach na stronie. A kodem document.getElementById('start') sam się zaorałeś w tym momencie, bo klasa zakłada, że gdzieś w DOM jest obiekt o id start. A jak chcesz zrobić kilka takich widżetów z różnymi przyciskami?

Bardziej elegancko byłoby albo przekazywanie tego przycisku w konstruktorze:

constructor(button){  
   (...)
   this.button = button;
}

Chociaż nie wiem czy elegancko, bo trochę bez sensu w tym kontekście, żeby przekazywać sam przycisk. Ale patrząc nie na ten kawałek kodu, ale ogólnie na praktyki programowania, to przekazywanie argumentów w konstruktorze ma sens.

Albo niech klasa sama sobie tworzy przycisk za pomocą jakiegoś document.create

Tutaj znowu querySelector:

    this.taskNumber = document.querySelector('h2 span')
    this.ul = document.querySelector('body ul')

Tylko w takim razie po co ta klasa? Klasa powinna coś enkapsulować, a tutaj wszystko jest na wierzchu i klasa pobiera sobie na pałę elementy z DOM.

Wyobraź sobie, że nad tobą stoi klient, który co chwila zmienia zdanie (i np. zamiast widżetu w jednym miejscu na stronie, musisz zrobić tak, żeby wyświetlały się 3 listy todo naraz). I że pracujesz w kilka osób i co chwila ktoś zmienia coś w HTMLu. Więc jak zrobisz selektor body ul, to wystarczy, że ktoś wstawi tag <ul> do HTMLa i już się rozleci za chwilę.

Taka klasa nie wytrzyma próby czasu.

Jak chcesz mieć szablony w HTML, to już lepiej mieć jakieś wzorcowe ukryte elementy HTML i robić na nich el.cloneNode(true)
https://developer.mozilla.org/en-US/docs/Web/API/Node/cloneNode

albo można też użyć WebComponentów.

albo zewnętrznej biblioteki (jak React), ale rozumiem, że chcesz to zrobić bez biblioteki.

W konsoli w ogóle nie pojawia mi się toDoList którą chciałbym wyświetlić za pomocą console.log.

Przypuszczam, że dlatego, że podmieniasz kontekst this:

 this.button.addEventListener('click', this.addTask.startGame.bind(this))

czyli przypisujesz this do instancji klasy Main.
Natomiast toDoList tworzysz w instancji klasy AddTask.

Jeśli chcesz, żeby startGame zachowało this klasy AddTask, to możesz zrobić tak:

 this.button.addEventListener('click', () => this.addTask.startGame() )

wtedy podajesz anonimową funkcję strzałkową, która po prostu wywoła metodę startGame na obiekcie this.addTask, więc metoda startGame zachowa swój this.

ew. tak (co pewnie chciałeś zrobić):

this.button.addEventListener('click', this.addTask.startGame.bind(addTask))

ale wg mnie mniej czytelne.

BTW w jakim celu funkcja startGame zwraca toDoList? To wygląda jak pomyłka.

1 użytkowników online, w tym zalogowanych: 0, gości: 1