Вы когда нибудь считали, сколько форм вы делаете во время разработки веб-приложения? И я не говорю о сложных формах вроде кастомного date-picker’а или же чего-то сложнее, а простых форм с тремя input, двумя select и одним textarea?
Я не считал. Но когда я начал писать очередное приложение на React и мне за один вечер пришлось создать 5 разных форм — мне поплохело. Ну, а когда разработчику плохеет — разработчик пишет велосипед!
Из таких вот соображений на свет появилась пока еще сырая, но уже используемая мной в двух разных проектах, библиотека для создания простейших форм на React. И я даже выделю слово простейших, потому как моя поделка даже близко не стоит рядом с такими проектами как React Forms или же Formsy-React.
Вместо картинки для привлечения внимания — количество однотипного кода, который нам всем приходится писать ради создания простейшей формы с одним полем.
Постановка задачи
После некоторого размышления я осознал, что хочу иметь под рукой React-компонет, готовый:
- Рендерить форму состоящую из простейших элементов (input, select, textarea) каждый из которых легко настраивается (placeholder, default value, type для input, и так далее)…
- … и имеющую одну лишь кнопку Submit, текст и поведение которой настраивается…
- … не имеющую готовых стилей, но способную принимать классы к своим элементам.
- Автоматически собирать данные из самой формы в плоский JSON объект с заданными ключами.
- Иметь простейший механизм валидации полей в форме.
- Показывать ошибки, связанные как с каким-то конкретным полем формы, так и общую ошибку для всей формы.
Также через некоторое время добавилось еще одно требование:
- Каким либо образом описание формы (но не сам рендеринг) должен быть идентичным для React и React Native.
Последние требование появилось после решения пилить проект на React-native. Очень уж не хотелось переписывать все формы по второму разу.
Решение
Вариантом, который в данный момент меня устраивает почти по всем пунктам стал mgr-form-react. Что-то там ещё не доработано, что-то забыто, но всё настолько просто, что может быть добавлено очень быстро и безболезненно. Но давайте пройдемся по главным идеям.
Сразу пример простейшей формы из документации на GitHub:
import Form from "mgr-form-react"; export default TestComponent = () => { // Описание полей формы const controls = [ { element: 'input', // тип поля. Может быть input, select, textarea id: 'Signup.Client.Form.Control.Name', // id DOM-елемента самого control'а label: 'Client name', //текст показаный на лэйбле рядом с полем type: 'text' // тип input'а }, { element: 'select', id: 'Signup.Client.Form.Control.Language', label: 'Client language', options: ['en', 'fr', 'it', 'de', 'ru', 'es'] // список возможных значений для select'а } ]; // Описание поведения и текста кнопки в форме const submit = { text: 'Submit button text', cb: (data) => { console.log(data); } }; // Флаг говорящий является ли форма активной const editable = true; return <Form controls={controls} submit={submit} editable={editable} />; }
Вот собственно и все. По количеству кода короче, конечно, не стало, но мне приятней разделять содержание формы (то есть поля, которые присутствуют в форме) от того, как форма рендерится.
Сейчас готовится такой же компонент для React-native. Плюс формат описания формы в виде JSON-документа я вижу в том, что он может быть определен на сервере и считываться клиетном. Таким образом нам не нужно будет ждать 2 недели в среднем пока клиенты обновят свое приложение, чтобы запретить им, допустим, оставлять фидбэк без электронного адреса для связи.
Пока что количество настраиваемых параметров формы не очень велико, но в планах есть добавление различных функций, как например определение поведения onInput, onChange, onFocus, onBlur для полей формы, добавление кнопок, регистрация собственных типов полей (то есть если вам нужно использовать что-либо более сложное чем input, select или textarea, должна быть возможность зарегистрировать ваш компонент и использовать его в описании формы) и так далее. Ну и, конечно же, проект открыт для пул-реквестов.
Полная документация находится на GitHub, здесь же — просто список.
- controls: массив с объектами, описывающими поля формы. Порядок полей будет соблюден.
— element
— id
— label
— default
— data
— class
— options
— placeholder
— type
— validator
— formatError - submit: объект описывающий текст и поведение кнопки в форме
— text
— cb
— class - errors: объект содержащий ошибки которые должны быть показаны в форме. Возможные поля — general для глобальной ошибки всей формы, или же значения полей id объектов описания формы
- editable: true или false, индикатор активности формы.
import Form from 'mgr-form-react'; export default TestComponent = () => { const controls = [ { element: 'input', id: 'Signup.Client.Form.Control.Name', label: 'Client name', placeholder: 'Client name', default: 'Default name value', type: 'text', data: 'name', // аргумент фунции cb в параметра submit для компонента формы будет иметь поле с ключом "name" и значением данного поля формы validator: /^[A-Za-z0-9\s]{3,30}$/, // регулярное выражения для валидации значения поля formatError: 'Wrong name format', // Ошибка, показанная в случае, когда значение поля не проходит вадидацию class: 'custom-input-class' // css-класс который будет добавлен к данному полю формы }, { element: 'select', id: 'Signup.Client.Form.Control.Language', label: 'Client language', options: ['en', 'fr', 'it', 'de', 'ru', 'es'], default: 'en', data: 'language', class: 'custom-select-class' } ]; const submit = { text: 'Submit button text', cb: (data) => { console.log(data); // { name: "Default name value", language: "en" } в случае дефолтных значений каждого из полей } }; const errors = { 'Signup.Client.Form.Control.Name': 'Name field error that is generated by someone outside of the form (e.g. server response error)', // будет показано рядом с полем имеющим id 'Signup.Client.Form.Control.Name'. general: 'A general error that will be shown under the form itself' // будет показано в отдельном div'е }; const editable = true; return <Form controls={controls} submit={submit} errors={errors} editable={editable} />; }
Стили
Как было сказано выше, мне требовался компонент, генерирующий формы без стилей. Таким образом, стили добавляются уже независимо от того, каким образом была создана форма — используя mgr-form-react или же каким-либо другим способом.
Структура классов в сгенерируемой форме будет следующей:
- mgrform-form — класс обертка для всей формы
- mgrform-control — класс — обертка для каждого поля формы.
- mgrform-has-error — класс, добавляющийся к mgrform-control в случае наличия ошибки для данного поля (либо ошибки валидации, либо ошибки из параметров компонента)
- mgrform-submit-btn — класс кнопки формы
- mgrform-error — класс div элемента, показывающего глобальную ошибку формы.
- к элементам с классами mgrform-control и mgrform-submit-btn добавляются классы, заданные в свойствах class объектов описания поля формы (объекта в массиве controls) и объекта submit.
Заключение
Я вполне понимаю, что это хороший пример велосипедостроения. Однако, мне очень удобно описывать простые формы, не требующие сложной логики внутри себя, в каком-либо виде, возможном для сохранения в отдельный файл с возможностью последующего переиспользования в других проектах.
Само собой я открыт для критики, и разумеется буду рад, если кому нибудь данная поделка сохранит 5-10 минут рабочего времени, которые можно будет потратить на еще одну чашку кофе.
P.S. я не особо гуглил существующие решения похожего типа, буду рад, если кто-нибудь укажет мне на них. Спасибо
ссылка на оригинал статьи https://habrahabr.ru/post/314088/
Добавить комментарий