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>
The issue is that in filterTodo, you just filter the tasks but not _commit the change.
So
_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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With