В Lit формы и пользовательский ввод обрабатываются через стандартные
HTML-элементы <form>, <input>,
<textarea>, <select> и их события.
Основной механизм взаимодействия с формами заключается в связывании
значений полей с состоянием компонента и отслеживании изменений через
реактивные свойства.
Для определения реактивного состояния используется декоратор
@property или функция static properties.
Например:
import { LitElement, html, css } from 'lit';
import { property } from 'lit/decorators.js';
class MyForm extends LitElement {
@property({ type: String }) name = '';
@property({ type: Number }) age = 0;
handleInput(e) {
const { name, value } = e.target;
this[name] = name === 'age' ? Number(value) : value;
}
render() {
return html`
<form @submit=${this.handleSubmit}>
<label>
Имя:
<input name="name" .value=${this.name} @input=${this.handleInput} />
</label>
<label>
Возраст:
<input name="age" type="number" .value=${this.age} @input=${this.handleInput} />
</label>
<button type="submit">Отправить</button>
</form>
<p>Введено имя: ${this.name}, возраст: ${this.age}</p>
`;
}
handleSubmit(e) {
e.preventDefault();
console.log({ name: this.name, age: this.age });
}
}
customElements.define('my-form', MyForm);
Ключевые моменты:
.value связывает значение инпута с реактивным
свойством. Префикс . указывает на установку свойства
элемента, а не атрибута.@input позволяет отслеживать каждое изменение
поля ввода.preventDefault() в submit предотвращает
стандартное поведение формы.Lit не предоставляет встроенной системы валидации, поэтому используются стандартные HTML-методы и кастомная логика. Например:
handleSubmit(e) {
e.preventDefault();
if (!this.name.trim()) {
alert('Имя обязательно');
return;
}
if (this.age < 0 || this.age > 120) {
alert('Возраст должен быть от 0 до 120');
return;
}
console.log({ name: this.name, age: this.age });
}
Можно также использовать свойства required,
min, max для HTML-элементов:
<input name="age" type="number" .value=${this.age} @input=${this.handleInput} min="0" max="120" required />
checkbox и radioДля чекбоксов и радио важно использовать свойство
checked, а не value. Пример:
@property({ type: Boolean }) subscribed = false;
handleCheckbox(e) {
this.subscribed = e.target.checked;
}
render() {
return html`
<label>
Подписка:
<input type="checkbox" .checked=${this.subscribed} @change=${this.handleCheckbox} />
</label>
<p>Подписка: ${this.subscribed ? 'Да' : 'Нет'}</p>
`;
}
Для радиокнопок:
@property({ type: String }) gender = '';
handleRadio(e) {
this.gender = e.target.value;
}
render() {
return html`
<label>
Мужчина
<input type="radio" name="gender" value="male" .checked=${this.gender === 'male'} @change=${this.handleRadio} />
</label>
<label>
Женщина
<input type="radio" name="gender" value="female" .checked=${this.gender === 'female'} @change=${this.handleRadio} />
</label>
<p>Выбран пол: ${this.gender}</p>
`;
}
<select>
и спискамиДля выпадающих списков используется привязка свойства
.value к реактивному свойству:
@property({ type: String }) country = '';
handleSelect(e) {
this.country = e.target.value;
}
render() {
return html`
<label>
Страна:
<select .value=${this.country} @change=${this.handleSelect}>
<option value="">Выберите...</option>
<option value="ru">Россия</option>
<option value="us">США</option>
<option value="fr">Франция</option>
</select>
</label>
<p>Выбрана страна: ${this.country}</p>
`;
}
Для динамических списков можно использовать массивы и
map:
@property({ type: Array }) countries = ['Россия', 'США', 'Франция'];
@property({ type: String }) selectedCountry = '';
render() {
return html`
<select .value=${this.selectedCountry} @change=${e => this.selectedCountry = e.target.value}>
${this.countries.map(c => html`<option value=${c}>${c}</option>`)}
</select>
<p>Выбрана страна: ${this.selectedCountry}</p>
`;
}
Для больших форм с множеством полей удобно объединять состояние в объект:
@property({ type: Object }) formData = { name: '', age: 0, email: '' };
handleInput(e) {
const { name, value } = e.target;
this.formData = { ...this.formData, [name]: value };
}
render() {
return html`
<form @submit=${this.handleSubmit}>
<input name="name" .value=${this.formData.name} @input=${this.handleInput} />
<input name="age" type="number" .value=${this.formData.age} @input=${this.handleInput} />
<input name="email" type="email" .value=${this.formData.email} @input=${this.handleInput} />
<button type="submit">Отправить</button>
</form>
<pre>${JSON.stringify(this.formData, null, 2)}</pre>
`;
}
Такой подход упрощает работу с динамическими формами и позволяет легко передавать данные на сервер.
Lit автоматически обновляет DOM при изменении реактивных свойств. Для сложных форм это позволяет отображать ошибки валидации, активировать кнопки отправки или показывать подсказки:
@property({ type: Boolean }) isValid = false;
updated(changedProperties) {
if (changedProperties.has('formData')) {
this.isValid = this.formData.name.trim() !== '' && this.formData.age > 0;
}
}
render() {
return html`
<button ?disabled=${!this.isValid}>Отправить</button>
`;
}
Использование реактивных свойств совместно с событиями формы обеспечивает полный контроль над состоянием и визуальным представлением данных.