-
Notifications
You must be signed in to change notification settings - Fork 0
/
app.js
230 lines (205 loc) · 9.55 KB
/
app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
(function() {
let todos = []; // хранилище списка всех задач
let users = []; // хранилище списка всех пользователей
const todoInput = document.getElementById('new-todo');
const userSelect = document.getElementById('user-todo');
const todoList = document.getElementById('todo-list');
/*
* Базовая логика приложения — создание списка всех задач и списка пользователей
* после загрузки страницы. При формировании списка задач назначаются обработчики
* клика по checkbox статуса задачи и клика по кнопке удаления задачи.
*/
document.addEventListener('DOMContentLoaded', initApp);
document.querySelector('form').addEventListener('submit', handleSubmit);
// вызывается, когда документ полностью загружен и DOM модель построена
function initApp() {
Promise.all([getAllTodos(), getAllUsers()]).then((values) => {
[todos, users] = values;
// формируем список всех задач и список всех пользователей
todos.forEach(todo => createTodoElement(todo));
users.forEach(user => createUserElement(user));
});
}
// добавляет в DOM документа один элемент <li> списка всех задач
function createTodoElement({ id, userId, title, completed }) {
const li = document.createElement('li');
li.className = 'todo-item';
li.dataset.id = id;
li.innerHTML = `<span>${title} <i>by</i> <b>${getUserName(userId)}</b></span>`;
const status = document.createElement('input');
status.type = 'checkbox';
status.checked = completed;
status.addEventListener('change', handleStatusClick); // обработчик клика по статусу задачи
const close = document.createElement('span');
close.innerHTML = '×';
close.className = 'close';
close.addEventListener('click', handleCloseClick); // обработчик клика по кнопке удаления
li.prepend(status);
li.append(close);
todoList.prepend(li);
}
// вспомогательная функция, получает имя пользователя по идентификатору
function getUserName(userId) {
const user = users.find(usr => usr.id === userId);
return user.name;
}
// добавляет в DOM один элемент <option> списка всех пользователей
function createUserElement(user) {
const option = document.createElement('option');
option.value = user.id;
option.text = user.name;
userSelect.append(option);
}
// обработчик отправки формы создания новой задачи, отправляет POST-запрос
// на сервер, добавляет в список всех задач новый элемент
function handleSubmit(event) {
event.preventDefault();
const title = todoInput.value.trim();
if (title.length === 0) { // нет текста задачи
todoInput.style.backgroundColor = '#fdd'; // подсветка красным
setTimeout(() => todoInput.style.backgroundColor = '', 1000);
return;
}
const userId = Number(userSelect.value);
if (userId === 0) { // не выбран пользователь
userSelect.style.backgroundColor = '#fdd'; // подсветка красным
setTimeout(() => userSelect.style.backgroundColor = '', 1000);
return;
}
const completed = false; // статус задачи
const data = {
userId,
title,
completed
};
// отправляем POST-запрос на сервер, добавляем новый DOM-элемент в список задач
createTodo(data).then(newTodo => {
if (newTodo) {
createTodoElement(newTodo);
todoInput.value = '';
userSelect.selectedIndex = 0;
}
});
}
/*
* Обработчики клика на checkbox изменения статуса задачи и на крестик удаления задачи
*/
// обработчик изменения checkbox статуса задачи, отправляет PATCH-запрос на сервер
function handleStatusClick() {
const todoId = Number(this.parentElement.dataset.id);
const completed = this.checked;
toggleStatus(todoId, completed).then(ok => {
// если запрос на сервер был неудачным, возвращаем старый статус задачи
if (!ok) this.checked = !this.checked;
});
}
// обработчик клика на кнопку удаления задачи, отправляет DELETE-запрос на сервер,
// при успешном удалении задачи на сервере — удаляет элемент из списка задач
function handleCloseClick() {
const todoId = Number(this.parentElement.dataset.id);
// удаляем задачу на сервере и удаляем элемент из списка
deleteTodo(todoId).then(ok => {
if (ok) eraseTodo(todoId);
});
}
/*
* После успешного удаления задачи на сервере нужно удалить из DOM-элемент списка
*/
// удаляет из DOM элемент списка всех задач, когда задача была удалена на сервере
function eraseTodo(todoId) {
// удалить задачу из хранилища всех задач
todos = todos.filter(todo => todo.id !== todoId);
// удалить элемент из списка всех задач
const todo = todoList.querySelector(`[data-id="${todoId}"]`);
todo.querySelector('input').removeEventListener('change', handleStatusClick);
todo.querySelector('.close').removeEventListener('click', handleCloseClick);
todo.remove();
}
/*
* Функции для отправки http-запросов на сервер: получение списка задач
* и пользователей, изменение статуса и удаление задачи
*/
// получение списка всех задач
async function getAllTodos() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos?_limit=15');
const data = await response.json();
return data;
} catch (error) {
alertError(error);
}
}
// получение всех пользователей
async function getAllUsers() {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/users?_limit=5');
const data = await response.json();
return data;
} catch (error) {
alertError(error);
}
}
// создание новой задачи
async function createTodo(todo) {
try {
const response = await fetch('https://jsonplaceholder.typicode.com/todos', {
method: 'POST',
body: JSON.stringify(todo),
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('Ошибка при создании задачи');
}
const newTodo = await response.json();
return newTodo;
} catch (error) {
alertError(error);
return false;
}
}
// изменить статус задачи
async function toggleStatus(todoId, completed) {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId}`, {
method: 'PATCH',
body: JSON.stringify({ completed }),
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('Ошибка при изменении статуса');
}
return true;
} catch (error) {
alertError(error);
return false;
}
}
// удалить задачу
async function deleteTodo(todoId) {
try {
const response = await fetch(`https://jsonplaceholder.typicode.com/todos/${todoId}`, {
method: 'DELETE',
headers: {
'Content-Type': 'application/json',
},
});
if (!response.ok) {
throw new Error('Ошибка при удалении задачи');
}
return true;
} catch (error) {
alertError(error);
return false;
}
}
/*
* Обработка всех ошибок, которые могут возникнуть
*/
function alertError(error) {
alert(error.message);
}
})();