Валидация данных в Lit тесно связана с реактивностью компонентов и
использованием свойств (properties). Свойства компонентов
определяются с указанием типа и дополнительных опций, что позволяет
встроить базовую проверку данных.
import { LitElement, html, css } from 'lit';
import { property } from 'lit/decorators.js';
class MyComponent extends LitElement {
@property({ type: String })
username = '';
@property({ type: Number })
age = 0;
}
customElements.define('my-component', MyComponent);
Типизация свойств позволяет Lit автоматически
преобразовывать значения атрибутов в нужный тип, что является первой
линией защиты от некорректных данных. Например, если указано
{ type: Number }, значение атрибута будет приведено к
числу.
Для более сложных правил используется переопределение сеттеров свойств. Это позволяет выполнять проверку данных перед их установкой и при необходимости отклонять некорректные значения.
@property({ type: String })
set username(value) {
if (typeof value !== 'string' || value.trim() === '') {
console.warn('Некорректное имя пользователя');
return;
}
const oldValue = this._username;
this._username = value;
this.requestUpdate('username', oldValue);
}
get username() {
return this._username;
}
Сеттеры дают возможность не только валидировать данные, но и инициировать дополнительные побочные эффекты, например, обновление других свойств или UI.
Для компонентов с формами рекомендуется комбинировать стандартные HTML-валидации и кастомные методы. Lit позволяет легко привязывать обработчики событий к элементам формы.
render() {
return html`
<form @submit="${this.handleSubmit}">
<input type="text" .value="${this.username}" @input="${this.handleInput}" required />
<input type="number" .value="${this.age}" @input="${this.handleInput}" min="0" max="120" />
<button type="submit">Отправить</button>
</form>
`;
}
handleInput(e) {
const { name, value } = e.target;
this[name] = value;
}
handleSubmit(e) {
e.preventDefault();
if (!this.validate()) {
console.warn('Данные не прошли проверку');
return;
}
console.log('Данные корректны', { username: this.username, age: this.age });
}
validate() {
if (this.username.trim() === '') return false;
if (this.age < 0 || this.age > 120) return false;
return true;
}
В этом примере HTML-атрибуты required, min
и max дают базовую проверку, а метод
validate() обеспечивает дополнительную бизнес-логику. Такой
подход делает валидацию гибкой и настраиваемой.
Lit поддерживает использование кастомных декораторов для упрощения повторяющихся операций, включая валидацию. Декоратор может автоматически проверять значение свойства перед его установкой.
function validateLength(min, max) {
return function (proto, propName) {
let value = proto[propName];
Object.defineProperty(proto, propName, {
get() {
return value;
},
set(newValue) {
if (newValue.length < min || newValue.length > max) {
console.warn(`Значение ${propName} должно быть от ${min} до ${max} символов`);
return;
}
value = newValue;
this.requestUpdate(propName);
}
});
};
}
class UserForm extends LitElement {
@validateLength(3, 15)
username = '';
}
customElements.define('user-form', UserForm);
Декораторы позволяют централизовать правила валидации, что особенно полезно для больших приложений с повторяющимися формами.
В некоторых случаях проверка данных требует обращения к серверу или внешним API. Lit позволяет интегрировать асинхронные функции в процесс обновления свойств.
async checkUsernameAvailable(username) {
const response = await fetch(`/api/check-username?username=${username}`);
const { available } = await response.json();
return available;
}
async handleInput(e) {
const value = e.target.value;
if (await this.checkUsernameAvailable(value)) {
this.username = value;
} else {
console.warn('Имя пользователя уже занято');
}
}
Асинхронная валидация требует аккуратного управления состоянием и отображения ошибок, чтобы не блокировать UI и не создавать гонки данных.
Lit отлично сочетается с реактивными подходами к формам, например,
хранением состояния в объекте formState и автоматическим
обновлением компонентов при изменении данных.
formState = {
username: '',
age: 0,
errors: {}
};
handleInput(e) {
const { name, value } = e.target;
this.formState[name] = value;
this.formState.errors[name] = this.validateField(name, value);
this.requestUpdate();
}
validateField(name, value) {
switch (name) {
case 'username':
return value.trim() === '' ? 'Имя обязательно' : '';
case 'age':
return value < 0 || value > 120 ? 'Возраст должен быть от 0 до 120' : '';
default:
return '';
}
}
Подход с централизованным состоянием облегчает отображение ошибок и последующую обработку формы.
В компонентах, где свойства могут быть объектами или массивами, рекомендуется создавать отдельные методы проверки структуры данных. Это позволяет избегать неконсистентного состояния и поддерживать целостность данных.
@property({ type: Array })
items = [];
validateItems() {
return this.items.every(item => item.name && item.quantity > 0);
}
addItem(item) {
if (this.validateItem(item)) {
this.items = [...this.items, item];
} else {
console.warn('Некорректный элемент');
}
}