Валидация на стороне клиента

Валидация на стороне клиента в связке с Awesomplete строится вокруг контроля двух уровней: ограничения допустимых значений на этапе выбора из подсказок и проверки итогового значения перед отправкой формы. Библиотека сама по себе отвечает только за автодополнение и фильтрацию списка, поэтому вся логика допустимости вводимых данных реализуется через стандартные механизмы JavaScript и HTML-инпутов, дополняемые событиями Awesomplete.

Awesomplete работает поверх обычного <input> и не блокирует ввод произвольного текста. Это означает, что без дополнительной логики пользователь может ввести значение, отсутствующее в списке. Основная задача валидации — синхронизировать:

  • список допустимых значений (list)
  • текущее значение input
  • выбранный элемент из автодополнения

Ключевая идея заключается в том, что валидным считается только значение, выбранное из списка Awesomplete, а не любое совпадающее текстово.

Ограничение значений через контроль списка

Самый прямой способ валидации — сравнение значения input с источником данных автодополнения.

const allowedValues = ["Apple", "Apricot", "Banana", "Blueberry"];

const input = document.querySelector("#fruit");
const awesomplete = new Awesomplete(input, {
  list: allowedValues
});

Далее добавляется функция проверки:

function isValidValue(value) {
  return allowedValues.includes(value);
}

Такой подход является базовым, но не учитывает поведение пользователя при ручном вводе. Поэтому он дополняется обработкой событий.

Проверка при выборе элемента из подсказки

Awesomplete предоставляет событие выбора элемента через awesomplete-selectcomplete. Оно является ключевой точкой фиксации валидного значения.

input.addEventListener("awesomplete-selectcomplete", function () {
  input.dataset.valid = "true";
});

После выбора из списка значение можно считать валидным без дополнительных проверок.

Обнуление валидности при ручном вводе

При любом изменении input состояние валидности должно сбрасываться, поскольку пользователь может изменить уже корректное значение.

input.addEventListener("input", function () {
  input.dataset.valid = "false";
});

Таким образом формируется простая модель состояния:

  • true — значение выбрано из Awesomplete
  • false — значение изменено вручную или не подтверждено

Валидация при потере фокуса

Одним из ключевых моментов является событие blur, где происходит окончательная проверка.

input.addEventListener("blur", function () {
  if (!isValidValue(input.value)) {
    input.value = "";
    input.classList.add("invalid");
  } else {
    input.classList.remove("invalid");
  }
});

Такой подход гарантирует, что даже если пользователь не выбрал элемент из списка, а ввёл его вручную, значение будет отклонено.

Жёсткое ограничение выбора только из списка

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

input.addEventListener("awesomplete-selectcomplete", function () {
  input.dataset.valid = "true";
});

input.addEventListener("input", function () {
  input.dataset.valid = "false";
});

И финальная проверка перед отправкой:

form.addEventListener("submit", function (e) {
  if (!isValidValue(input.value)) {
    e.preventDefault();
    input.classList.add("invalid");
  }
});

Интеграция с HTML5-валидацией

Awesomplete хорошо сочетается с нативными механизмами HTML5, такими как required, pattern и setCustomValidity.

<input id="fruit" required>
input.addEventListener("input", function () {
  if (!isValidValue(input.value)) {
    input.setCustomValidity("Недопустимое значение");
  } else {
    input.setCustomValidity("");
  }
});

Такой механизм позволяет использовать стандартный UI браузера для отображения ошибок.

Валидация через регулярные выражения

Когда список значений динамический или частично структурированный, используется регулярная проверка.

const pattern = /^[A-Z][a-z]+$/;

function isValidValue(value) {
  return pattern.test(value);
}

Awesomplete при этом остаётся только инструментом подсказок, а не источником истины.

Ограничение значений через нормализацию ввода

Проблема валидации часто связана с различием регистра, пробелов и формата. Поэтому ввод нормализуется перед проверкой.

function normalize(value) {
  return value.trim().toLowerCase();
}

function isValidValue(value) {
  return allowedValues
    .map(v => v.toLowerCase())
    .includes(normalize(value));
}

Нормализация позволяет устранить ложные несоответствия при визуально идентичных значениях.

Запрет ручного ввода вне списка

В некоторых интерфейсах требуется поведение, при котором пользователь может только выбирать элементы из списка. Это реализуется через блокировку неподтверждённых значений.

input.addEventListener("keydown", function (e) {
  if (e.key === "Enter") {
    const match = allowedValues.includes(input.value);
    if (!match) {
      e.preventDefault();
    }
  }
});

Дополнительно можно очищать поле при некорректном вводе:

input.addEventListener("blur", function () {
  if (!allowedValues.includes(input.value)) {
    input.value = "";
  }
});

Множественный ввод и валидация токенов

При использовании Awesomplete для тегов или множественных значений ввод разбивается на части.

const input = document.querySelector("#tags");

function splitValues(value) {
  return value.split(",").map(v => v.trim()).filter(Boolean);
}

Проверка каждого элемента:

function areValid(values) {
  return values.every(v => allowedValues.includes(v));
}

В этом сценарии Awesomplete работает только на уровне последнего токена, а валидация охватывает весь массив.

Валидация на уровне формы

Финальный слой проверки всегда располагается на уровне формы, так как пользователь может обойти любые клиентские события.

form.addEventListener("submit", function (e) {
  const values = splitValues(input.value);

  if (!areValid(values)) {
    e.preventDefault();
    input.classList.add("invalid");
  }
});

Это обеспечивает целостность данных независимо от поведения UI.

Динамическое обновление списка и пересинхронизация валидности

При изменении источника данных список Awesomplete может обновляться, что требует пересчёта валидных значений.

awesomplete.list = newValues;

После обновления важно сбросить текущее состояние:

input.dataset.valid = "false";

Если этого не сделать, ранее валидные значения могут стать недопустимыми, оставаясь в поле.

Обработка асинхронных источников данных

При загрузке списка с сервера валидность определяется только после завершения запроса.

fetch("/api/fruits")
  .then(res => res.json())
  .then(data => {
    awesomplete.list = data;
    allowedValues = data;
  });

Во время загрузки ввод либо блокируется, либо помечается как временно невалидный.

input.disabled = true;

После загрузки:

input.disabled = false;

Приоритизация событий Awesomplete в цепочке валидации

Awesomplete генерирует несколько событий, которые используются для контроля состояния:

  • awesomplete-open
  • awesomplete-close
  • awesomplete-select
  • awesomplete-selectcomplete

Наиболее важным для валидации является awesomplete-selectcomplete, так как он фиксирует итоговое значение после выбора.

input.addEventListener("awesomplete-selectcomplete", function () {
  input.setCustomValidity("");
});

Стилизация состояния ошибки

Валидация обычно сопровождается визуальным состоянием поля.

input.invalid {
  border-color: red;
  background-color: #ffecec;
}

Изменение класса происходит синхронно с логикой проверки:

function markInvalid(state) {
  input.classList.toggle("invalid", !state);
}

Контроль целостности данных при комбинированном вводе

В сложных формах Awesomplete используется вместе с другими источниками ввода. В этом случае валидация строится по принципу приоритетов:

  1. Выбор из Awesomplete
  2. Проверка по локальному списку
  3. Проверка через серверную модель
  4. Финальная проверка формы

Такая многоуровневая схема позволяет исключить расхождения между UI и бизнес-логикой.

Синхронизация состояния между Awesomplete и бизнес-логикой

Awesomplete не хранит состояние валидности, поэтому оно дублируется в отдельной переменной.

let state = {
  valid: false,
  value: ""
};

Обновление состояния:

input.addEventListener("awesomplete-selectcomplete", function () {
  state.valid = true;
  state.value = input.value;
});

Такой подход позволяет отделить UI-слой от логики валидации и использовать единый источник истины при обработке формы.