过滤任务不起作用 - 使用 es6 的 MVC 模式待办事项列表 类

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

我正在尝试使用 MVC 模式用纯 JS 编写待办事项列表项目。这是我使用 MVC 的第一个项目,我遇到了一个无法解决的实际问题。 我有三个按钮,每个按钮都有一个值作为过滤器值(在模型 class 中),用于 completeactive全部个任务。过滤器的默认值是 0 指的是 all 按钮。 当完成按钮处于活动状态时,新的待办事项将添加到页面,但该页面仅用于完成待办事项,新待办事项必须在完成页面仍处于活动状态时添加到所有页面。 我写了一些方法来处理它,但它们不起作用,我不明白问题出在哪里。 我该如何解决?

这是我的代码:

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>

我怀疑 return 价值观的最大问题。例如:

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

此代码不 return 任何东西,它只调用 this.model.filterTodo。在相应的方法 filterTodo 中,您正在使用 this.todoS.filter 创建一个新数组,但您没有在任何地方 return 它:

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

您可以在此处执行与 toggleTodomap 函数类似的操作:

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

...但这只会起作用一次,因为设置过滤器会从您的数据库中删除其他待办事项。

根据我对代码的理解(没有尝试过),我可能只设置过滤器,每当 _commit 被调用时,根据所选过滤器传递待办事项的过滤版本

constructor() {
    ...
    this.possibleFilters = {
        0: () => true,
        1: todo => !todo.completed,
        2: todo => todo.completed
    };
}

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

_commit(todoS) {
    const selectedFilter = this.possibleFilters[this.filter];
    this.onTodoListChanged(todoS.filter(selectedFilter));
}

问题是在 filterTodo 中,您只是过滤任务而不是 _commit 更改。

所以

  1. 将过滤器存储在模型中
  2. 将筛选器移至 _commit(因此它会保留任何其他操作的筛选器,例如添加待办事项)

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