Skip to content

Instantly share code, notes, and snippets.

@rogerlos
Last active October 20, 2022 12:21
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save rogerlos/7502541070942e16a1188dd0bb9ac2b9 to your computer and use it in GitHub Desktop.
Save rogerlos/7502541070942e16a1188dd0bb9ac2b9 to your computer and use it in GitHub Desktop.
Allows Gutenberg SelectControl to have its options dynamically populated.
/*
* This is an abbreviated "ES5" script (though it has some ES6 language, just not JSX, or dependencies on npm)
* demonstrating how to get the WordPress "Gutenberg" SelectControl to allow dynamic options.
*
* It probably isn't elegant, but neither am I...I included more context than may be necessary.
*
* (SelectControl pukes on dynamic options because it completely dies, never to be seen again, if it
* is sent an empty options array. This prevents that with a placeholder until your results come back.)
*/
( function ( BLOCK, ED, COMPS, EL, FETCH ) {
const { registerBlockType } = BLOCK;
const { SelectControl } = COMPS;
const { InspectorControls } = ED;
/* Custom route in the rest API; it returns all posts, to be filtered up here. */
const ROUTES = { posts : '/myblocks/v1/all-posts' };
/* In an elegant app, these would be passed to the server, DRY and all that. */
const TYPES = [ 'post', 'page' ];
/* My default empty option */
const DY = { key : 'postnone', label : 'Select a Post', value : 0 };
/* The dynamic store. It needs to be an object so a property can be altered. */
const DYNAMIC = { posts: [] };
/* My 'register block' config array, eliminated for brevity. */
const CFG = { /* sanity */ };
/* Calling 'post_selectors' starts the "fetch"...it might be done by the time someone starts poking at the
inspector; but if not, no worries: it will update the select on the 'focus' after info is returned. */
post_selectors();
/* Register the block */
registerBlockType( 'pulling/teeth', {
...CFG,
edit : function ( props ) {
return EL( 'div', {},
EL( InspectorControls, {},
poster( props ) // <-- returns the select control with dynamic options
)
)
},
save : function () { /* ... */ }
} );
/* Now you can have dynamic options fetched from whereever...
*
* 'poster' runs every time the block is focused, so it checks our const 'DYNAMIC' and
* eventually substitutes fetched results for the placeholder. Remember, 'DY' is the generic
* "Select a Post" option object.
*
* In my fiddling around, the posts property immediately became undefined rather than
* the empty array it was initialized as once the fetch started...hence the test as shown. */
function poster( props ) {
return EL(
SelectControl,
{
key : CFG.itemPost.key,
label : CFG.itemPost.label,
help : CFG.itemPost.help,
options : typeof( DYNAMIC.posts ) === 'undefined' ? [ DY ] : DYNAMIC.posts,
value : props.attributes.itemPost,
onChange : function ( val ) {
props.setAttributes( props.attributes.itemPost = val )
}
}
)
}
/* FETCH
* I found this chain of three functions was needed, I suspect because I don't quite get promises.
*
* 'get_posts' fetches the posts...filtering them by types allowed via 'TYPES'. */
function get_posts() {
let myTypes = [];
return new Promise(
function ( resolve ) {
FETCH( { path : '/wp/v2/types' } ).then(
function ( types ) {
TYPES.forEach(
function ( el ) {
if ( typeof( types[ el ] ) !== 'undefined' )
myTypes.push( el );
} );
} )
.then(
FETCH( { path : ROUTES.posts, types : myTypes } ).then(
function ( res ) {
resolve( res )
} ) )
} )
}
/* 'post_selector_opts' takes the posts 'get_posts' and turns them into an options array */
function post_selector_opts() {
return new Promise(
function ( resolve ) {
get_posts().then( function ( posts ) {
let opts = [ DY ];
posts.forEach( function ( P ) {
opts.push( { key: P.slug, label: P.title, value: P.id } );
} );
resolve( opts );
}
) }
)
}
/* For some reason which makes sense to someone, 'post_selector_opts' results in a promise,
* not an array. Unless you retrieve it in a promise, the sole reason for 'post_selectors'.
*
* I plunk the array into 'DYNAMIC.posts` so everyone can enjoy. */
function post_selectors() {
post_selector_opts().then( opts => {
DYNAMIC.posts = opts;
} );
}
} )( wp.blocks, wp.editor, wp.components, wp.element.createElement, wp.apiFetch );
@sparkbold
Copy link

shouldn't we use withSelect for wpapi?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment