Building the TodoList Class
Defining the TodoList Class
Now that we have defined the Todo class, let’s create a TodoList class that represents a collection of todo items. Create a new file named todo-list.js in the src directory and define the TodoList class as follows:
import Todo from "./todo.js";
export const FILTERS = {
ALL: "all",
ACTIVE: "active",
COMPLETED: "completed",
};
class TodoList {
#todos = [];
addTodo(text) {
this.#todos.push(new Todo(text));
}
toggleTodo(id) {
const todo = this.#todos.find((todo) => todo.id === id);
if (todo) {
todo.toggle();
}
}
getTodos(filter) {
switch (filter) {
case FILTERS.ALL:
return [...this.#todos];
case FILTERS.COMPLETED:
return this.#todos.filter((todo) => todo.completed);
case FILTERS.ACTIVE:
return this.#todos.filter((todo) => !todo.completed);
default:
return [...this.#todos];
}
}
getNumberOfActiveTodos() {
return this.#todos.reduce((acc, todo) => acc + !todo.completed, 0);
}
clearCompleted() {
this.#todos = this.#todos.filter((todo) => !todo.completed);
}
markAllCompleted() {
this.#todos.forEach((todo) => {
todo.completed = true;
});
}
}
export default TodoList;
Let’s break down the code step by step:
-
We import the
Todoclass from thetodo.jsfile. -
We define a constant
FILTERSobject that contains three properties:ALL,ACTIVE, andCOMPLETED. These properties represent the different filters we can apply to the todo list. -
We define the
TodoListclass with a private field#todosthat stores the list of todo items. The#todosfield is initialized as an empty array. -
We do not define a constructor method for the
TodoListclass. If a class does not define a constructor, JavaScript provides a default one. The#todosfield is already initialized as an empty array, so there is nothing else to set up. -
We define several methods in the
TodoListclass:-
addTodo: This method adds a new todo item to the list. It creates a new instance of theTodoclass with the provided text and pushes it to the#todosarray. -
toggleTodo: This method toggles the completion status of a todo item with the specified ID. It finds the todo item in the list based on the ID and calls thetogglemethod on the todo item. -
getTodos: This method returns a filtered list of todo items based on the specified filter. It uses theFILTERSobject to determine the filter criteria and returns the filtered list of todo items. -
getNumberOfActiveTodos: This method calculates the number of active (incomplete) todo items in the list. It uses thereducemethod to count the number of todo items with a completion status offalse. -
clearCompleted: This method removes completed todo items from the list. It filters out todo items with a completion status oftrueand updates the#todosarray. -
markAllCompleted: This method marks all todo items as completed. It iterates over each todo item in the list and sets the completion status totrue.
-
-
We export the
TodoListclass as the default export from thetodo-list.jsfile.
Let’s make a few observations about the TodoList class:
-
Making the
#todosfield private encapsulates the internal state of theTodoListclass. Code outside the class cannot access the list of todo items directly. -
We mix the practices of functional programming, structured programming, and object-oriented programming in the
TodoListclass. The class uses methods likefilter,reduce, andforEachto work with the list of todo items. These methods are common in functional programming. We use them to transform and process data in a declarative way. On the other hand, we push directly to the#todosarray in theaddTodomethod, and we directly toggle the completion status of a todo item in thetoggleTodomethod. Those actions are closer to structured programming. And the class as a whole is object-oriented programming: we define a class with properties and methods that operate on those properties. -
We did not use the special getter method syntax with the
getTodosmethod. This is because the method takes a parameter (filter) and performs a transformation on the data. A getter method usually returns a property value without any additional processing, and a getter cannot take parameters. -
We used the
exportkeyword with the declaration of theFILTERSobject to make it available for import in other modules. This is an example of a named export, where we export a specific value from the module. If we were to import theFILTERSobject in another module, we would use the following syntax:import { FILTERS } from './todo-list.js';.
Checkpoint: Commit your progress.
git add .
git commit -m "todos-18: Create TodoList class with encapsulation"
git push