You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Web-Dev-For-Beginners/translations/uk/7-bank-project/3-data
leestott a2fda673a6
🌐 Update translations via Co-op Translator
3 weeks ago
..
README.md 🌐 Update translations via Co-op Translator 3 weeks ago
assignment.md 🌐 Update translations via Co-op Translator 3 weeks ago

README.md

Створення банківського додатку, частина 3: Методи отримання та використання даних

Тест перед лекцією

Тест перед лекцією

Вступ

У центрі кожного веб-додатку знаходяться дані. Дані можуть мати різні форми, але їх основна мета — показувати інформацію користувачеві. З розвитком веб-додатків, які стають все більш інтерактивними та складними, спосіб доступу користувача до інформації та взаємодії з нею стає ключовою частиною веб-розробки.

У цьому уроці ми розглянемо, як асинхронно отримувати дані з сервера та використовувати ці дані для відображення інформації на веб-сторінці без перезавантаження HTML.

Передумови

Для цього уроку вам потрібно створити Форму входу та реєстрації як частину веб-додатку. Також необхідно встановити Node.js і запустити сервер API локально, щоб отримати дані облікових записів.

Ви можете перевірити, чи сервер працює належним чином, виконавши цю команду в терміналі:

curl http://localhost:5000/api
# -> should return "Bank API v1.0.0" as a result

AJAX та отримання даних

Традиційні веб-сайти оновлюють вміст, що відображається, коли користувач вибирає посилання або надсилає дані через форму, шляхом перезавантаження повної HTML-сторінки. Кожного разу, коли потрібно завантажити нові дані, веб-сервер повертає абсолютно нову HTML-сторінку, яку потрібно обробити браузером, перериваючи поточну дію користувача та обмежуючи взаємодії під час перезавантаження. Цей робочий процес також називається багатосторінковим додатком або MPA.

Робочий процес оновлення в багатосторінковому додатку

Коли веб-додатки стали більш складними та інтерактивними, з'явилася нова техніка під назвою AJAX (Asynchronous JavaScript and XML). Ця техніка дозволяє веб-додаткам асинхронно надсилати та отримувати дані з сервера за допомогою JavaScript, без необхідності перезавантаження HTML-сторінки, що забезпечує швидше оновлення та плавнішу взаємодію з користувачем. Коли нові дані отримуються з сервера, поточна HTML-сторінка також може бути оновлена за допомогою API DOM. З часом цей підхід еволюціонував у те, що зараз називається односторінковим додатком або SPA.

Робочий процес оновлення в односторінковому додатку

Коли AJAX вперше був представлений, єдиним API для асинхронного отримання даних був XMLHttpRequest. Але сучасні браузери тепер також реалізують більш зручний і потужний Fetch API, який використовує проміси та краще підходить для роботи з даними у форматі JSON.

Хоча всі сучасні браузери підтримують Fetch API, якщо ви хочете, щоб ваш веб-додаток працював на застарілих або старих браузерах, завжди варто перевірити таблицю сумісності на caniuse.com.

Завдання

У попередньому уроці ми реалізували форму реєстрації для створення облікового запису. Тепер ми додамо код для входу за допомогою існуючого облікового запису та отримання його даних. Відкрийте файл app.js і додайте нову функцію login:

async function login() {
  const loginForm = document.getElementById('loginForm')
  const user = loginForm.user.value;
}

Спочатку ми отримуємо елемент форми за допомогою getElementById(), а потім отримуємо ім'я користувача з введення через loginForm.user.value. Кожен елемент форми можна отримати за його ім'ям (встановленим у HTML за допомогою атрибута name) як властивість форми.

Аналогічно до того, що ми зробили для реєстрації, ми створимо ще одну функцію для виконання запиту до сервера, але цього разу для отримання даних облікового запису:

async function getAccount(user) {
  try {
    const response = await fetch('//localhost:5000/api/accounts/' + encodeURIComponent(user));
    return await response.json();
  } catch (error) {
    return { error: error.message || 'Unknown error' };
  }
}

Ми використовуємо fetch API для асинхронного запиту даних із сервера, але цього разу нам не потрібні додаткові параметри, окрім URL, оскільки ми лише запитуємо дані. За замовчуванням fetch створює HTTP-запит типу GET, що саме нам і потрібно.

encodeURIComponent() — це функція, яка екранує спеціальні символи для URL. Які проблеми можуть виникнути, якщо ми не викличемо цю функцію і використаємо значення user безпосередньо в URL?

Тепер оновимо нашу функцію login, щоб використовувати getAccount:

async function login() {
  const loginForm = document.getElementById('loginForm')
  const user = loginForm.user.value;
  const data = await getAccount(user);

  if (data.error) {
    return console.log('loginError', data.error);
  }

  account = data;
  navigate('/dashboard');
}

Оскільки getAccount є асинхронною функцією, нам потрібно використовувати ключове слово await, щоб дочекатися результату сервера. Як і у випадку з будь-яким запитом до сервера, ми також повинні обробляти помилки. Поки що ми просто додамо повідомлення в лог для відображення помилки, а пізніше повернемося до цього.

Потім нам потрібно зберегти дані десь, щоб пізніше використовувати їх для відображення інформації на панелі управління. Оскільки змінна account ще не існує, ми створимо глобальну змінну для неї у верхній частині нашого файлу:

let account = null;

Після того, як дані користувача збережені у змінній, ми можемо перейти зі сторінки login на dashboard за допомогою функції navigate(), яку ми вже маємо.

Нарешті, нам потрібно викликати нашу функцію login, коли форма входу надсилається, модифікувавши HTML:

<form id="loginForm" action="javascript:login()">

Перевірте, чи все працює правильно, зареєструвавши новий обліковий запис і спробувавши увійти за допомогою цього ж облікового запису.

Перед тим як перейти до наступної частини, ми також можемо завершити функцію register, додавши це в кінці функції:

account = result;
navigate('/dashboard');

Чи знали ви, що за замовчуванням ви можете викликати серверні API лише з того ж домену та порту, що й веб-сторінка, яку ви переглядаєте? Це механізм безпеки, який реалізують браузери. Але зачекайте, наш веб-додаток працює на localhost:3000, тоді як сервер API працює на localhost:5000. Чому це працює? Використовуючи техніку під назвою Cross-Origin Resource Sharing (CORS), можна виконувати HTTP-запити між різними доменами, якщо сервер додає спеціальні заголовки до відповіді, дозволяючи виключення для конкретних доменів.

Дізнайтеся більше про API, пройшовши цей урок

Оновлення HTML для відображення даних

Тепер, коли ми маємо дані користувача, нам потрібно оновити існуючий HTML, щоб їх відобразити. Ми вже знаємо, як отримати елемент із DOM, наприклад, за допомогою document.getElementById(). Після того, як у вас є базовий елемент, ось деякі API, які ви можете використовувати для його модифікації або додавання дочірніх елементів:

  • Використовуючи властивість textContent, ви можете змінити текст елемента. Зверніть увагу, що зміна цього значення видаляє всіх дочірніх елементів (якщо вони є) і замінює їх на наданий текст. Таким чином, це також ефективний метод для видалення всіх дочірніх елементів даного елемента, присвоюючи йому порожній рядок ''.

  • Використовуючи document.createElement() разом із методом append(), ви можете створювати та додавати один або кілька нових дочірніх елементів.

Використовуючи властивість innerHTML елемента, також можна змінювати його HTML-вміст, але цей метод слід уникати, оскільки він вразливий до атак міжсайтового скриптингу (XSS).

Завдання

Перед тим як перейти до екрану панелі управління, є ще одна річ, яку ми повинні зробити на сторінці login. Зараз, якщо ви спробуєте увійти з ім'ям користувача, яке не існує, повідомлення з'являється в консолі, але для звичайного користувача нічого не змінюється, і ви не знаєте, що відбувається.

Додамо елемент-заповнювач у форму входу, де ми можемо відображати повідомлення про помилку, якщо це необхідно. Хорошим місцем буде розташування перед кнопкою входу <button>:

...
<div id="loginError"></div>
<button>Login</button>
...

Цей елемент <div> порожній, тобто нічого не буде відображатися на екрані, поки ми не додамо до нього вміст. Ми також надаємо йому id, щоб легко отримати його за допомогою JavaScript.

Поверніться до файлу app.js і створіть нову допоміжну функцію updateElement:

function updateElement(id, text) {
  const element = document.getElementById(id);
  element.textContent = text;
}

Ця функція досить проста: отримавши id елемента та текст, вона оновлює текстовий вміст DOM-елемента з відповідним id. Використаємо цей метод замість попереднього повідомлення про помилку у функції login:

if (data.error) {
  return updateElement('loginError', data.error);
}

Тепер, якщо ви спробуєте увійти з недійсним обліковим записом, ви побачите щось на кшталт цього:

Скріншот, що показує повідомлення про помилку під час входу

Тепер у нас є текст помилки, який відображається візуально, але якщо ви спробуєте це з екранним читачем, ви помітите, що нічого не оголошується. Щоб текст, який динамічно додається на сторінку, оголошувався екранними читачами, він повинен використовувати щось, що називається Live Region. Тут ми використаємо конкретний тип live region, який називається alert:

<div id="loginError" role="alert"></div>

Реалізуйте таку ж поведінку для помилок функції register (не забудьте оновити HTML).

Відображення інформації на панелі управління

Використовуючи ті ж техніки, які ми щойно розглянули, ми також займемося відображенням інформації облікового запису на сторінці панелі управління.

Ось як виглядає об'єкт облікового запису, отриманий із сервера:

{
  "user": "test",
  "currency": "$",
  "description": "Test account",
  "balance": 75,
  "transactions": [
    { "id": "1", "date": "2020-10-01", "object": "Pocket money", "amount": 50 },
    { "id": "2", "date": "2020-10-03", "object": "Book", "amount": -10 },
    { "id": "3", "date": "2020-10-04", "object": "Sandwich", "amount": -5 }
  ],
}

Примітка: щоб полегшити собі життя, ви можете використовувати попередньо створений обліковий запис test, який вже заповнений даними.

Завдання

Почнемо з заміни розділу "Баланс" у HTML, щоб додати елементи-заповнювачі:

<section>
  Balance: <span id="balance"></span><span id="currency"></span>
</section>

Ми також додамо новий розділ трохи нижче для відображення опису облікового запису:

<h2 id="description"></h2>

Оскільки опис облікового запису функціонує як заголовок для вмісту під ним, він семантично позначений як заголовок. Дізнайтеся більше про те, чому структура заголовків важлива для доступності, і критично оцініть сторінку, щоб визначити, що ще може бути заголовком.

Далі ми створимо нову функцію в app.js, щоб заповнити елементи-заповнювачі:

function updateDashboard() {
  if (!account) {
    return navigate('/login');
  }

  updateElement('description', account.description);
  updateElement('balance', account.balance.toFixed(2));
  updateElement('currency', account.currency);
}

Спочатку ми перевіряємо, чи маємо необхідні дані облікового запису, перш ніж продовжувати. Потім ми використовуємо функцію updateElement(), яку створили раніше, для оновлення HTML.

Щоб зробити відображення балансу більш привабливим, ми використовуємо метод toFixed(2), щоб примусово відображати значення з 2 цифрами після десяткової точки.

Тепер нам потрібно викликати нашу функцію updateDashboard() кожного разу, коли завантажується сторінка панелі управління. Якщо ви вже завершили завдання уроку 1, це має бути просто, інакше ви можете використати наступну реалізацію.

Додайте цей код у кінці функції updateRoute():

if (typeof route.init === 'function') {
  route.init();
}

І оновіть визначення маршрутів:

const routes = {
  '/login': { templateId: 'login' },
  '/dashboard': { templateId: 'dashboard', init: updateDashboard }
};

З цією зміною кожного разу, коли відображається сторінка панелі управління, викликається функція updateDashboard(). Після входу ви повинні побачити баланс облікового запису, валюту та опис.

Динамічне створення рядків таблиці за допомогою HTML-шаблонів

У першому уроці ми використовували HTML-шаблони разом із методом appendChild() для реалізації навігації в нашому додатку. Шаблони також можуть бути меншими та використовуватися для динамічного заповнення повторюваних частин сторінки.

Ми використаємо схожий підхід для відображення списку транзакцій у HTML-таблиці.

Завдання

Додайте новий шаблон у <body> HTML:

<template id="transaction">
  <tr>
    <td></td>
    <td></td>
    <td></td>
  </tr>
</template>

Цей шаблон представляє один рядок таблиці з трьома стовпцями, які ми хочемо заповнити: дата, об'єкт і сума транзакції.

Потім додайте властивість id до елемента <tbody> таблиці в шаблоні панелі управління, щоб його було легше знайти за допомогою JavaScript:

<tbody id="transactions"></tbody>

Наш HTML готовий, перейдемо до JavaScript і створимо нову функцію createTransactionRow:

function createTransactionRow(transaction) {
  const template = document.getElementById('transaction');
  const transactionRow = template.content.cloneNode(true);
  const tr = transactionRow.querySelector('tr');
  tr.children[0].textContent = transaction.date;
  tr.children[1].textContent = transaction.object;
  tr.children[2].textContent = transaction.amount.toFixed(2);
  return transactionRow;
}

Ця функція робить саме те, що її назва передбачає: використовуючи шаблон, який ми створили раніше, вона створює новий рядок таблиці та заповнює його вміст даними транзакції. Ми використаємо це у функції updateDashboard() для заповнення таблиці:

const transactionsRows = document.createDocumentFragment();
for (const transaction of account.transactions) {
  const transactionRow = createTransactionRow(transaction);
  transactionsRows.appendChild(transactionRow);
}
updateElement('transactions', transactionsRows);

Тут ми використовуємо метод document.createDocumentFragment(), який створює новий фрагмент DOM, з яким ми можемо працювати, перш ніж остаточно прикріпити його до нашої HTML-таблиці.

Є ще одна річ, яку ми повинні зробити, перш ніж цей код зможе працювати, оскільки наша функція updateElement() наразі підтримує лише текстовий вміст. Змінімо її код трохи:

function updateElement(id, textOrNode) {
  const element = document.getElementById(id);
  element.textContent = ''; // Removes all children
  element.append(textOrNode);
}

Ми використовуємо метод append(), оскільки він дозволяє прикріплювати як текст, так і DOM-вузли до батьківського елемента, що ідеально підходить для всіх наших випадків Якщо ви спробуєте увійти за допомогою облікового запису test, тепер ви повинні побачити список транзакцій на панелі управління 🎉.


🚀 Виклик

Працюйте разом, щоб зробити сторінку панелі управління схожою на справжній додаток для банкінгу. Якщо ви вже стилізували свій додаток, спробуйте використати медіа-запити, щоб створити адаптивний дизайн, який добре працює як на настільних комп’ютерах, так і на мобільних пристроях.

Ось приклад стилізованої сторінки панелі управління:

Скріншот прикладу результату панелі управління після стилізації

Вікторина після лекції

Вікторина після лекції

Завдання

Рефакторизуйте та прокоментуйте ваш код


Відмова від відповідальності:
Цей документ був перекладений за допомогою сервісу автоматичного перекладу Co-op Translator. Хоча ми прагнемо до точності, будь ласка, майте на увазі, що автоматичні переклади можуть містити помилки або неточності. Оригінальний документ на його рідній мові слід вважати авторитетним джерелом. Для критичної інформації рекомендується професійний людський переклад. Ми не несемо відповідальності за будь-які непорозуміння або неправильні тлумачення, що виникають внаслідок використання цього перекладу.