Перейти к содержанию

Computed и отбор заявок

После добавления изменения статуса возникает второй вид состояния: диспетчер хочет видеть только новые заявки или найти слово в заголовке. Сама коллекция не должна исчезать при включении фильтра. Мы сохраним исходное рабочее состояние в tickets, а видимые заявки получим через computed. Результат — один набор предметных данных и производное представление, которое автоматически следует текущему поиску и выбранному статусу.

Производное значение можно однозначно вычислить из других значений. Для списка такими входами служат массив tickets, строка query и значение status. Если хранить visible отдельно и обновлять его вручную, каждый обработчик придётся учить пересчитывать результат. Изменение статуса, очистка поиска и новая загрузка станут тремя возможностями забыть один вызов. Computed описывает зависимость один раз: актуальное представление появляется из актуальных входов, а не из последовательности побочных команд.

Исходное и вычисляемое

В локальном App добавьте signals query и status и поле visible из следующего фрагмента. Импортируйте computed вместе с signal. Поле tickets и набор fixtures остаются из предыдущей главы. В шаблоне замените коллекцию цикла на visible(). Строку поиска можно пока менять из простой кнопки или обработчика input; typed reactive form появится позже. Сам фильтр не зависит от того, каким элементом пользователь задаёт входные значения.

readonly query = signal('');
readonly status = signal<Status | ''>('');
readonly visible = computed(() => {
  const q = this.query().trim().toLocaleLowerCase();
  const selected = this.status();
  return this.tickets().filter(row =>
    (!selected || row.status === selected) &&
    row.title.toLocaleLowerCase().includes(q)
  );
});

Значение status равно пустой строке для режима «Все». Это значение относится к фильтру, а не к Ticket.status: у заявки пустого статуса нет. В условии пустой фильтр пропускает строку, непустой сравнивается с предметным значением. Поиск сначала удаляет пробелы по краям и приводит строку к нижнему регистру, затем сравнивает буквальный фрагмент заголовка. Таким способом «проектор» и « ПРОЕКТОР » ожидаемо обозначают одинаковый запрос на исходном русском наборе.

Если query содержит «проектор», visible ожидаемо состоит из rq-101. Если одновременно выбран active, результат пуст, поскольку rq-101 ещё новая. После локального start rq-101 заявка должна пройти обе части условия. Ничего не нужно вручную добавлять в visible: computed читает изменившийся tickets и получает следующий набор. Этот пример показывает причинную связь между записью предметного состояния и обновлением производного представления. Фильтр не создаёт новую предметную заявку и не сохраняет решение пользователя на сервере.

Подсчёт и пустое представление

Количество показанных строк равно длине visible, а число всех заявок — длине tickets. Они отвечают на разные вопросы. При поиске «проектор» пользователь должен понять, что в локальной коллекции есть три заявки, но условию соответствует одна. Не обозначайте нулевой результат сообщением «Нет данных», если коллекция существует и просто не проходит фильтр. В конечном интерфейсе появится фраза «По этому фильтру заявок нет», которая объясняет возможность изменить условие.

Вычисляемое значение предназначено для чтения. Присваивать ему новый массив или вызывать set нельзя: такая операция не объяснила бы, как должны измениться tickets и фильтры. Если требуется удалить заявку, изменяют tickets; если показать другие заявки, меняют условие отбора. Это разделение помогает находить правильный обработчик события. Проблема «нужно записать в computed» часто означает, что производное представление ошибочно принято за самостоятельный источник предметных данных.

Функция вычисления не должна выполнять запросы, менять другие signals или показывать уведомления. Она отвечает на вопрос о значении. Например, попытка установить query внутри computed может породить неожиданную цепочку пересчётов и делает чтение небезопасным. Для нашей страницы вся необходимая информация уже находится в синхронных значениях. Когда придёт HTTP, запрос будет отдельной асинхронной операцией, а computed сможет вычислять показанный счётчик из готового состояния загрузки.

Границы локального поиска

Сравнение toLocaleLowerCase достаточно для вымышленного русского набора, но не определяет универсальные правила поиска для всех языков. PostgreSQL будет использовать lower в своей locale/collation. Произвольные Unicode-строки могут преобразовываться иначе. Контракт лаборатории фиксирует буквальный поиск и признаёт эту границу. Не превращайте совпадение на трёх заголовках в утверждение, что frontend и база одинаково обрабатывают любой письменный язык, диакритику или нормализацию.

Кроме того, локальное вычисление знает всю свою коллекцию. После перехода к серверу браузер получит максимум двести подходящих строк, а total сможет быть больше. Повторный клиентский фильтр поверх частичного ответа уже не найдёт заявку, которую сервер не прислал. Поэтому в конечной странице q и status отправляются API, а computed shown считает только длину полученного items. Один и тот же инструмент computed решает разные задачи в зависимости от полноты доступных входных данных.

Для проверки зависимости измените только query, затем только status и, наконец, статус одной заявки. Ожидаемое представление нужно объяснить каждый раз до выполнения действий. Если условие требует done, начальный список содержит rq-103; слово «Wi-Fi» вместе с done не содержит ничего. Если очистить оба фильтра, все три строки возвращаются без повторного создания данных. Такой разбор помогает обнаружить неверное логическое «или» там, где интерфейс предполагает совместное выполнение условий.

Порядок visible пока сохраняет порядок tickets. Сортировка могла бы стать ещё одной чистой частью вычисления, однако sort изменяет массив на месте. Перед сортировкой создайте копию, если исходный порядок нужно сохранить. Этот нюанс относится к обычному JavaScript и не исчезает после оборачивания данных в signal. На следующем шаге отфильтрованные заявки станут входными данными отдельного компонента, а предметная коллекция продолжит принадлежать родителю. Сохранённый фильтр не должен постепенно удалять строки из tickets. Если после выбора done заменить tickets отфильтрованным массивом, очистка status уже не вернёт rq-101 и rq-102: исходные входы потеряны. Computed избегает такой ошибки именно благодаря чтению, а не записи в источник. Проверьте мысленно несколько последовательных условий вместо одного удачного поиска. Каждый следующий отбор должен начинаться с актуальной предметной коллекции, а не с результата предыдущего отбора. Это правило остаётся полезным и вне Angular, при любом производном представлении массива.

Статус примера — not_run. Зависимости, приложение, SQL, HTTP и тесты не запускались. Описанные ответы и изменения интерфейса ожидаемые; чтение документации не является проверкой выполнением. Полная конечная версия находится в архиве исходников, а её ограничения и действия читателя — в README и CONTRACT.md.

Перейдите к оглавлению серии или общему маршруту интерактивной веб-разработки. Механизм этой главы сверяется с официальным первичным источником; конкретные версии и непроверенные части перечислены в договоре курса.