Problems implementing Load More functionality

Background

As an overly broad comparison of the legacy AJAX handler system and the REST API:

  • The AJAX handler system is little more than a way to connect a request to specific PHP file to a WordPress hook callback, which leaves virtually all other implementation details up to the developer.
  • The REST API is more like a “HTTP API framework” which provides
    a variety of systems, tools, and conventions to aide in exposing data
    and functionality at logical URIs in an organized fashion.

The REST API also comes complete with all of the necessary functionality to expose most WordPress data and all general interactions with it automatically. Out of the box, you can Create/Read/Update/Delete WordPress users, pages, posts, taxonomies, etc. through standardized HTTP requests to conventional endpoints.

As you’ve already seen, the process of exposing the core data for a Custom Post Type on the REST API can be as simple as flipping a switch in the CPT’s register_post_type() call:

register_post_type(
  'radgivare',
  [
    // ...
    'show_in_rest' => true
  ]
);

After which the post type is exposed through the REST API at the route

/wp-json/wp/v2/radgivare

And specific posts of that type through the parameterized route

/wp-json/wp/v2/radgivare/<id>

No need to write all the logic to query for, create, update, or delete your CPT through an HTTP request like you would need to if you wanted to perform these operations using the legacy AJAX handler system – the REST API has taken care of the basics for you, which is pretty neat 🙂

The type of interaction with the data exposed at a route is determined by the HTTP Method of the request. A POST request is used to update or insert. A DELETE request will delete. A GET request will retrieve/query for data – which also means that visiting a route directly in your browser will execute this sort of operation (I recommend using a JSON-prettifying browser extension when exploring REST API routes in a browser so you don’t loose your mind).

This all just scratches the surface of the REST API – I highly recommend spending some time reading the REST API Handbook to become more acquainted with it’s concepts and systems. It’s also a handy reference for the core endpoints, and which parameters they accept and the format of the data they return.

The core REST API routes are also self-documenting – this means that detailed descriptions of each of the core routes are available as data in a response. Primarily through the GET /wp-json/wp/v2 route. The Handbook is a lot easier to look at, but sometimes you can find a reference for fields and functionality which has been added by plugins in the response.


Solution

There are many ways in which could deliver a post’s content as rendered through a template part. One solution would be to add a field to the the REST API’s response for a GET request for your CPT which will contain the template-rendered markup.

Because get_template_part() simply executes the referenced PHP file, the markup in that file will be sent to output (e.g. echo‘d) instead of returned – it’s not possible to acquire the markup as a string via something like $out = get_template_part(...);. Instead, we need to use PHP “output buffers” to capture the output, then dump it to a string.

function wpse408606_rest_radgivare_templated_content_field() {
  // Add a new field to responses to requests for `radgivare` posts, which
  // will be populated with the return value from the `get_callback`.
  register_rest_field(
    'radgivare',
    'templated_content',
    [
      'get_callback' => 'wpse408606_get_radgivare_templated_content',
    ]
  );
}

add_action( 'rest_api_init', 'wpse408606_rest_radgivare_templated_content_field' );

function wpse408606_get_radgivare_templated_content() {
  ob_start(); // Create a new, empty buffer to capture any proceeding output.

  // The $post object is already set up as though we're in The Loop.
  // Load the template.
  get_template_part( 'template-parts/content', 'page' );

  return ob_get_clean(); // Close the output buffer, and dump it's contents as a string.
}

That’s all that’s necessary on the PHP side. A new field named templated_content will now be available in the responses to requests for radgivare posts, containing the output of that template part.

We don’t need add any code to handle which page or how many posts to return, because the default REST controller for posts already facilitates these parameters.

Your jQuery code becomes like

jQuery("#btn-load-more").on("click", function ($) { // When btn is pressed.
    jQuery("#btn-load-more").attr("disabled", true); // Disable the button, temp.
    pageNumber++;

    var rest_route = `${load_ajax_data.restURL}wp/v2/radgivare?page=${pageNumber}&per_page=${ppp}`;

    jQuery.ajax({
        type: "GET",
        dataType: "json",
        url: rest_route,
        data: str,
        success: function (data) {
            for( var i = 0; i < data.length; i++ ) {
                $post_content = jQuery( data[i].templated_content );
                jQuery("#load-more-posts-container").append($ post_content );
            };
           
            if (total < pageNumber) {
                jQuery("#btn-load-more").hide();
            }
            else {
                jQuery("#btn-load-more").attr("disabled", false);
            }
        },
        error: function (jqXHR, textStatus, errorThrown) {
            $loader.html(jqXHR + " :: " + textStatus + " :: " + errorThrown);
        }

    });
    return false;
});

There are many ways in which this could be improved, and it likely still needs to be adapted for your specific use-case, but I hope it’s enough to get you started!