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
Lee Stott 2daab5271b
Update Quiz Link
3 weeks ago
..
README.md Update Quiz Link 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-сторінка також може бути оновлена за допомогою JavaScript через DOM API. З часом цей підхід еволюціонував у те, що зараз називається односторінковим додатком або 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). Тут ми будемо використовувати специфічний тип живої області, який називається сповіщенням:

<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. Хоча ми прагнемо до точності, будь ласка, майте на увазі, що автоматичні переклади можуть містити помилки або неточності. Оригінальний документ на його рідній мові слід вважати авторитетним джерелом. Для критичної інформації рекомендується професійний людський переклад. Ми не несемо відповідальності за будь-які непорозуміння або неправильні тлумачення, що виникають внаслідок використання цього перекладу.