Dev Notes

Software Development Resources by David Egan.

WordPress Basic Ajax


Ajax, WordPress
David Egan

The WordPress way of handling AJAX requests is to use a specially constructed hook name built from the ‘action’ property of your AJAX request prepended with ‘wp_ajax_’ or ‘wp_ajax_nopriv_’.

In a namespaced setup (like Roots\Sage) you’d set up hooks and callbacks like this:

<?php
add_action( 'wp_ajax_my_custom_action', __NAMESPACE__ . '\\ajax_processor' );
add_action( 'wp_ajax_nopriv_my_custom_action', __NAMESPACE__ . '\\ajax_processor' );

function ajax_processor() {

  // Validate incoming POST data before further processing - return errors if necessary
  $currency = $_POST['currency'];

  // Do stuff, return true on success
  $processed_data = data_processor($currency);

  if( true === $processed_data ) {

    // Success condition: send back stuff to alter in the browser
    $response = [];
    $response['status'] = 'success!!';
    $response['postedData'] = $_POST;
    $response['currencySelected'] = $currency;
    $response['price'] = $price;

  } else {

    // Error condition: Build an informative error message
    $response = ['error'] = 'foo';

  }

  // send $response back as a JSON object
  return wp_send_json( $response );

}

In this case, when you send an AJAX request to WordPress with a request ‘action’ property set to ‘my_custom_action’ the hook callback will be automatically executed.

If you’re using the ‘nopriv’ option (for users who are not logged in) your ajaxurl property won’t be automatically defined, so you need to include it manually or pass it in via wp_localize_script() within the enqueuing script. You should enqueue the javascript in any case.

Enqueue JavaScript

In the context of Sage, create a JavaScript file in /assets/js. You then need to specify that this file should be built in with your front end processor. In Sage, you do this by providing the relevant info in manifest.json. This instructs Gulp (Sage 8.x) to process your JS file and build it into the dist directory - where it can be accessed.

Simplified manifest.json, adding test.js to our build directory:

{
  "dependencies": {
    "main.js": {
      "files": [
        "scripts/main.js"
      ],
      "main": true
    },
    "test.js": {
      "files": [ "scripts/test.js" ]
    },
    "main.css": {
      "files": [
        "styles/main.scss"
      ],
      "main": true
    },
  "config": {
    "devUrl": "http://localhost/sage-test"
  }
}

Next up, enqueue the script. Sage provides a useful helper function to access the file path: Roots\Sage\Assets\asset_path(). Depending on the location of your enqueuing script, you may need to import the namespace.

Enqueuing from /lib/extras.php might look like this:

<?php
use Roots\Sage\Assets;

add_action('wp_enqueue_scripts', function () {

  wp_register_script( 'your_custom_handle', Assets\asset_path('scripts/test.js'), ['jquery'], null, false );
  wp_enqueue_script( 'your_custom_handle' );
  wp_localize_script( 'your_custom_handle', 'yourVars', [
    'yourAjaxURL' => admin_url( 'admin-ajax.php' )
    ]
  );
}, 100);

The JavaScript

A simple jQuery AJAX handler might look like this:

jQuery(document).ready(function($) {

  var url = yourVars.yourAjaxURL;

  $('#currency').change( function() {

    var data = {
      'action': 'my_custom_action',
      'currency': $('#currency').val()
    };

    $.post(url, data, function(response) {

      console.log(response);
      alert("Currency Selected: " + response.currencySelected);

    });

  });

});

This is designed to take and submit data from an on-page form that might look like this:

<form method="post" id="currencyselector" name="currencyselector" action="">
  <select name="currency" id="currency">
    <option value = "USD">USD</option>
    <option value = "GBP">GBP</option>
    <option value = "EUR">EUR</option>
  </select>
</form>

Summary

This example is just a stripped down version - mainly it’s a memory jogger for the hook name setup. I would generally build this functionality in an Object Oriented context - so the callbacks would be held within class methods rather than the functions shown here.


comments powered by Disqus