Confused on AJAX submit form through page template

<form action="<?php echo get_template_directory_uri() . "/validation.php"; ?>" id="contactForm">

😱

So I’ll outline the basic fundamentals, so you have a framework to move forwards with

Fixing Admin AJAX

So I’ll cover this very briefly as it’s not the crux of my answer, but would be useful:

  • Add a hidden input field named action rather than adding it to the URL
  • validation.php and any other standalone files should burn with the fire of a thousand suns, do this validation in JS, and again in the form handler. Handling the form and validating it are the same step, else someone could skip validation and go straight to handling

Finally, use a standard form handler for when JS isn’t used/possible, check for the existence of something in the form that would only happen if it’s been submitted, and submit the form to the same page, by having an empty action attribute e.g.:

if ( !empty( $_POST['form_page'] ) ) {
    $valid = false;

    // do validation
    // 
    if ( true === $valid ) {
        // yay do whatever submitting the form is meant to do
        // if it's a multipage form, handle that here
    } else {
        get_template_part( 'form' ); // show the form again, but with warnings for validation
    }
} else {
    get_template_part( 'form' );
}

RESTful Form Submissions

Change your form submission jQuery to use a REST endpoint, lets call this darthvader/v1/contact, I used some code from a stackoverflow answer here:

jQuery('input#submitButton').click( function() {
    jQuery.ajax({
        url: '/wp-json/darthvader/v1/contact',
        type: 'post',
        dataType: 'json',
        data: jQuery('form#contactForm').serialize(),
        success: function(data) {
            //... do something with the data...
        }
    });
});

That’s pretty much every form submission via REST API you’ll ever need, but you still need that endpoint to exist, ‘/wp-json/darthvader/v1/form’ needs creating, so lets tell WP we want an endpoint, that accepts POST’s:

add_action( 'rest_api_init', function () {
        register_rest_route( 'darthvader/v1', '/contact/', array(
                'methods' => 'POST',
                'callback' => 'darth_contact_form'
        ) );
} );

Then define what will happen when it’s called:

function darth_contact_form( \WP_REST_Request $request ) {
    //
}

That’s where we’ll handle our form, e.g.:

function darth_contact_form( \WP_REST_Request $request ) {
    $title = $request['title'];
    $message = $request['message'];
    // send an email or create a post, or whatever
    // the contact form does when its submitted
    return "Your contact request had the title ".$title;
}

The return value gets turned into JSON and sent back to the browser, so that you can handle it in the success function in javascript that you saw earlier in this answer. You can also return other responses, e.g.:

return \WP_REST_Response( "Bad request, X Y and Z are empty", 400);

and you can return a WP_Error object to indicate something going wrong or incorrect:

return new WP_Error( 'awesome_no_author', 'Invalid author', array( 'status' => 404 ) );

You can do your validation inside the endpoint, but you can also make the endpoint do the validation for you by specifying what fields to expect, and a function to validate them, but I leave that as an exercise ( or new question ) for you