Валидация входных данных

FAST Element предоставляет удобный механизм для создания веб-компонентов с реактивными свойствами и встроенной поддержкой валидации данных. В основе валидации лежат реактивные свойства (reactive properties), которые могут быть связаны с атрибутами HTML, что позволяет контролировать входные данные на уровне компонента.

Для объявления реактивного свойства используется декоратор @attr или объектная конфигурация свойства в классе компонента:

import { FASTElement, html, css, attr } from "@microsoft/fast-element";

class UserInput extends FASTElement {
    @attr({ mode: "fromView" })
    username;

    @attr({ mode: "fromView" })
    email;
}

Свойства с режимом fromView автоматически синхронизируются с пользовательским вводом, что делает их удобными для проверки и валидации.


Создание правил валидации

FAST Element не предоставляет встроенных формальных валидаторов наподобие React Hook Form, но поддерживает декларативное управление состоянием свойства и его валидируемость через геттеры/сеттеры и подписки на изменения:

class UserInput extends FASTElement {
    @attr username;

    usernameChanged(oldValue, newValue) {
        this.isUsernameValid = /^[a-zA-Z0-9_]{3,15}$/.test(newValue);
    }
}
  • usernameChanged — автоматически вызывается при изменении свойства username.
  • isUsernameValid — флаг, который можно использовать в шаблоне для отображения ошибок.

Также можно использовать вычисляемые свойства:

get usernameError() {
    if (!this.username) return "Поле обязательно для заполнения";
    if (!/^[a-zA-Z0-9_]{3,15}$/.test(this.username)) return "Недопустимые символы";
    return "";
}

Интеграция с шаблонами

FAST Element использует декларативный HTML-шаблон с привязками через ${}. Валидацию удобно интегрировать прямо в шаблон:

const template = html<UserInput>`
    <label>
        Логин:
        <input type="text" 
               value="${x => x.username}" 
               @input="${(x, c) => x.username = c.event.target.value}">
    </label>
    <div class="error" if.bind="${x => x.usernameError}">
        ${x => x.usernameError}
    </div>
`;
  • Атрибут if.bind позволяет динамически отображать блок ошибки только при наличии сообщения.
  • Привязка @input синхронизирует значение поля с реактивным свойством компонента.

Валидация числовых и сложных данных

Для чисел и сложных объектов рекомендуется использовать настраиваемые сеттеры или вспомогательные методы:

class UserInput extends FASTElement {
    @attr age;

    ageChanged(oldValue, newValue) {
        this.ageError = "";
        const numeric = Number(newValue);
        if (isNaN(numeric)) this.ageError = "Должно быть числом";
        else if (numeric < 0 || numeric > 120) this.ageError = "Недопустимый возраст";
    }
}

В шаблоне это интегрируется аналогично текстовому полю:

<input type="number" 
       value="${x => x.age}" 
       @input="${(x, c) => x.age = c.event.target.value}">
<div class="error" if.bind="${x => x.ageError}">
    ${x => x.ageError}
</div>

Универсальные валидаторы

Для повторного использования логики валидации удобно создавать функции-валидаторы:

function required(value) {
    return value ? "" : "Поле обязательно";
}

function minLength(value, length) {
    return value.length >= length ? "" : `Минимум ${length} символов`;
}

function emailFormat(value) {
    return /\S+@\S+\.\S+/.test(value) ? "" : "Неверный формат email";
}

Использование в компоненте:

class UserInput extends FASTElement {
    @attr email;

    get emailError() {
        return required(this.email) || emailFormat(this.email);
    }
}

Асинхронная валидация

FAST Element поддерживает асинхронные операции через методы жизненного цикла или реакции на изменения свойств:

class UserInput extends FASTElement {
    @attr username;
    usernameAvailable = true;

    async usernameChanged(oldValue, newValue) {
        if (!newValue) return;
        this.usernameAvailable = await checkUsernameAvailability(newValue);
    }
}

async function checkUsernameAvailability(username) {
    const response = await fetch(`/api/check-username?username=${username}`);
    const data = await response.json();
    return data.available;
}

В шаблоне можно отобразить статус:

<div class="error" if.bind="${x => !x.usernameAvailable}">
    Имя пользователя уже занято
</div>

Композиция правил

FAST Element позволяет комбинировать несколько валидаторов для одного поля, используя массив функций:

const validators = [
    required,
    value => minLength(value, 3),
    emailFormat
];

function validate(value) {
    for (const fn of validators) {
        const error = fn(value);
        if (error) return error;
    }
    return "";
}

Поддержка форм и сабмитов

Для форм с FAST Element полезно агрегировать ошибки всех полей:

class UserForm extends FASTElement {
    @attr username;
    @attr email;

    get isValid() {
        return !this.usernameError && !this.emailError;
    }

    submit() {
        if (this.isValid) {
            // отправка данных на сервер
        }
    }
}

Привязка кнопки:

<button ?disabled.bind="${x => !x.isValid}" @click="${x => x.submit()}">
    Отправить
</button>

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


Итоговая архитектура валидации

  • Реактивные свойства — основной инструмент отслеживания изменений данных.
  • Сеттеры и propertyChanged методы — проверка значений при изменении.
  • Шаблонные привязки (${} и if.bind) — динамическое отображение ошибок.
  • Функции-валидаторы — переиспользуемая логика проверки.
  • Асинхронные проверки — интеграция с API для динамических условий.
  • Композиция правил — объединение нескольких проверок для одного поля.

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