JS-класс оживляющий все формы на странице.

Задача

Написать страницу с асинхронными формами.
Данную задачу разработчику сайта приходится решать довольно часто.
Возникает вопрос: можно ли её решить раз и навсегда унифицированным способом без применения конструкторов и фрэймворков? Так чтобы все формы отправляли запросы без перезагрузки страницы и не конфликтовали между собой.

Проблемы

При написании обработчиков форм на чистом js у начинающего разработчика возникает множество вопросов:
как повесить обработчик события? На что его вешать? На форму или кнопку? Как вытащить из формы все значения? А если в форме есть селектор? А что делать если в форме инпут файла? Какие должны быть заголовки (headers)? Задавать ли enctype? Как отправить данные без перезагрузки? А если форма переместится в структуре документа – нужно будет переписывать обработчик? Наконец, как получить ответ от сервера и что с ним делать?

Желание

Написать один волшебный класс решающий все проблемы без необходимости писать код по безумному принципу Шайа Лабафа just do it или ещё проще как в сказке “js – по щучьему веленью, по моему прошенью”.
Чтобы всё работало независимо от того сколько на странице форм, чтобы у каждой формы был обработчик. И пусть будет неважно сколько полей у формы и какого они типа.
И без необходимости присваивать формам уникальные id.

Всё что нужно

Написать валидную html разметку
Подключить js-файл
И пусть оно само работает

Решение

И да, в какой то степени, это получилось сделать и уложить решение в примерно 60 строк кода.
Вот оно:

/*
те же действия для отдельных кнопок с классом
made by Erid Nord april 2022
*/
class Server {
    static allForms;
    constructor() {}
    
    // Обработка всех форм
    static main() {
        this.allForms = document.querySelectorAll('form');
        
        for (let element of this.allForms) {
            this.formAddListener(element);
        }
    }
    
    // Повесить обработчик на форму
    static formAddListener(element) {
        element.addEventListener(
            'submit', 
            {   handleEvent: this.formEventListener, 
                server: this
            }
        );
    }
    
    // Обработчик формы
    static async formEventListener(event) {
        event.preventDefault();
        let url = event.target.action;
        let data = new FormData(event.target);
        
        this.server.request(url, data).then(
            result => this.server.formAnswer(result, event.target, '.report'),
            error => this.server.formAnswer(error, event.target, '.report')
        );
    }
    
    // Отправить данные по адресу
    static async request(url, data = {}) {
        let response = await fetch(
            url, 
            {   method: 'POST',
                body: data
            }
        );
        
        return await response.text();
    }
    
    // разобрать ответ сервера
    static formAnswer(result, form, selector = '.report') {
        let block = form.parentElement.querySelector(selector);
        if (!block) {
            console.log(result, block);
            return false;
        }
        
        block.innerHTML = result;
    }
}

Server.main();

Тестирование:

Посмотреть как работает можно тут

Поместите этот скрипт к себе в js/forms.js
Просто подключите файл с этим скриптом к своей странице с аттрибутом defer

Обратите внимание, если один php обрабатывает несколько форм – просто задайте у этих форм скрытое поле с нужным вам значение аттрибута value – и по нему создавайте условие разбора запроса от формы.
Второй момент на который следует обратить внимание это вывод ответа от сервера. По умолчанию, он выводится в консоль браузера. Если нужно вывести его в html документ – то оберните форму в какой нибудь тег и внутри него рзместите тег с классом “report” – перед формой или после формы. Тогда ответ сервера попадёт в этот тег.

для проверки работы создайте страницу с такой html-разметкой:

<!DOCTYPE html>
<html>
<head>
    <link rel="stylesheet" href="main.css">
    <script defer src="js/forms.js"></script>
</head>

<body>
<div class="wrapper">
    <div class="form_wrapper">
        <div class="report">
        </div>
        <form action="/ajax/exec_form.php">
            <input type="text" name="name" placeholder="название" autocomplete="off" />
            <input type="text" name="property" placeholder="свойство" autocomplete="off" />
            <input type="text" name="value" placeholder="значение" autocomplete="off" />
            <input type="submit" value="Отправить" />
        </form>
    </div>
    
    <div class="form_wrapper">
        <div class="report">
        </div>
        <form action="/ajax/exec_form2.php">
            <input type="hidden" name="page_action" placeholder="Событие" value="create" />
            <input type="text" name="value" placeholder="доп параметр" autocomplete="off" />
            <select name="category">
                <option value="1">Холодильники</option>
                <option value="2">Печи</option>
                <option value="3">Мебель</option>
            </select>
            <input type="submit" value="Отправить" />
        </form>
    </div>
    
    <div class="form_wrapper">
        <div class="report">
        </div>
        <form action="/ajax/exec_form2.php">
            <input type="text" name="property" placeholder="доп параметр" autocomplete="off" />
            <input type="hidden" name="page_action" placeholder="Событие" value="upload" />
            <input type="file" name="file" />
            <input type="submit" value="Отправить" />
        </form>
    </div>
    
    <div class="button_wrapper">
        <ul>
            <li><a href="/ajax/execute.php?id=2" class="button">кнопка 1</a></li>
            <li><a href="/ajax/execute.php?id=5" class="button">кнопка 2</a></li>
            <li><a href="/ajax/execute.php?id=12" class="button">кнопка 3</a></li>
        <ul>
    </div>
</div>
</body>
</html>

Не забудьте создать php скрипты на которые отправляете данные: /ajax/exec_form.php и /ajax/exec_form2.php.

В качестве кода этих скриптов можете вставить следующие отладочные заглушки:

<?
echo 'Обработчик ' . basename($_SERVER['PHP_SELF']) . ' получил данные: ';
echo '<pre>';
print_r($_POST);
if (!empty($_FILES)) {
    echo '<p>Переданные файлы</p>';
    print_r($_FILES);
}
echo '</pre>';
?>

Пока не реализовано:

  • возврат json
  • изменение селектора для ответа
  • вывод ошибок по конкретным полям
  • работа с отдельными ссылками, не формами
  • разные действия при получении ответа, например скрытие формы, замена формы ответом или блоком или добавление ответа к списку или таблице

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *