Skip to content

Instantly share code, notes, and snippets.

@mrbobbybryant
Last active November 13, 2022 01:58
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mrbobbybryant/379df35b2eec710eef86234bbdae6958 to your computer and use it in GitHub Desktop.
Save mrbobbybryant/379df35b2eec710eef86234bbdae6958 to your computer and use it in GitHub Desktop.
Example WordPress Rest API pagination abstraction
import queryString from 'query-string';
import { getSpinner } from './components';
/**
* Implementation Example at the very bottom.
*/
export default function( settings ) {
let page = 2;
let buttonContent;
let headers;
const query = queryString.stringify( Object.assign( {}, {page: page}, settings.query ) );
const endpoint = `${window.location.protocol}//${window.location.host}/wp-json/wp/v2/${settings.resource}?${query}`;
if ( ! settings.buttonEl || ! settings.containerEl ) {
return;
}
settings.buttonEl.addEventListener( 'click', () => {
/**
* Conditionally show spinner and store original innerHTML for later.
*/
if ( settings.showSpinner ) {
buttonContent = settings.buttonEl.innerHTML;
settings.buttonEl.innerHTML = getSpinner();
}
fetch( endpoint )
.then( ( response ) => {
/**
* Conditionally remove spinner and put button content back.
*/
if ( settings.showSpinner ) {
settings.buttonEl.innerHTML = buttonContent;
}
/**
* Store response headers so we can see if more posts exists.
*/
headers = parseInt( response.headers.get('X-WP-TotalPages') );
/**
* If we have a good response, then parse it's json.
*/
if ( response.ok ) {
return response.json();
}
})
.then( (json) => {
if ( json.length ) {
/**
* Loop over each post in the response, and call the callback
* function on each element.
*/
json.forEach( ( post ) => {
const itemHTML = settings.callback( post );
/**
* If the callback result is a string, then we need to convert it to
* an HTMLElement so that we can call appendChild on the parent wrapper.
*/
if ( 'string' === typeof itemHTML ) {
let frag = document.createRange().createContextualFragment(itemHTML);
settings.containerEl.appendChild(frag);
}
/**
* If the the callback result is an HTMLElement then we simply append it to
* the parent wrapper.
*/
if ( 1 === itemHTML.nodeType ) {
settings.containerEl.appendChild(itemHTML);
}
});
/**
* We need to check the headers we stored earlier to see if more posts
* exist. If not then we can hide the load more button since there
* are no more posts to load.
*/
if ( page === headers ) {
settings.buttonEl.style.display = 'none';
}
/**
* Increament the page count so that we continue to fetch the next page
* of results from the Rest API.
*/
page++;
}
} );
});
}
/**
* Template String Callback Example used below.
*/
export const videoThumbnail = ( post ) => {
const imageURL = `${post.related_question.link}?answer=${post.id}`;
return `
<a href="${imageURL}">
<div class="people-answers-item">
<img src="${post.related_contact.image}" alt="">
<div class="people-answers-item-overlay">
<h3>${post.title.rendered}</h3>
</div>
</div>
</a>
`;
}
/**
* Clone node component example. Not sure why, but I love stealing an existing element
* and using it as a sudo template.
*/
export const videoClone = ( post ) => {
const imageURL = `${post.related_question.link}?answer=${post.id}`;
const wrapper = document.querySelector( '.people-answers-wrapper' );
const el = wrapper.firstElementChild;
const newEl = el.cloneNode(true);
const img = newEl.querySelector( 'img' );
const title = newEl.querySelector( 'h3' );
newEl.setAttribute( 'href', imageURL );
img.setAttribute( 'src', post.related_contact.image );
title.innerText = post.title.rendered;
return newEl;
}
/**
* Copy of spinner button per import statement at the top.
*/
export const getSpinner = () => {
return `
<svg version="1.1"
id="svg-spinner"
x="0px"
y="0px"
viewBox="0 0 80 80"
xml:space="preserve">
<path
id="spinner"
d="M40,72C22.4,72,8,57.6,8,40C8,22.4,
22.4,8,40,8c17.6,0,32,14.4,32,32c0,1.1-0.9,2-2,2
s-2-0.9-2-2c0-15.4-12.6-28-28-28S12,24.6,12,40s12.6,
28,28,28c1.1,0,2,0.9,2,2S41.1,72,40,72z">
<animateTransform
attributeType="xml"
attributeName="transform"
type="rotate"
from="0 40 40"
to="360 40 40"
dur="0.8s"
repeatCount="indefinite"
/>
</path>
</svg>
`;
}
/**
* Load More: Archive Page Example
*/
const archivePage = document.querySelector( '.archive' );
const category = ( archivePage ) ? archivePage.querySelector( '.app').dataset.category : false;
loadMore({
buttonEl: document.querySelector('.js-load-more'), //Button DOM Element
containerEl: document.querySelector('.people-answers-wrapper'), //Container Element where new post will be appended
resource: 'answers', //Post Type to fetch
query: {
per_page: 20, // Accepts any WP Rest API query args
categories: category
},
callback: videoClone, // Your Template function. See two examples above.
showSpinner: true // Do you want to show a loading spinner?
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment