Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Filter tasks doesn't work - Todo list with MVC pattern using es6 classes

I'm trying to write a todo list project with pure JS using MVC pattern. It's my first project with MVC and I have a real problem that I can't solve. I have three buttons, each button gets a value as filter value (in Model class) for complete, active and all task. default value for filter is 0 that refers to all button. when complete button is active, new todo adds to page, but that page is only for complete todo and new todo must add to all pages while complete page is still active. I write some methods for handle it, but they doesn't work and I can't understand where is the problem. how can I solve it?

this is my code:

class Model {
    constructor() {
        this.todoS = [];
        this.filter = 0;
    }

    bindTodoListChanged(callback) {
        this.onTodoListChanged = callback;
    }

    _commit(todoS) {
        this.onTodoListChanged(todoS);
    }

    addTodo(todoText) {
        var todo = {
            id:
                this.todoS.length > 0
                    ? this.todoS[this.todoS.length - 1].id + 1
                    : 0,
            text: todoText,
            complete: false
        };

        this.todoS.push(todo);

        this._commit(this.todoS);
    }

    toggleTodo(id) {
        this.todoS = this.todoS.map(todo =>
            todo.id === id
                ? {
                      id: todo.id,
                      text: todo.text,
                      complete: !todo.complete
                  }
                : todo
        );

        this._commit(this.todoS);
    }

    filterTodo(filter) {
        this.todoS.filter(todo => {
            if (filter === 0) return true;
            return filter === 1 ? !todo.complete : todo.complete;
        });
    }
}

class View {
    constructor() {
        this.form = document.querySelector("#taskForm");
        this.input = document.getElementById("taskInput");
        this.list = document.querySelector("#taskList");
        this.filterBtnS = document.getElementById("filterButtons");
        this.allBtn = document.querySelector(".all");
        this.activeBtn = document.querySelector(".active");
        this.completeBtn = document.querySelector(".complete");
    }

    createElement(tag, className) {
        var element = document.createElement(tag);
        if (className) element.classList.add(className);

        return element;
    }

    getElement(selector) {
        var element = document.querySelector(selector);

        return element;
    }

    get _todoText() {
        return this.input.value;
    }

    _resetInput() {
        this.input.value = "";
    }

    displayTodoS(todoS) {
        // Faster way for clear tasks
        while (this.list.firstChild) {
            this.list.removeChild(this.list.firstChild);
        }

        if (todoS.length !== 0) {
            todoS.forEach(todo => {
                var li = this.createElement("li", "task"),
                    span = this.createElement("span");

                li.id = todo.id;            

                var checkbox = this.createElement("input");
                checkbox.type = "checkbox";
                checkbox.checked = todo.complete;

                if (todo.complete) {
                    var strike = this.createElement("s");
                    strike.textContent = todo.text;
                    span.innerHTML = "";    
                    span.append(strike);
                } else {
                    span.textContent = todo.text;
                }

                li.append(checkbox, span);

                this.list.append(li);
            });
        }
    }

    bindAddTodo(handler) {
        this.form.addEventListener("submit", e => {
            e.preventDefault();

            if (this._todoText) {
                handler(this._todoText);
                this._resetInput();
            } 
        });
    }
    
    bindToggleTodo(handler) {
        this.list.addEventListener("change", event => {
            if (event.target.type === "checkbox") {
                var id = +event.target.parentElement.id;

                handler(id);
            }
        });
    }

    bindFilterTodo(handler) {
        this.filterBtnS.addEventListener("click", e => {
            var filter = +e.target.getAttribute("value");

            handler(filter);
        });
    }

}

class Controller {
    constructor(model, view) {
        this.model = model;
        this.view = view;

        this.model.bindTodoListChanged(this.onTodoListChanged);
        this.view.bindAddTodo(this.handleAddTodo);
        this.view.bindToggleTodo(this.handleToggleTodo);
        this.view.bindFilterTodo(this.handleFilterTodo);

        this.onTodoListChanged(this.model.todoS);
    }

    onTodoListChanged = todoS => {
        this.view.displayTodoS(todoS);
    };

    handleAddTodo = todoText => {
        this.model.addTodo(todoText);
    };

    handleToggleTodo = id => {
        this.model.toggleTodo(id);
    };

    handleFilterTodo = filter => {
        this.model.filterTodo(filter);
    };
}

var app = new Controller(new Model(), new View());
<div id="main">
            <h2>Task List</h2>
            <form id="taskForm">
                <input
                    id="taskInput"
                    placeholder="New task..."
                    autocomplete="off"
                />
                <input class="submit" type="submit" value="Add Task" />
            </form>
            <div id="filterButtons" class="buttons">
                <div class="all" value="0">All</div>
                <div class="active" value="1">Active</div>
                <div class="complete" value="2">Completed</div>
            </div>
            <ul id="taskList"></ul>
        </div>
like image 563
mary Avatar asked Mar 10 '26 07:03

mary


1 Answers

The issue is that in filterTodo, you just filter the tasks but not _commit the change.

So

  1. Store the filter in the model
  2. Move the filter to _commit (so it will keep filter for any other actions such as add todo)

class Model {
    constructor() {
        this.todoS = [];
        this.filter = 0;
    }

    bindTodoListChanged(callback) {
        this.onTodoListChanged = callback;
    }

    _commit(todoS = this.todoS) {
        this.onTodoListChanged(todoS.filter(todo => {
            if (this.filter === 0) return true;
            return this.filter === 1 ? !todo.complete : todo.complete;
        }));
    }

    addTodo(todoText) {
        var todo = {
            id:
                this.todoS.length > 0
                    ? this.todoS[this.todoS.length - 1].id + 1
                    : 0,
            text: todoText,
            complete: false
        };

        this.todoS.push(todo);

        this._commit(this.todoS);
    }

    toggleTodo(id) {
        this.todoS = this.todoS.map(todo =>
            todo.id === id
                ? {
                      id: todo.id,
                      text: todo.text,
                      complete: !todo.complete
                  }
                : todo
        );

        this._commit(this.todoS);
    }

    filterTodo(filter) {
        this.filter = filter;
        this._commit();
    }
}

class View {
    constructor() {
        this.form = document.querySelector("#taskForm");
        this.input = document.getElementById("taskInput");
        this.list = document.querySelector("#taskList");
        this.filterBtnS = document.getElementById("filterButtons");
        this.allBtn = document.querySelector(".all");
        this.activeBtn = document.querySelector(".active");
        this.completeBtn = document.querySelector(".complete");
    }

    createElement(tag, className) {
        var element = document.createElement(tag);
        if (className) element.classList.add(className);

        return element;
    }

    getElement(selector) {
        var element = document.querySelector(selector);

        return element;
    }

    get _todoText() {
        return this.input.value;
    }

    _resetInput() {
        this.input.value = "";
    }

    displayTodoS(todoS) {
        // Faster way for clear tasks
        while (this.list.firstChild) {
            this.list.removeChild(this.list.firstChild);
        }

        if (todoS.length !== 0) {
            todoS.forEach(todo => {
                var li = this.createElement("li", "task"),
                    span = this.createElement("span");

                li.id = todo.id;            

                var checkbox = this.createElement("input");
                checkbox.type = "checkbox";
                checkbox.checked = todo.complete;

                if (todo.complete) {
                    var strike = this.createElement("s");
                    strike.textContent = todo.text;
                    span.innerHTML = "";    
                    span.append(strike);
                } else {
                    span.textContent = todo.text;
                }

                li.append(checkbox, span);

                this.list.append(li);
            });
        }
    }

    bindAddTodo(handler) {
        this.form.addEventListener("submit", e => {
            e.preventDefault();

            if (this._todoText) {
                handler(this._todoText);
                this._resetInput();
            } 
        });
    }
    
    bindToggleTodo(handler) {
        this.list.addEventListener("change", event => {
            if (event.target.type === "checkbox") {
                var id = +event.target.parentElement.id;

                handler(id);
            }
        });
    }

    bindFilterTodo(handler) {
        this.filterBtnS.addEventListener("click", e => {
            var filter = +e.target.getAttribute("value");

            handler(filter);
        });
    }

}

class Controller {
    constructor(model, view) {
        this.model = model;
        this.view = view;

        this.model.bindTodoListChanged(this.onTodoListChanged);
        this.view.bindAddTodo(this.handleAddTodo);
        this.view.bindToggleTodo(this.handleToggleTodo);
        this.view.bindFilterTodo(this.handleFilterTodo);

        this.onTodoListChanged(this.model.todoS);
    }

    onTodoListChanged = todoS => {
        console.log(todoS);
        this.view.displayTodoS(todoS);
    };

    handleAddTodo = todoText => {
        this.model.addTodo(todoText);
    };

    handleToggleTodo = id => {
        this.model.toggleTodo(id);
    };

    handleFilterTodo = filter => {
        this.model.filterTodo(filter);
    };
}

var app = new Controller(new Model(), new View());
<div id="main">
  <h2>Task List</h2>
  <form id="taskForm">
    <input id="taskInput" placeholder="New task..." autocomplete="off" />
    <input class="submit" type="submit" value="Add Task" />
  </form>
  <div id="filterButtons" class="buttons">
    <div class="all" value="0">All</div>
    <div class="active" value="1">Active</div>
    <div class="complete" value="2">Completed</div>
  </div>
  <ul id="taskList"></ul>
</div>

https://stackblitz.com/edit/js-1u6dxi

like image 157
Mosh Feu Avatar answered Mar 11 '26 21:03

Mosh Feu



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!