Przesyłanie formularzy bez ponownego ładowania strony: Implementacja AJAX w WordPress
Opublikowany: 2022-03-10Jeśli kiedykolwiek chciałeś wysłać formularz bez ponownego ładowania strony, zapewnić funkcję wyszukiwania z wyprzedzeniem, która podpowiada użytkownikowi sugestie podczas wpisywania lub automatycznie zapisywać dokumenty, to potrzebujesz AJAX (znanego również jako XHR ). Do serwera wysyłane jest zakulisowe żądanie, które zwraca dane do formularza. Za każdym razem, gdy zobaczysz animację programu ładującego po wykonaniu jakiejś akcji na stronie, prawdopodobnie jest to żądanie AJAX wysyłane do serwera.
W tym artykule przeprowadzę Cię przez cały proces tworzenia i obsługi wywołań AJAX. Dowiesz się nie tylko, jak wykonać połączenie AJAX, ale także jak zrobić to najlepiej, korzystając z funkcji, które WordPress oferuje programistom zaraz po wyjęciu z pudełka. Zbudujemy prostą wtyczkę, która pozwoli czytelnikom wysłać raport do administratora z informacją o wszelkich błędach, które mogą zauważyć na Twojej stronie.

Na końcu artykułu możesz pobrać działający przykład i w pełni przeanalizować kod.
Zagłębmy się!
AJAX
„Bez przeładowywania strony” to tutaj kluczowe zdanie. AJAX to skrót od Asynchronous JavaScript And XML , ponieważ początkowo dane zwracane z serwera powinny być w formacie XML. Jednak łatwiej jest wysłać je w formacie JSON, który JavaScript bardziej lubi.
Zamierzamy użyć AJAX do wysłania wiadomości e-mail. Nie możesz tego zrobić z front-endu, więc musisz zadzwonić do back-endu. Zwykle wysyłamy żądanie POST do serwera, obsłużymy je i przekierujemy użytkownika z powrotem na stronę z formularzem. W tej iteracji nie chcemy ponownie ładować strony. Zamiast tego wywołujemy backend bezpośrednio, w którym zamierzamy przechwycić dane formularza za pomocą JavaScript, a następnie wysłać asynchroniczne żądanie do serwera, aby obsłużyć odpowiedź.
Istnieją trzy rzeczy, które WordPress AJAX musi działać — pięć z nich działa dobrze . To są:
- Obiekt dla akcji AJAX
- Skrypt JavaScript
- Akcja WordPress
- Ochrona
- Obsługa błędów
Przyjrzyjmy się każdemu z nich.
Obiekt
Naszym przedmiotem jest forma. To jest nasza rzecz do obsługi JavaScript. Zacząłem od stworzenia pliku z nagłówkiem potrzebnym wtyczce WordPress i wstawienia do niego pustego obiektu. Na koniec tworzę nową instancję klasy wtyczki.
<?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();
Mimo że używam tutaj trochę OOP, nie zamierzamy stosować żadnych zaawansowanych praktyk. Kod działałby równie dobrze, gdyby został napisany proceduralnie z oddzielnymi funkcjami. Ale obiekty we wtyczkach WordPress mają jedną zaletę: nie musisz dodawać prefiksów do swoich funkcji — tylko nazwa klasy musi być unikalna.
Pokażmy nasz formularz użytkownikom. Zamierzamy podpiąć się pod filtr the_content
i osadzić formularz na końcu każdego posta. W konstruktorze klasy dodałem filtr:
public function __construct() { add_filter( 'the_content', array( $this, 'report_button' ), 10, 1 ); }
I stworzyłem metodę wywołania zwrotnego z formularzem 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; }
Formularz ma wszystkie potrzebne znaczniki:
- Przycisk do wyświetlenia formularza i wysłania wiadomości;
-
textarea
; - Pojemnik na odpowiedź (będziemy z niego korzystać później).
Przycisk posiada atrybut data-post_id
, który przechowuje aktualny identyfikator posta. Przechwycimy to w JavaScript, aby zidentyfikować artykuł.
Potrzebujemy tylko podstawowej stylizacji, więc zarejestrujmy własny arkusz stylów z akcją wp_enqueue_scripts
i odpowiednim wywołaniem zwrotnym:
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' ); }
Sam arkusz stylów jest bardzo prosty. Chcemy uporządkować projekt.
.report-a-bug-message { display: none; margin-top: 1em; } .report-a-bug-response { margin-top: 1em; }
Tak wygląda przycisk:

Scenariusz
Nasz przedmiot jest gotowy, aby tchnąć w niego trochę życia. Zamiast robić to w zwykłym JavaScript, użyjemy jQuery, które WordPress domyślnie ładuje na każdej stronie.
W istniejącej metodzie scripts dodam plik JS za pomocą funkcji 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' ) ) ); }
Używam również funkcji wp_localize_script
, aby przekazać przetłumaczoną etykietę przycisku do JS. Nie mamy żadnych funkcji gettext
w JavaScript, więc musimy to zrobić tutaj. Ta funkcja utworzy obiekt ustawień , do którego można uzyskać dostęp bezpośrednio z naszego skryptu.
W naszym skrypcie będziemy nasłuchiwać kliknięcia przycisku. Gdy to się stanie, pokażemy obszar textarea
i przełączymy klasę przycisku, aby nasłuchiwać drugiego kliknięcia, które wysyła formularz.
( 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 );
Oto aktualny postęp:

textarea
po kliknięciuTo najlepszy czas na utworzenie żądania AJAX!
Żądania AJAX do WordPress
Aby wysłać żądanie AJAX, naprawdę potrzebujesz tylko jednego parametru: żądanego adresu URL. WordPress posiada specjalny plik dla AJAX, więc nie musimy tworzyć własnego. Jest to /wp-admin/admin-ajax.php
.
Dopóki jesteśmy w obszarze wp-admin
, ten adres URL pliku jest dostępny w JS w zmiennej ajaxurl
. Na froncie musimy sami przekazać tę zmienną. Na szczęście użyliśmy już funkcji wp_localize_script
, więc możemy po prostu dodać do niej kolejny klucz:
wp_localize_script( 'report-a-bug', 'settings', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'send_label' => __( 'Send report', 'reportabug' ) ) );
Przygotowaliśmy wszystkie zmienne, więc stwórzmy wywołanie AJAX. Wyślemy go, gdy użytkownik kliknie przycisk po raz drugi.
$( '.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' ); } ); } );
Nasłuchujemy kliknięcia na zajęciach, które wcześniej włączyliśmy.
Jak widzisz, zmieniam tekst przycisku na „…” Dlaczego? Ponieważ dobrą praktyką jest pokazanie użytkownikowi, że coś się dzieje. Żądania AJAX zależą od wydajności serwera i zajmą trochę czasu. Może 30ms, ale może też 3 sekundy. Jeśli po kliknięciu przycisku wydaje się, że nie ma żadnego efektu, najprawdopodobniej przycisk zostanie kliknięty po raz drugi. Spowodowałoby to zduplikowanie żądania, ponieważ jak już wiesz, są one asynchroniczne.
Następnie tworzę obiekt data
. Zawiera wszystkie zmienne, które zostaną wysłane do wywołania zwrotnego serwera. Plik admin-ajax.php
WordPressa wymaga właściwości action. Musi to być unikalne, chyba że chcesz, aby druga wtyczka obsługiwała twoje żądania. Pozostałe parametry są opcjonalne. Wysyłam identyfikator posta i wiadomość z raportem z textarea
.
Następnie wywołujemy metodę $.post
. Następnie zgadłeś; wysyła żądanie POST. Jest to metoda skrótowa, ale możesz również użyć metody $.ajax
, która ma więcej opcji. Jako pierwszy parametr musimy podać adres URL naszego pliku obsługi, następnie parametry, a następnie funkcję wywołania zwrotnego sukcesu. To jest miejsce, w którym zajmujemy się odpowiedzią. Na razie wysyłamy tylko prostą wiadomość do konsoli przeglądarki.


Jesteśmy gotowi do obsługi żądania na zapleczu.
Akcja WordPress
Być może zastanawiasz się, w jaki sposób podłączymy się do admin-ajax.php . Oczywiście z działaniem! WordPress ma dwa typy akcji:
wp_ajax_nopriv_{$action} wp_ajax_{$action}
Tam, gdzie jest $action
, nazwa akcji przekazana w parametrach AJAX. W naszym przypadku jest to send_bug_report
.
Pierwsza z tych akcji zostanie wykonana tylko dla niezalogowanych użytkowników. Drugi tylko dla zalogowanych użytkowników. Więc jeśli chcesz, aby żądanie było obsługiwane dla obu, musisz zdefiniować oba. Oto co zrobiłem w konstruktorze klasy:
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' ) ); }
W callbacku otrzymuję tylko tytuł posta i wysyłam e-mail:
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' ) ); }
Najważniejszą częścią z punktu widzenia obsługi AJAX w WordPressie jest ostatnia funkcja - wp_send_json_success
, która drukuje dane wyjściowe i matryce zakodowane w formacie JSON. Wszystko, czego potrzebujemy, aby uzyskać odpowiedź za pomocą wywołania zwrotnego AJAX. Ma też brata bliźniaka, wp_send_json_error
, ale do tego dojdziemy później.
Obiekt z tych funkcji ma dwie właściwości:
- sukces
Jest wartością logiczną i zależy od tego, czy nazwiesz ją funkcją sukcesu czy błędu. - dane
Jeśli podasz parametr funkcji.
To wszystko od strony zaplecza. Zajmijmy się odpowiedzią w JS.
Usuwamy przycisk i pole tekstowe oraz wyświetlamy wiadomość zwróconą z serwera w przygotowanym wcześniej kontenerze:
$.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 ); });
I to wszystko! Mamy działające połączenie AJAX! Ale nie poprzestawaj na tym. Nasza prośba nie jest bezpieczna iz pewnością nie jest przyjazna dla użytkownika. Musimy upewnić się, że żądanie jest wykonywane tylko wtedy, gdy jest to konieczne.
Ochrona
Chwilowo
Nonce to „liczba użyta raz”. Jest to krótki hash utworzony z ciągu wejściowego. Możemy go użyć do walidacji żądania — jeśli rzeczywiście zostało wykonane przez WordPressa i nikt go nie podrabia.
Dla przycisku użyjemy innego atrybutu danych:
$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>';
Jak widać, dodaję identyfikator posta do ciągu wejściowego. Będziemy mieć ten identyfikator we właściwościach żądania AJAX, więc jest to po prostu bezpieczniejsze.
Teraz musimy dodać wartość jednorazową do właściwości 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() };
Teraz zwalidujemy go w backendzie za pomocą funkcji check_ajax_referer
, którą WordPress zapewnia:
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' ) ); }
Aby zweryfikować żądanie, musimy ponownie wygenerować ciąg wejściowy, więc używam klucza post_id
wysłanego z AJAX. Drugi parametr to klucz w $_REQUEST array
. Trzeci kontroluje automatyczne wp_die
, jeśli nonce nie pasuje.
Nie chcę, żeby samo umarło. Zamiast tego przechwytuję wynik tej funkcji i wysyłam błąd JSON w ładnym obiekcie.
Możesz również zauważyć użycie funkcji sanitize_text_field
w parametrze wiadomości e-mail. Ma to na celu upewnienie się, że użytkownik nie wyśle żadnych szkodliwych skryptów ani kodu HTML.
Na koniec musimy opakować wywołanie zwrotne sukcesu AJAX w JS w instrukcję if, aby sprawdzić, czy żądanie się powiodło:
$.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 ); } });
Ochrona przycisku
Wiesz, że użytkownik może kliknąć przycisk po raz drugi, zanim AJAX zakończy połączenie. Ale jest jedna prosta sztuczka, która zablokuje drugie kliknięcie - wyłączenie przycisku. Więc po kliknięciu mam zamiar go zablokować i odblokować po otrzymaniu odpowiedzi:
$('.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); }); });
Obsługa błędów
Walidacja
Co się stanie, jeśli użytkownik spróbuje wysłać pustą wiadomość? Nie chcę zawracać sobie głowy takimi mailami. Zablokujmy te próby technikami walidacji.
W JS zamierzam dodać prostą walidację, aby pokazać użytkownikowi, że coś poszło nie tak. Jeśli wiadomość jest pusta, użytkownik zobaczy czerwoną ramkę wokół obszaru tekstowego. Jeśli jest tam wiadomość, przywracamy neutralną granicę:
$('.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 });
Sprawdzanie błędów w działaniu WordPress
Możemy również mieć problem z wysłaniem e-maila. Jeśli nie sprawdzimy wyniku funkcji wp_mail
, użytkownik otrzyma wiadomość o powodzeniu, nawet jeśli nie wysłano żadnego e-maila.
Zajmijmy się tym:
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(); } }
Jak widać, dwukrotnie użyliśmy funkcji wp_send_json_error
, ale nie ma potrzeby wyświetlania unikalnych komunikatów dla użytkowników. Zamiast tego, przekazując dokładny opis błędu, dodam kolejny klucz do naszego obiektu ustawień skryptu, który obejmie oba błędy:
// 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' ) ) );
Pozostało tylko wyświetlić błąd użytkownikowi:
$.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 ); } );
Kompletny przykład
Zrobiliśmy to! Nasza prośba AJAX została złożona; jest dobrze zabezpieczony i przyjazny dla użytkownika. Oto, co zobaczy użytkownik w przypadku jakichkolwiek błędów:

Poniżej możesz pobrać pełną wtyczkę i zapoznać się z przewodnikami użytkownika:
- Pobierz wtyczkę na Github
- Pobierz wtyczkę na Wordpress.org
- Przejdź do instrukcji obsługi
Mam nadzieję, że da to dobry fundament pod własne rozwiązania „bez odświeżania strony”. Możesz to zrobić we wszystkich formularzach w swojej witrynie. To także świetny sposób na optymalizację każdego ciężkiego fragmentu witryny, który nie musi być ładowany od razu, na przykład dużej listy produktów w rozwijanej liście. Możesz je załadować za pomocą AJAX zaraz po kliknięciu menu — to takie proste.
Możliwości są prawie nieograniczone. Dajcie znać w komentarzach jakie są Wasze pomysły!