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

Stencil — это современный фреймворк для создания веб-компонентов с использованием стандартов веб-технологий. Он позволяет разработчикам создавать компоненты, которые могут работать в любых современных браузерах и быть интегрированы в любые существующие приложения, используя такие технологии как JavaScript, TypeScript, HTML и CSS. Важной частью разработки любого приложения является валидация данных, особенно когда речь идет о компонентах пользовательского интерфейса.

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

Валидация с помощью свойств компонента

Stencil использует декораторы для объявления свойств компонента. Эти свойства могут быть валидированы с помощью стандартных механизмов JavaScript или TypeScript. Один из распространенных способов валидации — использование типов данных, задаваемых для каждого свойства компонента. Типы обеспечивают базовую проверку на соответствие, однако для более сложных проверок можно использовать дополнительные методы.

Пример базовой валидации с типами

@Component({
  tag: 'app-input',
  styleUrl: 'app-input.css',
  shadow: true
})
export class AppInput {
  @Prop() value: string;

  @Watch('value')
  validateValue(newValue: string) {
    if (newValue.length < 3) {
      console.error('Значение должно быть не менее 3 символов');
    }
  }
}

В этом примере используется декоратор @Prop(), который определяет свойство value компонента. При изменении этого свойства срабатывает метод validateValue, который проверяет, что длина значения больше или равна 3 символам. Такой подход позволяет выполнять валидацию при каждом изменении данных.

Сложная валидация с асинхронными проверками

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

Пример асинхронной валидации

@Component({
  tag: 'app-username',
  styleUrl: 'app-username.css',
  shadow: true
})
export class AppUsername {
  @Prop() username: string;

  @Watch('username')
  async validateUsername(newUsername: string) {
    if (newUsername.length < 3) {
      console.error('Имя пользователя должно быть не менее 3 символов');
      return;
    }

    const isUsernameTaken = await this.checkUsernameAvailability(newUsername);
    if (isUsernameTaken) {
      console.error('Это имя пользователя уже занято');
    }
  }

  async checkUsernameAvailability(username: string): Promise<boolean> {
    // Здесь можно выполнить запрос к серверу
    const response = await fetch(`/api/check-username?username=${username}`);
    const data = await response.json();
    return data.isTaken;
  }
}

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

Использование кастомных валидаторов

Stencil позволяет создавать кастомные валидаторы, которые могут быть использованы в компонентах для более сложных проверок. Такие валидаторы могут быть реализованы как отдельные функции или методы, которые принимают данные и возвращают либо true, либо ошибку валидации.

Пример кастомного валидатора

function isValidEmail(email: string): boolean {
  const re = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
  return re.test(email);
}

@Component({
  tag: 'app-email',
  styleUrl: 'app-email.css',
  shadow: true
})
export class AppEmail {
  @Prop() email: string;

  @Watch('email')
  validateEmail(newEmail: string) {
    if (!isValidEmail(newEmail)) {
      console.error('Неверный формат email');
    }
  }
}

Здесь функция isValidEmail реализует проверку формата email с помощью регулярного выражения. Метод validateEmail использует этот валидатор для проверки введенного email и выводит ошибку, если формат неверный.

Обработка ошибок и отображение сообщений

После выполнения валидации важно правильно обработать ошибки и уведомить пользователя о некорректных данных. В Stencil можно использовать такие подходы, как отображение текстовых сообщений рядом с полями ввода, либо визуальное выделение ошибок.

Пример отображения ошибок

@Component({
  tag: 'app-input',
  styleUrl: 'app-input.css',
  shadow: true
})
export class AppInput {
  @Prop() value: string;
  @State() errorMessage: string;

  @Watch('value')
  validateValue(newValue: string) {
    if (newValue.length < 3) {
      this.errorMessage = 'Значение должно быть не менее 3 символов';
    } else {
      this.errorMessage = '';
    }
  }

  render() {
    return (
      <div>
        <input type="text" value={this.value} />
        {this.errorMessage && <p class="error">{this.errorMessage}</p>}
      </div>
    );
  }
}

В этом примере введенное значение проверяется на минимальную длину. Если длина меньше 3 символов, отображается сообщение об ошибке. Визуально это сообщение выводится в элемент <p> с классом error.

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

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

Пример валидации формы

@Component({
  tag: 'app-form',
  styleUrl: 'app-form.css',
  shadow: true
})
export class AppForm {
  @State() name: string;
  @State() email: string;
  @State() errorMessage: string;

  validateForm() {
    if (!this.name || !this.email) {
      this.errorMessage = 'Все поля обязательны для заполнения';
      return false;
    }

    if (!isValidEmail(this.email)) {
      this.errorMessage = 'Неверный формат email';
      return false;
    }

    this.errorMessage = '';
    return true;
  }

  handleSubmit(event: Event) {
    event.preventDefault();

    if (this.validateForm()) {
      console.log('Форма отправлена');
    }
  }

  render() {
    return (
      <form onSub mit={(event) => this.handleSubmit(event)}>
        <input type="text" value={this.name} onIn put={(event) => this.name = (event.target as HTMLInputElement).value} />
        <input type="email" value={this.email} onIn put={(event) => this.email = (event.target as HTMLInputElement).value} />
        {this.errorMessage && <p class="error">{this.errorMessage}</p>}
        <button type="submit">Отправить</button>
      </form>
    );
  }
}

Здесь реализована форма с двумя полями — для имени и email. При отправке формы выполняется валидация, которая проверяет обязательность заполнения полей и корректность email. Если данные некорректны, выводится сообщение об ошибке.

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

Stencil можно интегрировать с внешними библиотеками для более сложной валидации данных. Одним из популярных решений является библиотека yup, которая предоставляет мощные средства для валидации и обработки ошибок.

Пример с библиотекой yup

import * as yup from 'yup';

@Component({
  tag: 'app-form',
  styleUrl: 'app-form.css',
  shadow: true
})
export class AppForm {
  @State() name: string;
  @State() email: string;
  @State() errorMessage: string;

  schema = yup.object().shape({
    name: yup.string().required('Имя обязательно'),
    email: yup.string().email('Неверный формат email').required('Email обязателен'),
  });

  async validateForm() {
    try {
      await this.schema.validate({ name: this.name, email: this.email });
      this.errorMessage = '';
      return true;
    } catch (error) {
      this.errorMessage = error.message;
      return false;
    }
  }

  handleSubmit(event: Event) {
    event.preventDefault();
    this.validateForm().then(valid => {
      if (valid) {
        console.log('Форма отправлена');
      }
    });
  }

  render() {
    return (
      <form onSub mit={(event) => this.handleSubmit(event)}>
        <input type="text" value={this.name} onIn put={(event) => this.name = (event.target as HTMLInputElement).value} />
        <input type="email