Fake chat - garevna/js-course GitHub Wiki
Внесем некоторые изменения в базу данных db.json
Добавим запись lastUpdate с двумя полями:
"lastUpdate": {
"data": "26.10.2018",
"time": "12:38:01"
}Мы будем использовать эти данные для обновления чата
после того, как данные в db.json были обновлены
( операции POST | PUT | DELETE )
В каждую запись базы данных postsдобавим свойстваdateиtime`
"posts": [
{
"id": 0,
"date": "05.08.2018",
"time": "10:30:15",
"userId": 2,
"title": "My first post here",
"body": "It's really wonder!"
},
...
}
Запускаем json-server
json-server --watch db.jsonПолучаем endpoints:
Resources
http://localhost:3000/lastUpdate
http://localhost:3000/users
http://localhost:3000/posts
http://localhost:3000/comments
Home
http://localhost:3000- Открываем в браузере страницу http://localhost:3000
- заходим в Chrome DevTools
- Создаем snippet
| vars | functions |
|---|---|
lastUpdate |
getData |
chat |
appelem |
posts |
buildChat |
users |
initChat |
currentUser |
updateChat |
chatInput |
Запуск |
Объявляем переменную lastUpdate,в которой будем хранить дату и время модификации загруженных данных
let lastUpdateОбъявляем переменную getData,
в которой будет ссылка на функцию, загружающую данные с сервера
по имени ресурса ( lastUpdate, users, posts, comments )
let getData = function ( ref ) {
return fetch ( 'http://localhost:3000/' + ref )
.then ( response => response.json () )
}Объявляем переменную appElem
В этой переменной будет ссылка на анонимную стрелочную функцию
Функция получает в первом аргументе имя тега tagName,
и создает элемент DOM
Второй ( опциональный ) аргумент container может содержать ссылку
на элемент-контейнер, куда нужно вставить созданный элемент
Если такой аргумент отсутствует, то функция вставляет элемент
в document.body
let appElem = ( tagName, container ) =>
( container ? container : document.body )
.appendChild (
document.createElement ( tagName )
)ссылка на элемент DOM, который будет контейнером для сообщений чата
В переменные posts и users будем получать данные из базы данных на сервере
объект активного пользователя чата ( от лица которого мы будем писать в чат )
let chat
let posts
let users
let currentUserСоздаем элемент input ( поле для ввода текста сообщения )
и стилизуем элемент:
let chatInput = appElem ( 'input' )
chatInput.style = `
position: fixed;
left: 20px;
width: 80%;
bottom: 10px;
border: inset 1px;
background-color: #af9;
overflow: auto;
`Ссылка на функцию, создающую элемент section ( это будет чат )
let buildChat = function () {
chat = appElem ( 'section' )
chat.style = `
position: fixed;
top: 30px;
left: 20px;
right: 20px;
bottom: 70px;
border: inset 1px;
overflow: auto;
padding: 10px;
`
}Ранее мы уже объявили переменную chat
После вызова функции buildChat
в этой переменной ( chat) будет ссылка на элементsection,
который будет контейнером для сообщений в чате
Асинхронная функция initChatбудет итерировать массивpost
с помощью метода forEach
заполнять контейнер chatданными из массиваpost
( данные к моменту вызова функции должны быть уже получены от сервера )
В первую очередь контейнер chat освобождается:
chat.innerHTML = ""На каждой итерации по значению post.userId
находится соответствующий элемент массива users
( с помощью метода filter )
На каждой итерации создается элемент div,
который будет контейнером для очередной записи
из массива post
Для создания новых элементов DOM и вставки их на страницу
используем асинхронную функцию appElem
let initChat = async function () {
chat.innerHTML = ""
posts.forEach ( post => {
let user = users.filter (
x => x.id === post.userId
)[0]
chat.appendChild (
( function () {
let cont = appElem ( 'div' )
let ava = appElem ( 'img', cont )
ava.src = user.photoURL
ava.width = "40"
ava.title = ` ${user.name} ${user.lastName}`
appElem ( 'span', cont ).innerHTML =
` <small> ${post.date} ${post.time}</small>`
appElem ( 'p', cont ).innerText = post.body
return cont
}) ( user )
)
})
}Объявляем переменную updateChat,
в которую помещаем ссылку на асинхронную анонимную функцию updateChat
✋ Используем функцию getData для получения даты и времени
последнего обновления базы данных
в переменную updated,
используя ключевое слово await,
чтобы дождаться результата асинхронной операции
✋ Сравниваем дату и время обновления загруженных данных
с датой и временем последнего обновления базы данных
Если загруженные данные актуальны
( после их загрузки обновлений базы данных не было ),
то завершаем выполнение функции updateChat(return )
✋ В противном случае формируем массив промисов:
[
getData ( "users" ).then ( x => users = x ) ,
getData ( "posts" ).then ( x => posts = x )
]и передаем его методу Promise.all,
который также вызываем с ключевым словом await
✋ Когда Promise.all будет разрешен, проверяем, есть ли значение
у объявленной ранее переменной currentUser
( пользователь, от лица которого будут добавляться сообщения в чат )
Если активный пользователь еще не определен,
выбираем его случайным образом из числа всех зарегистрированных пользователей ( users )
✋ Вызываем функцию initChat ()
let updateChat = async function () {
let updated = await getData ( "lastUpdate" )
if ( lastUpdate && updated.data === lastUpdate.data &&
updated.time === lastUpdate.time ) return
let scrollValue = chat.scrollTop
await Promise.all ( [
getData ( "users" ).then ( x => users = x ) ,
getData ( "posts" ).then ( x => posts = x )
] )
if ( !currentUser ) {
currentUser = users [
Math.floor ( Math.random () * users.length )
]
currentUserId = currentUser.id
}
initChat ()
chat.scrollTop = scrollValue
}Свойство scrollTop можно использовать для управления прокруткой чата
Если установить
chat.scrollTop = chat.offsetTopто элемент chat будет прокручен до конца
( мы будем видеть последние сообщения в чате )
- вызваем buildChat (), чтобы создать контейнер для чата
- вызваем updateChat (), чтобы заполнить контейнер данными
- устанавливаем интервал обновления чата ( setInterval )
через заданные интервалы времени
мы будем вызывать updateChat (),
чтобы проверить, было ли за это время
обновление базы данных на серевере,
и если да - обновлять
содержимое чата на клиенте
- вешаем обработчика события change элемента chatInput
если клиент введет сообщение, это сообщение нужно
отправить на сервер для добавления в базу данных
Итак:
buildChat ()
updateChat ()
setTimeout ( function () {
chat.scrollTop = chat.scrollHeight
}, 100 )
let interval = setInterval ( function () {
updateChat ()
}, 1000 )
chatInput.onchange = function ( event ) {
let postTime = new Date().toLocaleString ().split ( ', ' )
fetch ( 'http://localhost:3000/posts', {
method: 'POST',
body: JSON.stringify ({
date: postTime [0],
time: postTime [1],
userId: currentUserId,
body: event.target.value
}),
headers: {
"Content-type": "application/json"
}
})
}| 📃 Полный код сниппета |
|---|