Last active
November 13, 2022 01:58
-
-
Save mrbobbybryant/379df35b2eec710eef86234bbdae6958 to your computer and use it in GitHub Desktop.
Example WordPress Rest API pagination abstraction
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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