Отправка форм без перезагрузки страницы: реализация AJAX в WordPress

Опубликовано: 2022-03-10
Краткий обзор ↬ Существует множество решений «без обновления страницы», но вот одно из них, которое вы можете создать с помощью AJAX. В этом руководстве вы узнаете, как создать простой плагин, который позволит читателям отправлять отчеты без перезагрузки страницы.

Если вы когда-либо хотели отправить форму без перезагрузки страницы, предоставить функцию упреждающего поиска, которая предлагает пользователю предложения по мере ввода, или автоматическое сохранение документов, тогда вам нужен AJAX (также известный как XHR ). За кулисами запрос отправляется на сервер и возвращает данные в вашу форму. Всякий раз, когда вы видите анимацию загрузчика после того, как вы сделали какое-либо действие на странице, это, вероятно, запрос AJAX, отправленный на сервер.

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

Процесс отправки отчета об ошибке
Пользователь обнаруживает ошибку и нажимает кнопку «Сообщить об ошибке». В результате представлена ​​форма для сообщения. При повторном нажатии на кнопку пользователь будет отправлен к администратору.

В конце статьи вы можете скачать рабочий пример и полностью изучить код.

Давайте копать!

Еще после прыжка! Продолжить чтение ниже ↓

АЯКС

«Без перезагрузки страницы» здесь ключевое предложение. AJAX означает асинхронный JavaScript и XML , потому что изначально данные, возвращаемые с сервера, должны быть в формате XML. Однако их проще отправить в формате JSON, который больше нравится JavaScript.

Мы собираемся использовать AJAX для отправки электронной почты. Вы не можете сделать это из внешнего интерфейса, поэтому вам нужно вызвать серверную часть. Обычно мы отправляем POST-запрос на сервер, обрабатываем его и перенаправляем пользователя обратно на страницу с формой. В этой итерации мы не хотим перезагружать страницу. Вместо этого мы напрямую вызываем серверную часть, где мы собираемся захватить данные формы с помощью JavaScript, а затем отправить асинхронный запрос на сервер для обработки ответа.

Есть три вещи, которые необходимы для работы WordPress AJAX — пять из них, чтобы работать хорошо . Эти:

  • Объект для действия AJAX
  • JavaScript-скрипт
  • WordPress действие
  • Защита
  • Обработка ошибок

Давайте подробнее рассмотрим каждый из них.

Объект

Наш объект — форма. Это наша задача с помощью JavaScript. Я начал с создания файла с заголовком, необходимого плагину WordPress, и поместил внутрь пустой объект. В конце я создаю новый экземпляр класса плагина.

 <?php /* Plugin Name: Report a bug Description: Allow your visitors to report a bug in your articles Author: Jakub Mikita Author URI: https://underdev.it Version: 1.0 License: GPL2 Text Domain: reportabug */ class Report_a_bug { } new Report_a_bug();

Несмотря на то, что я использую здесь некоторые ООП, мы не собираемся использовать какие-либо передовые методы. Код будет работать так же хорошо, если он написан процедурно с отдельными функциями. Но у объектов в плагинах WordPress есть одно преимущество: вам не нужно добавлять префикс к вашим функциям — уникальным должно быть только имя класса.

Давайте покажем нашу форму пользователям. Мы собираемся подключиться к фильтру the_content и встроить форму в конце каждого сообщения. В конструкторе класса я добавил фильтр:

 public function __construct() { add_filter( 'the_content', array( $this, 'report_button' ), 10, 1 ); }

И создал метод обратного вызова с формой HTML:

 public function report_button( $content ) { // display button only on posts if ( ! is_single() ) { return $content; } $content .= '<div class="report-a-bug"> <button class="show-form" data-post_>' . __( 'Report a bug', 'reportabug' ) . '</button> <textarea class="report-a-bug-message" placeholder="' . __( 'Describe what\'s wrong...', 'reportabug' ) . '"></textarea> <p class="report-a-bug-response"></p> </div>'; return $content; }

На форме есть вся необходимая разметка:

  • Кнопка для отображения формы и отправки сообщения;
  • textarea ;
  • Контейнер для ответа (мы собираемся использовать его позже).

Кнопка имеет атрибут data-post_id , в котором хранится идентификатор текущего поста. Мы возьмем это в JavaScript, чтобы идентифицировать статью.

Нам нужны только базовые стили, поэтому давайте зарегистрируем нашу собственную таблицу стилей с действием wp_enqueue_scripts и соответствующим обратным вызовом:

 public function __construct() { add_filter( 'the_content', array( $this, 'report_button' ), 10, 1 ); add_action( 'wp_enqueue_scripts', array( $this, 'scripts' ) ); } function scripts() { wp_enqueue_style( 'report-a-bug', plugin_dir_url( __FILE__ ) . 'css/style.css' ); }

Сама таблица стилей очень проста. Хотим привести дизайн в порядок.

 .report-a-bug-message { display: none; margin-top: 1em; } .report-a-bug-response { margin-top: 1em; }

Вот так выглядит кнопка:

кнопка сообщить об ошибке
Кнопка "Сообщить об ошибке"

Скрипт

У нас есть объект, готовый вдохнуть в него немного жизни. Вместо простого JavaScript мы будем использовать jQuery, который WordPress загружает по умолчанию на каждой странице.

В существующем методе сценариев я добавлю файл JS, используя функцию wp_enqueue_script .

 function scripts() { wp_enqueue_style( 'report-a-bug', plugin_dir_url( __FILE__ ) . 'css/style.css' ); wp_enqueue_script( 'report-a-bug', plugin_dir_url( __FILE__ ) . 'js/scripts.js', array( 'jquery' ), null, true ); // set variables for script wp_localize_script( 'report-a-bug', 'settings', array( 'send_label' => __( 'Send report', 'reportabug' ) ) ); }

Я также использую функцию wp_localize_script для передачи переведенной метки кнопки в JS. У нас нет функций gettext в JavaScript, поэтому мы должны сделать это здесь. Эта функция создаст объект настроек, к которому можно получить доступ непосредственно из нашего скрипта.

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

 ( function( $ ) { $( document ).ready( function() { $( '.report-a-bug' ).on( 'click', '.show-form', function( event ) { // change label and switch class $( this ).text( settings.send_label ).removeClass( 'show-form' ).addClass( 'send-report' ); // show textarea $( '.report-a-bug-message' ).slideDown( 'slow' ); }) }); })( jQuery );

Вот текущий прогресс:

Выпадающее текстовое поле при клике
textarea выпадающее при клике

Самое время создать запрос AJAX!

AJAX-запросы к WordPress

Чтобы отправить запрос AJAX, вам действительно нужен только один-единственный параметр: запрошенный URL. В WordPress есть специальный файл для AJAX, поэтому нам не нужно создавать свой собственный. Это /wp-admin/admin-ajax.php .

Пока мы находимся в области wp-admin , этот URL-адрес файла доступен в JS в переменной ajaxurl . Во внешнем интерфейсе мы должны передать эту переменную самостоятельно. К счастью, мы уже использовали функцию wp_localize_script , поэтому мы можем просто добавить к ней еще один ключ:

 wp_localize_script( 'report-a-bug', 'settings', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'send_label' => __( 'Send report', 'reportabug' ) ) );

Мы подготовили все переменные, поэтому давайте создадим вызов AJAX. Мы собираемся отправить его, как только пользователь нажмет кнопку во второй раз.

 $( '.report-a-bug' ).on( 'click', '.send-report', function( event ) { var $button = $( this ); $button.width( $button.width() ).text('...'); // set ajax data var data = { 'action' : 'send_bug_report', 'post_id': $button.data( 'post_id' ), 'report' : $( '.report-a-bug-message' ).val() }; $.post( settings.ajaxurl, data, function( response ) { console.log( 'ok' ); } ); } );

Мы слушаем щелчок по классу, который мы включили ранее.

Как видите, я меняю текст кнопки на «…» Почему? Потому что это хорошая практика — показывать пользователю, что что-то происходит. Запросы AJAX зависят от производительности сервера и занимают некоторое время. Может 30 мс, а может и 3 секунды. Если после нажатия на кнопку кажется, что эффекта нет, скорее всего, кнопка будет нажата во второй раз. Это приведет к дублированию запроса, потому что, как вы теперь знаете, они асинхронны.

Далее я создаю объект data . Он содержит все переменные, которые будут отправлены обратному вызову сервера. Для файла WordPress admin-ajax.php требуется свойство action. Это должно быть уникальным, если вы не хотите, чтобы другой плагин обрабатывал ваши запросы. Остальные параметры являются необязательными. Я отправляю идентификатор сообщения и сообщение отчета из textarea .

Затем мы вызываем метод $.post . Далее, вы догадались; он отправляет запрос POST. Это сокращенный метод, но вы также можете использовать метод $.ajax , который имеет больше возможностей. В качестве первого параметра мы должны передать URL-адрес нашего файла обработчика, затем параметры и затем функцию обратного вызова успеха. Это место, где мы обрабатываем ответ. На данный момент мы просто отправляем простое сообщение в консоль браузера.

Отправка запроса AJAX на серверную часть
Отправка запроса AJAX на серверную часть

Мы готовы обработать запрос на серверной части.

Действие WordPress

Вам может быть интересно, как мы подключаемся к admin-ajax.php . С действием, конечно! WordPress имеет два типа действий:

 wp_ajax_nopriv_{$action} wp_ajax_{$action}

Где $action — имя действия, переданное в параметрах AJAX. В нашем случае это send_bug_report .

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

 public function __construct() { add_filter( 'the_content', array( $this, 'report_button' ), 10, 1 ); add_action( 'wp_enqueue_scripts', array( $this, 'scripts' ) ); add_action( 'wp_ajax_nopriv_send_bug_report', array( $this, 'send_bug_report' ) ); add_action( 'wp_ajax_send_bug_report', array( $this, 'send_bug_report' ) ); }

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

 function send_bug_report() { $data = $_POST; $post_title = get_the_title( intval( $data['post_id'] ) ); wp_mail( '[email protected]', 'Bug report in post: ' . $post_title, $data['report'] ); wp_send_json_success( __( 'Thanks for reporting!', 'reportabug' ) ); }

Самая важная часть с точки зрения обработки AJAX в WordPress — это последняя функция — wp_send_json_success , которая печатает вывод в формате JSON и умирает. Все, что нам нужно, чтобы получить ответ с обратным вызовом успеха AJAX. У него также есть брат-близнец, wp_send_json_error , но мы вернемся к этому позже.

Объект из этих функций имеет два свойства:

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

Это все с обратной стороны. Давайте обработаем ответ в JS.

Мы удалим кнопку и текстовое поле и отобразим сообщение, возвращенное сервером, в контейнере, который мы подготовили ранее:

 $.post(settings.ajaxurl, data, function(response) { // remove button and textarea $button.remove(); $('.report-a-bug-message').remove(); // display success message $('.report-a-bug-response').html( response.data ); });

Вот и все! У нас есть работающий вызов AJAX! Но не останавливайтесь на достигнутом. Наш запрос не является безопасным и, конечно, неудобным для пользователя. Мы должны убедиться, что запрос выполняется только тогда, когда это необходимо.

Защита

одноразовый номер

Nonce — это «число, используемое один раз». Это короткий хэш, созданный из входной строки. Мы можем использовать его для проверки запроса — если он действительно был сделан WordPress и никто его не подделывает.

Мы собираемся использовать другой атрибут данных для кнопки:

 $nonce = wp_create_nonce( 'report_a_bug_' . get_the_ID() ); $content .= '<div class="report-a-bug"> <button class="show-form" data-nonce="' . $nonce . '" data-post_>' . __( 'Report a bug', 'reportabug' ) . '</button> <textarea class="report-a-bug-message" placeholder="' . __( 'Describe what\'s wrong...', 'reportabug' ) . '"></textarea> <p class="report-a-bug-response"></p> </div>';

Как видите, я добавляю идентификатор сообщения в строку ввода. У нас будет этот идентификатор, доступный в свойствах запроса AJAX, так что это просто более безопасно.

Теперь нам нужно добавить одноразовый номер в свойства AJAX:

 // set ajax data var data = { 'action' : 'send_bug_report', 'post_id': $button.data('post_id'), 'nonce' : $button.data('nonce'), 'report' : $('.report-a-bug-message').val() };

Теперь мы проверим его в бэкенде с помощью функции check_ajax_referer , которую предоставляет WordPress:

 function send_bug_report() { $data = $_POST; // check the nonce if ( check_ajax_referer( 'report_a_bug_' . $data['post_id'], 'nonce', false ) == false ) { wp_send_json_error(); } $post_title = get_the_title( intval( $data['post_id'] ) ); wp_mail( '[email protected]', 'Bug report in post: ' . $post_title, sanitize_text_field( $data['report'] ) ); wp_send_json_success( __( 'Thanks for reporting!', 'reportabug' ) ); }

Чтобы проверить запрос, мы должны повторно сгенерировать входную строку, поэтому я использую ключ post_id , отправленный из AJAX. Второй параметр — это ключ в $_REQUEST array . Третий управляет автоматическим wp_die , если одноразовый номер не совпадает.

Я не хочу, чтобы он умер сам. Вместо этого я перехватываю результат этой функции и отправляю ошибку JSON в красивом объекте.

Вы также можете заметить использование функции sanitize_text_field в параметре сообщения электронной почты. Это просто для того, чтобы пользователь не отправлял вредоносные скрипты или HTML.

Наконец, нам нужно обернуть обратный вызов успеха AJAX в JS в операторе if, чтобы проверить, был ли запрос успешным:

 $.post(settings.ajaxurl, data, function(response) { if ( response.success == true ) { // remove button and textarea $button.remove(); $('.report-a-bug-message').remove(); // display success message $('.report-a-bug-response').html( response.data ); } });

Защита кнопок

Вы знаете, что пользователь может нажать кнопку второй раз, прежде чем AJAX завершит вызов. Но есть один простой трюк, который заблокирует второй щелчок — отключение кнопки. Итак, после клика я собираюсь заблокировать его и разблокировать после получения ответа:

 $('.report-a-bug').on('click', '.send-report', function(event) { var $button = $(this); $button.width( $button.width() ).text('...').prop('disabled', true); // set ajax data var data = {...}; $.post(settings.ajaxurl, data, function(response) { if ( response.success == true ) {...} // enable button $button.prop('disabled', false); }); });

Обработка ошибок

Проверка

Что, если пользователь попытается отправить пустое сообщение? Я не хочу, чтобы меня беспокоили такие письма. Давайте заблокируем эти попытки методами проверки.

В JS я собираюсь добавить простую проверку, чтобы показать пользователю, что что-то пошло не так. Если сообщение пусто, пользователь увидит красную рамку вокруг текстовой области. Если там присутствует сообщение, мы восстанавливаем нейтральную границу:

 $('.report-a-bug').on('click', '.send-report', function(event) { var $button = $(this); // check if message is not empty if ( $( '.report-a-bug-message' ).val().length === 0 ) { $( '.report-a-bug-message' ).css( 'border', '1px solid red' ); return false; } else { $( '.report-a-bug-message' ).css( 'border', '1px solid rgba(51, 51, 51, 0.1)' ); } // ... ajax });

Проверка на ошибки в действии WordPress

У нас также может возникнуть проблема с отправкой электронной почты. Если мы не проверим результат функции wp_mail , пользователь получит сообщение об успешном завершении, даже если письмо не было отправлено.

Давайте разберемся с этим:

 function send_bug_report() { $data = $_POST; // check the nonce if ( check_ajax_referer( 'report_a_bug_' . $data['post_id'], 'nonce', false ) == false ) { wp_send_json_error(); } $post_title = get_the_title( intval( $data['post_id'] ) ); $result = wp_mail( '[email protected]', 'Bug report in post: ' . $post_title, sanitize_text_field( $data['report'] ) ); if ( $result == false ) { wp_send_json_success( __( 'Thanks for reporting!', 'reportabug' ) ); } else { wp_send_json_error(); } }

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

 // set variables for script wp_localize_script( 'report-a-bug', 'settings', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'send_label' => __( 'Send report', 'reportabug' ), 'error' => __( 'Sorry, something went wrong. Please try again', 'reportabug' ) ) );

Все, что осталось сделать, это отобразить ошибку пользователю:

 $.post( settings.ajaxurl, data, function( response ) { if ( response.success == true ) { // remove button and textarea $button.remove(); $( '.report-a-bug-message' ).remove(); // display success message $( '.report-a-bug-response' ).html( response.data ); } else { // display error message $( '.report-a-bug-response' ).html( settings.error ); } // enable button and revert label $button.text( settings.send_label ).prop( 'disabled', false ); } );

Полный пример

Мы сделали это! Наш запрос AJAX был сделан; он хорошо защищен и удобен для пользователя. Вот что увидит пользователь в случае каких-либо ошибок:

Готовый пример, запрос AJAX с проверкой
Готовый пример, запрос AJAX с проверкой

Ниже вы можете скачать полный плагин и ознакомиться с руководствами пользователя:

  • Загрузите плагин на Github
  • Скачать плагин на Wordpress.org
  • Перейти к руководствам пользователя

Я надеюсь, что это даст вам хорошую основу для ваших собственных решений «без обновления страницы». Вы можете сделать это со всеми формами на вашем сайте. Это также отличный способ оптимизировать любую тяжелую часть веб-сайта, которую не нужно загружать сразу, например, большой список продуктов в раскрывающемся списке. Вы можете загрузить их через AJAX сразу после нажатия на раскрывающийся список — это так просто.

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