Когда запрос не удался, нам не надо, чтобы страница просто сидела и ничего не делала. Мы определим простую функцию под названием reportError
, которая хотя бы покажет пользователю диалог, сообщающий об ошибке.
function reportError(error) {
if (error)
alert(error.toString());
}
Функция проверяет, есть ли ошибка, и выводит сообщение только при её наличии. Таким образом, мы можем напрямую передавать эту функцию в запрос для тех запросов, ответ на которые можно игнорировать. Тогда если запрос завершится с ошибкой, то об ошибке будет сообщено пользователю.
Показ тем
Чтобы иметь возможность обновлять список тем при поступлении изменений, клиент должен отслеживать темы, которые он показывает сейчас. Тогда, если поступает новая версия темы, которая уже есть на экране, её можно заменить прямо на месте обновлённой версией. Сходным образом, когда поступает информация об удалении темы, нужный элемент DOM можно удалить из документа.
Функция displayTalks
используется как для построения начального экрана, так и для его обновления при изменениях. Она будет использовать объект shownTalks
, связывающий заголовки тем с узлами DOM, чтобы запомнить темы, которые уже есть на экране.
var talkDiv = document.querySelector("#talks");
var shownTalks = Object.create(null);
function displayTalks(talks) {
talks.forEach(function(talk) {
var shown = shownTalks[talk.title];
if (talk.deleted) {
if (shown) {
talkDiv.removeChild(shown);
delete shownTalks[talk.title];
}
} else {
var node = drawTalk(talk);
if (shown)
talkDiv.replaceChild(node, shown);
else
talkDiv.appendChild(node);
shownTalks[talk.title] = node;
}
});
}
Структура DOM для тем строится по шаблону, включённому в HTML документ. Сначала нужно определить instantiateTemplate
, который находит и заполняет шаблон.
Параметр name
– имя шаблона. Чтобы найти элемент шаблона, мы ищем элементы, у которых имя класса совпадает с именем шаблона, который является дочерним у элемента с ID “template”
. Метод querySelector
облегчает этот процесс. На странице есть шаблоны “talk”
и “comment”
.
function instantiateTemplate(name, values) {
function instantiateText(text) {
return text.replace(/\{\{(\w+)\}\}/g, function(_, name) {
return values[name];
});
}
function instantiate(node) {
if (node.nodeType == document.ELEMENT_NODE) {
var copy = node.cloneNode();
for (var i = 0; i < node.childNodes.length; i++)
copy.appendChild(instantiate(node.childNodes[i]));
return copy;
} else if (node.nodeType == document.TEXT_NODE) {
return document.createTextNode(
instantiateText(node.nodeValue));
}
}
var template = document.querySelector("#template ." + name);
return instantiate(template);
}
Метод cloneNode
, который есть у всех узлов DOM, создаёт копию узла. Он не скопирует дочерние узлы, если не передать ему первым аргументом true
. Функция instantiate
рекурсивно создаёт копию шаблона, заполняя его по ходу дела.
Второй аргумент instantiateTemplate
должен быть объектом, чьи свойства содержат строки, которые надо ввести в шаблон. Метка вроде будет заменена значением свойства “title”
.
Этот подход к шаблонам довольно груб, но для создания drawTalk
его будет достаточно.
function drawTalk(talk) {
var node = instantiateTemplate("talk", talk);
var comments = node.querySelector(".comments");
talk.comments.forEach(function(comment) {
comments.appendChild(
instantiateTemplate("comment", comment));
});
node.querySelector("button.del").addEventListener(
"click", deleteTalk.bind(null, talk.title));
var form = node.querySelector("form");
form.addEventListener("submit", function(event) {
event.preventDefault();
addComment(talk.title, form.elements.comment.value);
form.reset();
});
return node;
}
После завершения обработки шаблона “talk”
нужно много чего подлатать. Во-первых, нужно вывести комментарии, путём многократного добавления шаблона "comment"
и добавления результатов к узлу класса "comments"
. Затем, обработчики событий нужно присоединить к кнопке, которая удаляет задачу и к форме, добавляющей комментарий.