I'm trying to create a Gutenberg block in Wordpress where the user of the website can chose values from two selects. Each time a user changes the value from one of the select values an API call to an external source should be performed. When the api call result is returned below the two selects a grid with 3 results should be shown.
In order to achieve this I've tried multiple solutions, one of which is the dynamic blocks that are discussed in the Wordpress documentation. But when I render the content of my dynamic block the content is rendered in the top left corner of the screen. Which feels very strange to me.
Furthermore I've seen some people discuss the possibility to change the select values in the save part of the block and let the attributes on the edit side react on this and fetch the results. I wasn't able to find any examples on this and it felt strange to me but this would be my preferred solution given my background in working with react.
tldr; is it possible to use Gutenberg blocks and do API calls and show the result on the front-end to the user after changing values in two different select components?
Yes, using Server Side Rendering, where PHP handles the API request and returns the result to be rendered to the block. Given your description of "content rendering in the top left..", I think you are on the right path, but are perhaps missing the link between the PHP function and the <ServerSideRender>
component.
Below is a (very) basic example of how this can all work together as a dynamic Gutenberg block. I started by creating a basic static Gutenberg block with npx @wordpress/create-block to set up the project. The example block will request a new User from an external API (randomuser.me) then display the result. The values for the API parameters gender
and nationality
are stored as block attributes, eg:
src/block.json
{
"$schema": "https://schemas.wp.org/trunk/block.json",
"apiVersion": 2,
"name": "create-block/dynamic-rendering",
...
"attributes": {
"gender": {
"type": "string",
"default": "any"
},
"nationality": {
"type": "string",
"default": "AU"
}
}
}
The UI for the Editor is defined in edit.js to enable a User to select/update the values of the blocks attributes that are required by the API. The <ServerSideRender>
component is also required to enable the blocks saved attributes to be passed to PHP and render the content.
Note: A common block validation issue occurs when there is invalid value for the saved attribute (wrong type) or the saved value doesn't match any option in the <SelectControl>
. Providing a valid default value in the block.json for the attribute is a really good idea especially with API requests.
Eg.
src/edit.js
import { useBlockProps, InspectorControls } from '@wordpress/block-editor';
import { Panel, PanelBody, Disabled, SelectControl } from '@wordpress/components';
import ServerSideRender from '@wordpress/server-side-render';
...
export default function Edit({ attributes, setAttributes }) {
const { gender, nationality } = attributes;
return (
<div {...useBlockProps()}>
<InspectorControls>
<Panel>
<PanelBody title="Random User API Options">
<SelectControl
label="Gender"
value={gender}
options={[
{ label: 'Any', value: 'any' },
{ label: 'Male', value: 'male' },
{ label: 'Female', value: 'female' },
]}
onChange={(value) => setAttributes({ gender: value })}
/>
<SelectControl
label="Nationality"
value={nationality}
options={[
{ label: 'Australian', value: 'AU' },
{ label: 'Brazilian', value: 'BR' },
{ label: 'Canadian', value: 'CA' },
]}
onChange={(value) => setAttributes({ nationality: value })}
/>
</PanelBody>
</Panel>
</InspectorControls>
<Disabled>
<ServerSideRender
block="create-block/dynamic-rendering"
attributes={attributes}
/>
</Disabled>
</div>
);
}
save.js
export default function save() {
// nothing to save, as block is dynamic..
}
Lastly, we need to add a render_callback
to enable the static block to render dynamically. The PHP function will render the content inside <ServerSideRender>
in the Editor and as normal markup on the frontend (without react), eg:
dynamic-rendering.php (or plugin-name.php)
<?php
...
/**
* Registers the block using the metadata loaded from the `block.json` file.
* Behind the scenes, it registers also all assets so they can be enqueued
* through the block editor in the corresponding context.
*
* @see https://developer.wordpress.org/reference/functions/register_block_type/
*/
function create_block_dynamic_rendering_block_init()
{
register_block_type(__DIR__ . '/build', array(
// Add a render_callback to handle dynamic rendering of block with PHP
'render_callback' => 'create_block_dynamic_rendering_output'
));
}
add_action('init', 'create_block_dynamic_rendering_block_init');
/**
* Server-side rendering function for the `create-block/dynamic-rendering` block.
* Requests a random user from external API using the values saved in the block attributes
*
* @see https://developer.wordpress.org/apis/handbook/making-http-requests/getting-data-from-an-external-service/
*/
function create_block_dynamic_rendering_output($attributes)
{
$response = wp_remote_get("https://randomuser.me/api/?gender={$attributes['gender']}&nat={$attributes['nationality']}");
$http_code = wp_remote_retrieve_response_code($response);
return $http_code == 200 ? wp_remote_retrieve_body($response) : "Something went wrong..";
}