Enviando formulários sem recarregar a página: implementação de AJAX no WordPress
Publicados: 2022-03-10Se você já quis enviar um formulário sem recarregar a página, fornecer uma função de pesquisa antecipada que solicite ao usuário sugestões à medida que ele digita ou salvar documentos automaticamente, então o que você precisa é de AJAX (também conhecido como XHR ). Uma solicitação de bastidores é enviada ao servidor e retorna os dados ao seu formulário. Sempre que você vê uma animação do carregador depois de ter feito alguma ação na página, provavelmente é uma solicitação AJAX sendo enviada ao servidor.
Neste artigo, vou guiá-lo por todo o processo de criação e tratamento de chamadas AJAX. Você aprenderá não apenas como fazer uma chamada AJAX, mas também como fazê-lo da melhor maneira usando os recursos que o WordPress oferece aos desenvolvedores imediatamente. Construiremos um plugin simples que permitirá aos leitores enviar um relatório ao administrador com informações sobre qualquer bug que possam detectar em seu site.
No final do artigo, você pode baixar o exemplo de trabalho e examinar o código na íntegra.
Vamos cavar!
AJAX
“Sem recarregar a página” é a frase chave aqui. AJAX significa Asynchronous JavaScript And XML porque, inicialmente, os dados retornados do servidor devem estar em XML. No entanto, é mais fácil enviá-los em JSON, que o JavaScript gosta mais.
Vamos usar AJAX para enviar um e-mail. Você não pode fazer isso no front-end, então você tem que chamar o back-end. Normalmente, enviamos uma solicitação POST para o servidor, tratamos dela e redirecionamos o usuário de volta para a página com o formulário. Nesta iteração, não queremos recarregar a página. Em vez disso, estamos chamando o back-end diretamente de onde vamos capturar os dados do formulário com JavaScript e, em seguida, enviar uma solicitação assíncrona ao servidor para lidar com a resposta.
Há três coisas que o WordPress AJAX precisa para funcionar – cinco delas para funcionar bem . Estes são:
- Objeto para a ação AJAX
- script JavaScript
- Ação do WordPress
- Proteção
- Manipulação de erros
Vamos dar uma olhada em cada um deles.
Objeto
Nosso objeto é a forma. Esta é a nossa coisa para lidar com JavaScript. Comecei criando um arquivo com cabeçalho necessário para o plugin WordPress e colocando um objeto vazio dentro. No final, estou criando uma nova instância da classe do plugin.
<?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();
Embora eu esteja usando um pouco de POO aqui, não vamos utilizar nenhuma prática avançada. O código funcionaria tão bem quando escrito proceduralmente com funções separadas. Mas os objetos dentro dos plugins do WordPress têm uma vantagem: você não precisa prefixar suas funções - apenas o nome da classe precisa ser exclusivo.
Vamos exibir nosso formulário para os usuários. Vamos nos conectar ao filtro the_content
e incorporar o formulário no final de cada post. No construtor da classe eu adicionei o filtro:
public function __construct() { add_filter( 'the_content', array( $this, 'report_button' ), 10, 1 ); }
E criei um método de retorno de chamada com formulário 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; }
O formulário tem toda a marcação necessária:
- Um botão para mostrar o formulário e enviar a mensagem;
-
textarea
; - Container para a resposta (vamos usar isso mais tarde).
O botão tem o atributo data-post_id
que armazena o ID da postagem atual. Vamos pegar isso no JavaScript para identificar o artigo.
Precisamos apenas de alguns estilos básicos, então vamos registrar nossa própria folha de estilo com a ação wp_enqueue_scripts
e o retorno de chamada correspondente:
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' ); }
A folha de estilo em si é muito simples. Queremos arrumar o design.
.report-a-bug-message { display: none; margin-top: 1em; } .report-a-bug-response { margin-top: 1em; }
É assim que o botão se parece:
Roteiro
Temos nosso objeto pronto para dar vida a ele. Em vez de fazer isso em JavaScript simples, vamos usar jQuery que o WordPress carrega por padrão em todas as páginas.
No método de scripts existente, adicionarei o arquivo JS usando a função 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' ) ) ); }
Também estou usando a função wp_localize_script
para passar o rótulo do botão traduzido para JS. Não temos nenhuma função gettext
em JavaScript, então temos que fazer isso aqui. Esta função criará o objeto de configurações que pode ser acessado diretamente do nosso script.
Em nosso script, vamos ouvir um clique de botão. Quando isso acontecer, mostraremos a área de textarea
e alternaremos a classe do botão para ouvir o segundo clique que está enviando o formulário.
( 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 );
Aqui está o progresso atual:
É o melhor momento para criar a solicitação AJAX!
Solicitações AJAX para WordPress
Para enviar uma solicitação AJAX, você realmente precisa de apenas um parâmetro: a URL solicitada. O WordPress tem o arquivo especial para AJAX, então não precisamos criar nosso próprio arquivo. É /wp-admin/admin-ajax.php
.
Enquanto estivermos na área wp-admin
, esta URL de arquivo está disponível em JS na variável ajaxurl
. No front-end, temos que passar essa variável por conta própria. Felizmente já usamos a função wp_localize_script
, então podemos adicionar outra chave a ela:
wp_localize_script( 'report-a-bug', 'settings', array( 'ajaxurl' => admin_url( 'admin-ajax.php' ), 'send_label' => __( 'Send report', 'reportabug' ) ) );
Preparamos todas as variáveis, então vamos criar a chamada AJAX. Vamos enviá-lo assim que o usuário clicar no botão uma segunda vez.
$( '.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' ); } ); } );
Estamos ouvindo um clique na aula que ativamos o botão antes.
Como você pode ver, estou mudando o texto do botão para “…” Por quê? Porque é uma boa prática mostrar ao usuário que algo está acontecendo. As solicitações AJAX dependem do desempenho do servidor e levarão algum tempo. Talvez 30ms, mas talvez também 3 segundos. Se depois de clicar no botão parece não haver efeito, provavelmente o botão será clicado uma segunda vez. Isso duplicaria a solicitação porque, como você sabe agora, elas são assíncronas.
Em seguida, estou criando o objeto de data
. Este contém todas as variáveis que serão enviadas para o retorno de chamada do servidor. O arquivo admin-ajax.php
WordPress requer a propriedade action. Isso deve ser exclusivo, a menos que você queira que o outro plugin lide com suas solicitações. O restante dos parâmetros é opcional. Estou enviando o ID do post e a mensagem de relatório de textarea
.
Estamos então chamando o método $.post
. Em seguida, você adivinhou; ele está enviando a solicitação POST. É um método abreviado, mas você também pode usar o método $.ajax
, que tem mais opções. Como primeiro parâmetro, temos que passar o URL do nosso arquivo manipulador, depois os parâmetros e depois a função de retorno de chamada de sucesso. Este é o lugar onde estamos lidando com a resposta. Por enquanto, estamos apenas enviando a mensagem simples para o console do navegador.
Estamos prontos para lidar com a solicitação no back-end.
Ação do WordPress
Você pode estar se perguntando como nos conectamos ao admin-ajax.php . Com ação, claro! O WordPress tem dois tipos de ação:
wp_ajax_nopriv_{$action} wp_ajax_{$action}
Onde está $action
, o nome da ação passado em parâmetros AJAX. É send_bug_report
no nosso caso.
A primeira dessas ações será executada apenas para usuários não logados. O segundo apenas para usuários logados. Portanto, se você deseja que a solicitação seja tratada para ambos, é necessário definir ambos. Isto é o que eu fiz no construtor da classe:
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' ) ); }
No retorno de chamada, estou apenas recebendo o título do post e estou enviando o 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' ) ); }
A parte mais importante em termos de manipulação de AJAX no WordPress é a última função - wp_send_json_success
que imprime a saída codificada em JSON e morre. Tudo o que precisamos para obter a resposta com o retorno de chamada de sucesso do AJAX. Ele também tem um irmão gêmeo, wp_send_json_error
, mas chegaremos a essa parte mais tarde.
O objeto dessas funções tem duas propriedades:
- sucesso
É um booleano e depende se você o chama de função de sucesso ou erro. - dados
Se você fornecer o parâmetro para a função.
Isso é tudo do lado do back-end. Vamos lidar com a resposta em JS.
Vamos remover o botão e a área de texto e exibir uma mensagem retornada do servidor no container que preparamos antes:
$.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 ); });
E é isso! Nós temos a chamada AJAX funcionando! Mas não pare por aí. Nosso pedido não é seguro e certamente não é fácil de usar. Temos que garantir que a solicitação seja executada apenas quando for necessário.
Proteção
Nonce
O nonce é um “número usado uma vez”. É um hash curto criado a partir de uma string de entrada. Podemos usá-lo para validar a solicitação — se realmente foi feita pelo WordPress e ninguém a falsifica.
Vamos usar outro atributo de dados para o botão:
$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>';
Como você pode ver, estou adicionando o ID do post à string de entrada. Teremos esse ID disponível nas propriedades da solicitação AJAX, então é mais seguro.
Agora temos que adicionar o nonce às propriedades 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() };
Agora vamos validá-lo no backend usando a função check_ajax_referer
, que o WordPress fornece:
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' ) ); }
Para validar a solicitação temos que regenerar a string de entrada, então estou usando a chave post_id
enviada do AJAX. O segundo parâmetro é a chave no $_REQUEST array
. O terceiro controla o wp_die
automatizado se o nonce não corresponder.
Eu não quero que ele morra sozinho. Em vez disso, estou pegando o resultado dessa função e enviando um erro JSON em um objeto legal.
Você também pode notar o uso da função sanitize_text_field
no parâmetro de mensagem de e-mail. Isso é apenas para garantir que o usuário não envie scripts ou HTML prejudiciais.
Por fim, precisamos envolver o retorno de chamada de sucesso do AJAX em JS na instrução if para verificar se a solicitação foi bem-sucedida:
$.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 ); } });
Proteção de botão
Você sabe que o usuário pode clicar no botão uma segunda vez, antes que o AJAX termine a chamada. Mas há um truque simples que bloqueará o segundo clique - desabilitar o botão. Então, após o clique, vou bloqueá-lo e desbloqueá-lo após obter a resposta:
$('.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); }); });
Manipulação de erros
Validação
E se o usuário tentar enviar uma mensagem vazia? Eu não quero ser incomodado com esses e-mails. Vamos bloquear essas tentativas com técnicas de validação.
Em JS vou adicionar uma validação simples, para mostrar ao usuário que algo deu errado. Se a mensagem estiver vazia, o usuário verá a borda vermelha ao redor da área de texto. Se houver uma mensagem lá, estamos restaurando a borda neutra:
$('.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 });
Verificando erros na ação do WordPress
Também podemos ter um problema com o envio do e-mail. Se não verificarmos o resultado da função wp_mail
, o usuário receberá uma mensagem de sucesso mesmo que nenhum e-mail tenha sido enviado.
Vamos lidar com isso:
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(); } }
Como você vê, usamos a função wp_send_json_error
duas vezes, mas não há necessidade de exibir mensagens exclusivas para os usuários. Em vez disso, passando a descrição exata do erro, adicionarei outra chave ao nosso objeto de configurações de script que cobrirá os dois erros:
// 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' ) ) );
Tudo o que resta a fazer é exibir o erro para o usuário:
$.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 ); } );
Exemplo Completo
Nós fizemos isso! Nossa solicitação AJAX foi feita; é bem seguro e amigável. Isto é o que um usuário verá no caso de algum erro:
Abaixo você pode baixar o plugin completo e conferir os guias do usuário:
- Baixe o plugin no Github
- Baixe o plugin no Wordpress.org
- Vá para os guias do usuário
Espero que isso lhe dê uma boa base para suas próprias soluções “sem atualização de página”. Você pode fazer isso em todos os formulários do seu site. Também é uma ótima maneira de otimizar qualquer parte pesada do site que não precise ser carregada imediatamente, como uma grande lista de produtos em um menu suspenso. Você pode carregá-los via AJAX logo após clicar no menu suspenso - é simples assim .
As possibilidades são quase ilimitadas. Me conta nos comentários quais são suas ideias!