Using wp.ajax for Async Requests in WordPress

Over the years WordPress has provided a myriad of tools to make ajax requests in WordPress a little easier. After the new media modal was introduced in WordPress 3.5, a new set of tools went in to WordPress core. The javascript object wp was extended with many things, including wp.ajax. This combined with the wp_send_json_success() and wp_send_json_error() helper functions can help make performing ajax requests with the admin-ajax.php processor very straight forward.

If you are not already familiar with the admin-ajax.php processor, it’s worth learning a little more about. It is not and should not be the only way you work with Ajax in WordPress, but it’s a very useful tool to have at your disposal and should not be overlooked.

Let’s start by defining what wp.ajax is and does. It contains two methods currently, wp.ajax.post() and wp.ajax.send(). I’m going to focus this article on wp.ajax.send() because wp.ajax.post() is really just a wrapper around the send method that ensures the request is sent as a POST request. Beyond the request type, both .send and .post work almost the same.

wp.ajax.send( action[, options])

File Location: //wp-includes/js/wp-utils.js:36-104
Registered Slug: wp-util

action
Type: String
The action corresponds with the action callback fired in the admin-ajax.php processor. This will be automatically be added to the data sent to the server. Alternately, you can omit the action string and place it in the data object of the options object and send the options object as the first parameter of this method.

options
Type: Object
The options object defines the data sent to the server, as well as the success and error callbacks. It will accept any options that jQuery.ajax() accepts along with a few others it handles itself.

  • context: (object) The context in which to call both the success and error callbacks.
  • data: (object) The data sent to the server. If options is sent as the first parameter of the call, the action must be included here.
  • error: (function) The callback triggered for responses sent with wp_send_json_error()
  • success: (function) The callback triggered for responses sent with wp_send_json_success()
  • type: (string) The type of request to make (e.g. POST, GET ). Default: POST
  • url: (string) The URL that the ajax request is sent to. Default: admin-ajax.php

Return
Type: jQuery.promise
The promise object allows you to attach additional callbacks to the request. All callbacks will be called with the correct context if context was sent as an option.

Additional Notes

By enqueuing this helper, a reference to the admin-ajax.php processor is automatically included in the page. There is no need to additionally include it anywhere through a script tag or through localizing a script.

This method unwraps the wp_send_json_success() and wp_send_json_error() methods calling the success and error callbacks based on which wp_send_json method was used. It will supply the data sent with wp_send_json so there is no need to look for a data parameter before processing the response.

Example

In this example, we are going to set up comment submissions using ajax. First in PHP, we need to enqueue a script to run on singular pages with comments. We’ll define wp-util as a dependency of our script so that it gets automatically enqueued and is available. Notice we don’t have to specify jQuery as a dependency because it’s already listed as a dependency for wp-util, and will get enqueued automatically.

// PHP
function lw_comment_fun_init() {
  if ( ! is_singular() || ! comments_open() ) {
    return;
  }
  wp_enqueue_script(
    "lw-comments",
    get_stylesheet_directory_uri() . "/assets/js/comments.js",
    array( "wp-util" ),
    "1.0.0",
    true //makes sure this is enqueued in the footer
  );
  // Print out a nonce so we can verify this request.
  wp_localize_script( "lw-comments", "lwCommentFormNonce", wp_create_nonce( "ls-comment-fun!" ) );
}
// Run on the wp action so we can check is_singular
add_action( "wp", "lw_comment_fun_init" );
add_filter( "pre_option_thread_comments", "__return_zero" );

Now our “comments.js” file will be enqueued along with the “wp-util” library that contains the wp.ajax helpers. We’re also going to remove the complexity of dealing the threaded comments by filtering the threaded comments option and always turning it off.

Before we take a look at the javascript, let’s take a quick look at the comments template to get an idea of the markup we’re working with.

<!-- HTML -->
<form action="http://lkwdwrd.com/wp-comments-post.php" method="post" id="commentform" class="comment-form">

  <input id="author" name="author" type="text" placeholder="Name" value="" size="30">
  <label for="author">Author</label>

  <input id="email" name="email" type="text" placeholder="Email" value="" size="30">
  <label for="email">Email</label>

  <input id="url" name="url" type="text" placeholder="Website" value="" size="30">
  <label for="url">Website</label>

  <textarea id="comment" name="comment" cols="45" rows="6" required=""></textarea>

  <input name="submit" type="submit" id="submit" value="Post Comment">

  <input type="hidden" name="comment_post_ID" value="54" id="comment_post_ID">
  <input type="hidden" name="comment_parent" id="comment_parent" value="0">

</form>

This is a simplified set of the HTML that typically makes up a comment form, but it gives us some context to work with. This will work as is to send in comments, but rather than actually posting the form, let’s send it back using some javascript and ajax.

Before diving in with speicifics, let’s take a look at a stubbed out version of the javascript file to get a 1000 foot view of the process.

// Javascript
(function(){
  var wp = window.wp,
      $commentForm = $( "#commentform" ),
      $commentWrap = $( ".comments .commentlist" ),
      $error = false, $comment = false,
      nonce = window.lwCommentFormNonce; //passed from PHP

  function commentSuccess( data ) {}
  function commentError( data ) {}
  function submitComment( event ) {}

  // Take over submissions.
  $commentForm.on( "submit", submitComment );
})( jQuery );

Basically, we’re going to define some variables and cache references to DOM objects with jQuery. We’re also grabbing a copy of the nonce we sent ourselves. Then we’re defining three functions, two will be used as ajax callbacks, one as an event handler for form submissions. Then finally we are binding our event handler to the form submit event.

Let’s think about what we want the form submission handler to do. First, we need to stop the form from submitting normally, that way the page doesn’t refresh. Then we need to get all the data currently in the form. We need to let the user know that something is happening. Finally we need to send the form to the server for processing. Let’s see what that looks like in code.

function submitComment( event ) {
  // Stop the form from submitting normally
  event.preventDefault();

  // Do not process if we are currently processing a comment
  if ( $comment !== false ) {
    return;
  }

  // Go get the form data as an object.
  // This is a custom written helper method.
  var formData = _serialToObject( $commentForm.serializeArray() );

  // Create a list item so the user sees something is happening.
  $comment = $( "<li />" ).text( "Processing Comment..." );
  $commentWrap.append( $comment );

  // Send the form via ajax
  wp.ajax.send( "lw_submit_comment", {
    success: commentSuccess,
    error:   commentError,
    data: {
      nonce: nonce,
      comment: formData
    }
  });
}

Alright! We’ve accomplished all of the goals we had for that method. Additionally we added a lock ( $comment !== false ) so the same form doesn’t get processed twice if we’re already working on it. Looking at the wp.ajax.send method we can see we’re sending lw_submit_comment as our action. As extra data, we’re sending our nonce and the comment form data we collected.

When this request makes it back to the admin-ajax.php processor it’s going to fire either the wp_ajax_lw_submit_comment action or the wp_ajax_nopriv_submit_comment action. Since we want both logged in users and non-logged-in users to be able to submit the form, we’ll add our PHP callback to both actions. Let’s take a look at the handler we’ll use to do this processing in PHP.

// PHP
function lw_ajax_comment() {
  // Make sure we have got the data we are expecting.
  $nonce = isset( $_POST["nonce"] ) ? $_POST["nonce"] : "";
  $comment = isset( $_POST["comment"] ) ? $_POST["comment"] : array();

  // Make sure the nonce is valid and we have comment data.
  if( wp_verify_nonce( $nonce, "ls-comment-fun!" ) && ! empty( $comment ) ) {
    //Transform the comment into the correct format.
    $comment_data = lw_process_comment_data( $comment );
    // Verify we have usable comment data.
    if ( ! is_array( $comment_data ) ) {
      wp_send_json_error( $comment_data );
    } else {
      // Submit the new comment to WordPress
      $comment_id = wp_new_comment( $comment_data );
    }
  } else {
    // If the nonce was invalid or the comment was empty, send an error.
    wp_send_json_error( "This came from the wrong place" );
  }

  // Make the comment ready for front end display
  $comment_obj = lw_prepare_comment_for_js( $comment_id );
  // Send the comment data back to Javascript.
  wp_send_json_success( $comment_obj );
}
// add our callback to both ajax actions.
add_action( "wp_ajax_lw_submit_comment", "lw_ajax_comment" );
add_action( "wp_ajax_nopriv_lw_submit_comment", "lw_ajax_comment" );

A couple things to note. First, there are two custom helpers in here, the first is lw_process_comment_data( $comment );. This helper takes the raw comment form data, verifies and validates it, then transforms it into an array that will be accepted by wp_new_comment. Most of this helper is derived from the normal WordPress comment processor wp-comments-post.php. The second helper, lw_prepare_comment_for_js, does what it says. The raw comment object is missing some bits of data needed to display it on the front end, so we’ll go ahead and process that here in PHP add it to the object before sending it back to Javascript for display.

The next step is going back to Javascript and looking at both the commentSuccess() and commentError() methods. The success method here is only going to be called if we successfully inserted the new comment in PHP. So all we have to do is accept the returned data and display it in the DOM. Similarly, the error handler will get the string errors we sent back if things fail, so we can expect that and process them accordingly. If we had been using a normal jQuery.ajax call, we’d have to process both the PHP error and the successful submission in the success callback.

Take a look at the finished product (video). It works well.

When to use this

Our comment submission is not completely finished yet, but we’ll hold off on that for now. We’ve got the ajax working—Javascript to the server back to Javascript—and that is what we were most interested in doing.

wp.ajax certainly takes some of the work out of making requests to the admin-ajax.php processor, but you might not always want to reach for it when writing ajax. By using it, you are enqueueing jQuery, Underscore, and the wp-util library. While I do still recommend using jQuery to send ajax requests, you might not want to drag in Underscore and the wp-util library if you’re only making a couple of requests.

Performance is very important and http requests can slow down the page. If, however, you are making a lot of ajax requests and using the other parts of the wp-util file as well, then it can speed up your Javascript authoring considerably.

Also, if you are working with ajax on any of the post editing screens, these libraries are already present, so go ahead and make use of them as needed. The main point is to be thoughtful about what the code you are writing is actually doing. If you’ve got a good use case for this helper, drop it in the comments so we can all get an idea of where this might be helpful.

Now, go make cool stuff!

6 Comments

  1. I’m slightly confused how wp_send_json_error() and wp_send_json_success() are handled.

    You say:

    error: (function) The callback triggered for responses sent with wp_send_json_error()
    success: (function) The callback triggered for responses sent with wp_send_json_success()

    But you also say:

    “If we had been using a normal jQuery.ajax call, we’d have to process both the PHP error and the successful submission in the success callback.”

    What is “normal jQuery.ajax call”?

    • Hey Janek, Thanks for stopping by! Sorry for the slightly delayed response.

      I suppose that is a bit unclear. When I am talking about a “normal jQuery.ajax() call” I am referring to using the jQuery ajax helper. wp.ajax() is a wrapper around the jquery function that makes it, arguably, more useful.

      With jQuery alone, it runs the success callback function on a successful request. This can be any response regardless of which wp_send_json_ method you use. The trigger is the server responding with a “200: OK” response code. With wp.ajax(), it looks at the response object and reads which wp_send_json_ method was used and will invoke the success and error callbacks accordingly.

  2. thx for article

  3. Can you provide the complete files as a download?

    Thanks.

  4. Nice Article. But will async speedup the admin panel?

  5. Great article Luke! wp.ajax is a must when it comes to doing ajax requests while in the admin. It’s very convenient and removes the need to localize just to pass on the admin-ajax.php url.

    One important thing when using wp.ajax, your action handlers SHOULD use wp_send_json_error and wp_send_json_error instead of doing a traditional die() or else all the results would look like a returned error.

    As you also pointed out, wp-util & Underscore are required for wp.ajax, so I would recommend using the old jQuery.ajax method for when doing ajax in the frontend.

Leave a Reply

Your email address will not be published.

*

© 2017 Luke on Everything

Theme by Anders NorénUp ↑