Angular IDPStrong Junior
🔥 0

Модуль 7 · урок 8 з 10

Живі області

Останній шматок доступності: як повідомити про те, що змінилось, людині, яка не бачить екрана. Спінер вона не побачить, зникнення таблиці не помітить, а «Збережено» у кутку екрана для неї не існує. Живі області вирішують саме це.

aria-live

<div aria-live="polite" class="visually-hidden">
  {{ statusMessage() }}
</div>

Скрінрідер стежить за вмістом такого елемента й озвучує зміни. Сам елемент має бути в DOM заздалегідь — якщо створити його разом із повідомленням, зміну може бути не помічено.

ЗначенняПоведінкаКоли
politeДочекається паузи в мовленніМайже завжди: завантаження, результати, збереження
assertiveПерерве поточне мовленняЛише критичне: втрата даних, помилка збереження
offНе озвучуватиЗначення за замовчуванням
Не зловживай assertive

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

Що озвучувати

export class TasksPage {
  protected readonly announcement = signal('');

  constructor() {
    effect(() => {
      const s = this.state();
      if (s.kind === 'loading') this.say('Завантаження списку задач');
      if (s.kind === 'data')    this.say(`Знайдено задач: ${s.rows.length}`);
      if (s.kind === 'empty')   this.say('Задач не знайдено');
      if (s.kind === 'error')   this.say('Не вдалось завантажити список');
    });
  }

  private say(text: string): void {
    this.announcement.set('');           // скинути — інакше однакове повідомлення не прочитається
    setTimeout(() => this.announcement.set(text));
  }
}

Хитрість зі скиданням потрібна тому, що скрінрідер реагує на зміну тексту. Якщо двічі поспіль встановити те саме значення, другий раз буде проігноровано — а користувач не дізнається, що фільтр застосувався й результат той самий.

Готовий інструмент із CDK

import { LiveAnnouncer } from '@angular/cdk/a11y';

export class TasksPage {
  private readonly announcer = inject(LiveAnnouncer);

  onSorted(column: string, dir: 'asc' | 'desc'): void {
    this.announcer.announce(
      `Відсортовано за колонкою ${column}, ${dir === 'asc' ? 'зростання' : 'спадання'}`,
    );
  }
}

LiveAnnouncer сам створює прихований елемент, керує ним і робить скидання. Якщо в проєкті вже є Angular CDK — простіше взяти його, ніж робити свій.

Що варто озвучувати в нашому застосунку

ПодіяПовідомленняРежим
Почалось завантаження«Завантаження списку»polite
Дані прийшли«Знайдено задач: 42»polite
Фільтр не дав результатів«Нічого не знайдено»polite
Сортування змінилось«Відсортовано за терміном, спадання»polite
Запис збережено«Задачу збережено»polite
Помилка збереження«Не вдалось зберегти»assertive
Сесія завершується«Сесія завершиться за хвилину»assertive

role="alert" і role="status"

<p role="alert">Не вдалось зберегти</p>      <!-- = aria-live="assertive" -->
<p role="status">Задачу збережено</p>        <!-- = aria-live="polite" -->

Це коротший запис того самого. Для повідомлень про помилки у формах із модуля 6 role="alert" — саме те: воно зʼявляється у відповідь на дію користувача, тож перебивання доречне.