Have you seen a new section gets loaded without a page refresh?
This is using a JavaScript technique called AJAX where the client can call API to retrieve data and append it to the current page.
Let’s learn by creating a “LOAD MORE” button to show the next set of posts.
Important: This tutorial is only intended to teach you about AJAX and not a good way to create a Load More button. It’s far better if you create a custom API endpoint and re-use the existing template-parts
from your theme to render.
Lesson #1: Choosing a Library
Although it’s possible to do without any library, it’s easier with one like React, Handlebars, or Template7.
React is the library used in Gutenberg editor. It is powerful yet hard to learn and definitely overkill for a Load More button.
On the other hand, Handlebars and Template7 are very simple libraries. They have nearly identical syntaxes but the latter is 4x smaller in size (50KB vs 12KB).
So we decided to use Template7 for this tutorial. Download it here or use the CDN link at:
https://cdnjs.cloudflare.com/ajax/libs/template7/1.4.1/template7.min.js
Lesson #2: Create & Render a Template
The template is written inside <template>
tag:
<template id="template-name">
<p> Hello, my name is {{ firstName }} {{ lastName }} </p>
</template>
If you’re wondering, that’s a legit HTML tag specifically used to hold content that will be rendered with JavaScript.
You can render the template by passing in an object like this:
let rawTemplate = document.querySelector('#template-name').innerHTML;
let template = Template7.compile( rawTemplate );
let html = template({
firstName: 'John',
lastName: 'Doe'
});
// the `html` variable contains the rendered template
// you can use it to insert anywhere you want
Here’s a CodePen showcasing the snippets above:
See the Pen Template 7 Basic Showcase by hrsetyono (@hrsetyono) on CodePen.
Lesson #3: Conditional and Loop
CONDITIONAL
The conditional in Template7 is limited to checking true or false like below:
<h4>{{ firstName }} {{ lastName }}</h4>
<p>{{ bio }}</p>
{{#if showEmail }}
<p>Email: {{ email }}</p>
{{else }}
<p>Email is hidden</p>
{{/if }}
See the Pen Template 7 – Basic Showcase 02 by hrsetyono (@hrsetyono) on CodePen.
LOOP
Let’s say we have this posts data:
[
{
title: 'Hello World',
excerpt: 'Lorem ipsum dolor sit amet consectetur, adipisicing elit. Soluta debitis quisquam, ullam officiis.',
link: 'https://yoursite.com/hello-world'
},
{
title: 'Second Post',
excerpt: 'Voluptatem obcaecati corrupti eius culpa, placeat rem eligendi deserunt earum ut possimus dolores',
link: 'https://yoursite.com/second-post'
},
]
Here’s the template to loop them:
{{#each this }}
<li>
<h2> <a href="{{ link }}">
{{ title }}
</a> </h2>
<p>{{ excerpt }}</p>
</li>
{{else }}
<li>There's no post</li>
{{/each }}
See the Pen Template 7 – Basic Showcase 03 by hrsetyono (@hrsetyono) on CodePen.
this
refers to the current variable. Since we passed in an array, we don’t have a name to refer the variable, so we use this
.
Lesson #4: API Helper
The native API function (called fetch
) is quite wordy. So it will make your life easier to create these helper functions as a shortcut:
const myAPI = {
get( endpoint ) {
return window.fetch( endpoint, {
method: 'GET',
headers: { 'Accept': 'application/json' }
} )
.then( this._handleError )
.then( this._handleContentType )
.catch( this._throwError );
},
post( endpoint, body ) {
return window.fetch( endpoint, {
method: 'POST',
headers: { 'content-type': 'application/json' },
body: JSON.stringify( body ),
} )
.then( this._handleError )
.then( this._handleContentType )
.catch( this._throwError );
},
_handleError( err ) {
return err.ok ? err : Promise.reject( err.statusText )
},
_handleContentType( res ) {
const contentType = res.headers.get( 'content-type' );
if( contentType && contentType.includes( 'application/json' ) ) {
return res.json()
}
return Promise.reject( 'Oops, we haven\'t got JSON!' )
},
_throwError( err ) {
throw new Error( err );
}
}
How to use it:
// GET request
myAPI.get( url ).then( result => {
// do something
} );
// POST request
myAPI.post( url, data ).then( result => {
// do something
} );
Lesson #5: Calling for API
Let’s try calling the Posts API of WPTips website and display it. By combining what we have learned, this is quite simple to do:
See the Pen Template 7 – Lesson 05 – API Call by hrsetyono (@hrsetyono) on CodePen.
Lastly, we created a Load More button with click listener and added pagination parameter to the API URL like shown below:
See the Pen Template 7 – Lesson 05b – API Call with Load More by hrsetyono (@hrsetyono) on CodePen.
Lesson #6: Using it in WordPress
Enqueue your custom JS and Template7:
add_action( 'wp_enqueue_scripts', function() {
$js_dir = get_template_directory_uri() . '/js';
wp_enqueue_script( 'my-template', $js_dir . '/my-template.js', [], '', true );
wp_enqueue_script( 'template7', 'https://cdnjs.cloudflare.com/ajax/libs/template7/1.4.1/template7.min.js', [], '', true );
} );
Find a way to output the <template>
tag. For example, we can echo it in footer:
add_action( 'wp_footer', function() { ?>
<template id="template-posts">
{{#each this }}
<li>
<h3> <a href="{{ link }}">
{{ title.rendered }}
</a> </h3>
<p>{{ excerpt.rendered }}</p>
</li>
{{else }}
<p>There's no post</p>
{{/each }}
</template>
<?php } );
Conclusion
You have learned how to do API calls with JavaScript and rendering it using the infamous “Mustache” syntax (because the curly brace looks like a mustache).
That should bring you up to speed with the basic of AJAX.
If you are interested to learn more, the first thing you should read next is Template7 official docs.
Thank you for reading. If you have any question, feel free to post it in the comment below 🙂