Сложные формы и вложенные поля

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

Управление состоянием формы

Solid.js использует реактивность для отслеживания изменений в данных формы. Каждый элемент формы может быть связан с отдельным состоянием с помощью createSignal — функции, которая создает реактивное состояние. Это состояние можно изменять и отслеживать, и оно автоматически обновляет компоненты, связанные с этим состоянием.

Простой пример:

import { createSignal } from 'solid-js';

function SimpleForm() {
  const [name, setName] = createSignal("");
  const [email, setEmail] = createSignal("");

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log({ name: name(), email: email() });
  };

  return (
    <form onSub mit={handleSubmit}>
      <label>
        Имя:
        <input type="text" value={name()} onIn put={(e) => setName(e.target.value)} />
      </label>
      <label>
        Email:
        <input type="email" value={email()} onIn put={(e) => setEmail(e.target.value)} />
      </label>
      <button type="submit">Отправить</button>
    </form>
  );
}

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

Вложенные поля и сложные структуры данных

Для более сложных форм, где данные вложены в несколько уровней (например, массивы или объекты), можно использовать несколько сигналов или объединить их в более сложные структуры. Вложенные поля требуют управления состоянием на нескольких уровнях, что Solid.js легко позволяет делать благодаря гибкости своих реактивных механизмов.

Рассмотрим пример формы для добавления адреса с несколькими вложенными полями:

import { createSignal } from 'solid-js';

function AddressForm() {
  const [address, setAddress] = createSignal({
    street: '',
    city: '',
    zip: ''
  });

  const handleInputChange = (field) => (e) => {
    setAddress(prev => ({
      ...prev,
      [field]: e.target.value
    }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(address());
  };

  return (
    <form onSub mit={handleSubmit}>
      <label>
        Улица:
        <input type="text" value={address().street} onIn put={handleInputChange('street')} />
      </label>
      <label>
        Город:
        <input type="text" value={address().city} onIn put={handleInputChange('city')} />
      </label>
      <label>
        Почтовый индекс:
        <input type="text" value={address().zip} onIn put={handleInputChange('zip')} />
      </label>
      <button type="submit">Отправить</button>
    </form>
  );
}

В этом примере состояние address — это объект с тремя полями: street, city, и zip. Каждый из этих элементов можно обновить отдельно с помощью функции handleInputChange, которая обрабатывает изменения на разных уровнях вложенности. Благодаря такому подходу можно управлять любыми сложными структурами данных, обеспечивая поддержку реактивности на каждом уровне.

Динамическое добавление и удаление полей

Один из случаев, когда формы становятся особенно сложными, — это добавление или удаление полей в зависимости от действий пользователя. Например, форма может включать динамическое добавление нескольких адресов для одной заявки. Solid.js предоставляет простое и эффективное решение для таких случаев.

Пример с динамическим добавлением полей:

import { createSignal } from 'solid-js';

function DynamicAddressForm() {
  const [addresses, setAddresses] = createSignal([{ street: '', city: '', zip: '' }]);

  const handleInputChange = (index, field) => (e) => {
    setAddresses(prev => {
      const newAddresses = [...prev];
      newAddresses[index] = { ...newAddresses[index], [field]: e.target.value };
      return newAddresses;
    });
  };

  const addAddress = () => {
    setAddresses(prev => [...prev, { street: '', city: '', zip: '' }]);
  };

  const removeAddress = (index) => () => {
    setAddresses(prev => prev.filter((_, i) => i !== index));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    console.log(addresses());
  };

  return (
    <form onSub mit={handleSubmit}>
      {addresses().map((address, index) => (
        <div key={index}>
          <label>
            Улица:
            <input type="text" value={address.street} onIn put={handleInputChange(index, 'street')} />
          </label>
          <label>
            Город:
            <input type="text" value={address.city} onIn put={handleInputChange(index, 'city')} />
          </label>
          <label>
            Почтовый индекс:
            <input type="text" value={address.zip} onIn put={handleInputChange(index, 'zip')} />
          </label>
          <button type="button" onCl ick={removeAddress(index)}>Удалить</button>
        </div>
      ))}
      <button type="button" onCl ick={addAddress}>Добавить адрес</button>
      <button type="submit">Отправить</button>
    </form>
  );
}

Здесь создается форма, где можно динамически добавлять и удалять блоки с адресами. Каждый новый адрес добавляется как новый объект в массив addresses, и на каждый из них создается набор полей для ввода. Для каждого поля и блока существует собственная реактивность, и любые изменения автоматически обновляют соответствующие части интерфейса.

Валидация форм

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

Пример валидации для формы с вложенными полями:

import { createSignal } from 'solid-js';

function ValidatedForm() {
  const [address, setAddress] = createSignal({ street: '', city: '', zip: '' });
  const [errors, setErrors] = createSignal({});

  const validate = () => {
    const errors = {};
    const addr = address();
    
    if (!addr.street) errors.street = 'Улица обязательна';
    if (!addr.city) errors.city = 'Город обязателен';
    if (!addr.zip || !/^\d{5}$/.test(addr.zip)) errors.zip = 'Неверный почтовый индекс';

    setErrors(errors);
    return Object.keys(errors).length === 0;
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    if (validate()) {
      console.log(address());
    }
  };

  const handleInputChange = (field) => (e) => {
    setAddress(prev => ({ ...prev, [field]: e.target.value }));
  };

  return (
    <form onSub mit={handleSubmit}>
      <label>
        Улица:
        <input type="text" value={address().street} onIn put={handleInputChange('street')} />
        {errors().street && <div>{errors().street}</div>}
      </label>
      <label>
        Город:
        <input type="text" value={address().city} onIn put={handleInputChange('city')} />
        {errors().city && <div>{errors().city}</div>}
      </label>
      <label>
        Почтовый индекс:
        <input type="text" value={address().zip} onIn put={handleInputChange('zip')} />
        {errors().zip && <div>{errors().zip}</div>}
      </label>
      <button type="submit">Отправить</button>
    </form>
  );
}

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

Заключение

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