Создание и структурирование форм

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

Основы создания форм

Форма в Mithril строится с использованием функции m(), которая создаёт виртуальные узлы. Базовая структура формы выглядит следующим образом:

const FormComponent = {
    view: () =>
        m("form", { onsubmit: (e) => e.preventDefault() },
            m("label", "Имя: "),
            m("input[type=text]", { placeholder: "Введите имя" }),
            m("button[type=submit]", "Отправить")
        )
};

Ключевые моменты:

  • Атрибут onsubmit обязательно обрабатывается через preventDefault(), чтобы предотвратить стандартное поведение браузера.
  • Все элементы формы создаются как виртуальные узлы m(), что позволяет интегрировать их с реактивной моделью данных.

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

Mithril не предоставляет встроенного state management, но реактивные данные можно хранить в простых объектах. Например, связывание поля ввода с данными выглядит так:

const formData = { name: "" };

const FormComponent = {
    view: () =>
        m("form", {
            onsubmit: (e) => {
                e.preventDefault();
                console.log(formData.name);
            }
        },
            m("label", "Имя: "),
            m("input[type=text]", {
                value: formData.name,
                oninput: (e) => formData.name = e.target.value
            }),
            m("button[type=submit]", "Отправить")
        )
};

Особенности:

  • value задаёт текущее значение поля.
  • oninput обновляет объект formData, обеспечивая синхронизацию данных с интерфейсом.
  • Такой подход гарантирует, что форма полностью контролируется реактивной моделью.

Структурирование больших форм

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

Пример разделения формы на блоки:

const PersonalInfo = {
    view: ({ attrs }) =>
        m("fieldset",
            m("legend", "Личная информация"),
            m("label", "Имя: "),
            m("input[type=text]", {
                value: attrs.data.name,
                oninput: (e) => attrs.data.name = e.target.value
            }),
            m("label", "Возраст: "),
            m("input[type=number]", {
                value: attrs.data.age,
                oninput: (e) => attrs.data.age = e.target.value
            })
        )
};

const ContactInfo = {
    view: ({ attrs }) =>
        m("fieldset",
            m("legend", "Контакты"),
            m("label", "Email: "),
            m("input[type=email]", {
                value: attrs.data.email,
                oninput: (e) => attrs.data.email = e.target.value
            }),
            m("label", "Телефон: "),
            m("input[type=tel]", {
                value: attrs.data.phone,
                oninput: (e) => attrs.data.phone = e.target.value
            })
        )
};

const FormComponent = {
    data: { name: "", age: "", email: "", phone: "" },
    view: function() {
        return m("form", {
            onsubmit: (e) => e.preventDefault()
        },
            m(PersonalInfo, { data: this.data }),
            m(ContactInfo, { data: this.data }),
            m("button[type=submit]", "Отправить")
        );
    }
};

Преимущества такого подхода:

  • Лёгкость поддержки и расширения формы.
  • Разделение ответственности между компонентами.
  • Возможность повторного использования подкомпонентов в других формах.

Управление состоянием и валидация

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

const validate = (data) => {
    const errors = {};
    if (!data.name) errors.name = "Имя обязательно";
    if (!data.email.includes("@")) errors.email = "Некорректный email";
    return errors;
};

const FormComponent = {
    data: { name: "", email: "" },
    errors: {},
    view: function() {
        return m("form", {
            onsubmit: (e) => {
                e.preventDefault();
                this.errors = validate(this.data);
                if (Object.keys(this.errors).length === 0) {
                    console.log("Форма валидна", this.data);
                }
            }
        },
            m("label", "Имя: "),
            m("input[type=text]", {
                value: this.data.name,
                oninput: (e) => this.data.name = e.target.value
            }),
            this.errors.name && m("div.error", this.errors.name),
            m("label", "Email: "),
            m("input[type=email]", {
                value: this.data.email,
                oninput: (e) => this.data.email = e.target.value
            }),
            this.errors.email && m("div.error", this.errors.email),
            m("button[type=submit]", "Отправить")
        );
    }
};

Особенности валидации:

  • Ошибки сохраняются в объекте errors.
  • Отображение ошибок происходит динамически с помощью условного рендеринга.
  • Такой подход позволяет легко расширять набор правил валидации.

Динамические поля и повторяющиеся блоки

Mithril поддерживает динамическое создание полей с использованием массивов данных:

const FormComponent = {
    data: { phones: [""] },
    view: function() {
        return m("form", {
            onsubmit: (e) => e.preventDefault()
        },
            m("h3", "Телефоны"),
            this.data.phones.map((phone, index) =>
                m("input[type=tel]", {
                    value: phone,
                    oninput: (e) => this.data.phones[index] = e.target.value
                })
            ),
            m("button[type=button]", {
                onclick: () => this.data.phones.push("")
            }, "Добавить телефон"),
            m("button[type=submit]", "Отправить")
        );
    }
};

Преимущества такого подхода:

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

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

Mithril легко интегрируется с сторонними библиотеками для валидации или масок ввода, так как все элементы формы рендерятся стандартными HTML-тегами. Например, можно подключить библиотеку для форматирования номеров телефонов через oninput или использовать валидацию через onblur.

Оптимизация перерисовок

Mithril обеспечивает эффективное обновление DOM, поэтому каждая реакция на oninput не приводит к полной перерисовке формы. Чтобы дополнительно оптимизировать производительность:

  • Использовать key при рендеринге списков динамических полей.
  • Разбивать форму на подкомпоненты, чтобы перерисовывались только изменённые части.
  • Минимизировать вычисления внутри view, вынося их в отдельные функции.

Фреймворк Mithril позволяет строить гибкие и масштабируемые формы с контролем состояния, валидацией и динамическими полями, сохраняя при этом лёгкость и простоту структуры кода.