I have created a map block plugin that works more or less, but I’m having trouble understanding how to load the map script when/after the block is inserted. If I reload the page after inserting/saving, or if I load the public view, the map script loads as expected (since I’ve enqueued the scripts and styles via PHP). Normally, I would attach the script to some kind of DOM event, but I realize that Gutenberg/React handles things differently.
So my questions are:
- How do I load a script (remote or inline) immediately after my block is inserted?
- How do I set block properties in a remote script that’s been enqueued via PHP (i.e. using
props.setAttributes()
, which isn’t available to scripts located outside of my block registration)?
This is my first stab at Gutenberg/React, but here’s what I’m working with at the moment. I may be missing some fundamentals.
import './style.scss';
import './editor.scss';
const { __ } = wp.i18n;
const { registerBlockType, getBlockDefaultClassName } = wp.blocks;
const { PlainText, InspectorControls, PanelBody } = wp.editor;
const { TextareaControl, TextControl } = wp.components;
const { withState } = wp.compose;
registerBlockType( 'myplugin/block-map-location', {
title: __( 'Location Map' ),
icon: 'location',
category: 'myplugin',
keywords: [ __( 'Map' ), __( 'Location' ), __( 'myplugin' ) ],
supports: {
anchor: true,
html: false,
multiple: false,
reusable: false,
},
description: __( 'A block for adding a location map.' ),
attributes: {
caption: {
type: 'string',
source: 'text',
selector: '.map-caption-pp',
},
lat: {
type: 'string',
selector: 'div.map-pp',
source: 'attribute',
attribute: 'data-lat',
},
lon: {
type: 'string',
selector: 'div.map-pp',
source: 'attribute',
attribute: 'data-lon',
},
zoom: {
type: 'string',
selector: 'div.map-pp',
source: 'attribute',
attribute: 'data-zoom',
},
mb_key: {
type: 'string',
selector: 'div.map-pp',
source: 'attribute',
attribute: 'data-mb-key',
},
maki: {
type: 'string',
selector: 'div.map-pp',
source: 'attribute',
attribute: 'data-zoom',
},
maki_color: {
type: 'string',
selector: 'div.map-pp',
source: 'attribute',
attribute: 'data-maki-color',
},
basemap: {
type: 'string',
selector: 'div.map-pp',
source: 'attribute',
attribute: 'data-basemap',
},
query: {
type: 'string',
selector: 'input.query-pp',
source: 'text',
},
},
edit( props ) {
const {
attributes: {
caption,
zoom,
lat,
lon,
mb_key,
maki,
maki_color,
basemap,
query,
},
className,
setAttributes,
} = props;
const onChangeCaption = caption => {
setAttributes( { caption } );
};
const onSubmitQuery = function( e ) {
e.preventDefault();
console.log( query );
};
const defaults = myplugin_plugin_settings.myplugin_defaults;
if ( ! zoom ) {
props.setAttributes( { zoom: defaults.default_zoom } );
}
if ( ! lat ) {
props.setAttributes( { lat: defaults.default_latitude } );
}
if ( ! lon ) {
props.setAttributes( { lon: defaults.default_longitude } );
}
if ( ! mb_key ) {
props.setAttributes( { mb_key: defaults.mapbox_key } );
}
if ( ! maki ) {
props.setAttributes( { maki: defaults.maki_markers } );
}
if ( ! maki_color ) {
props.setAttributes( { maki_color: defaults.maki_markers_color } );
}
if ( ! basemap ) {
props.setAttributes( { basemap: defaults.default_map_type } );
}
return (
<div
className={ props.className }
aria-label={ __( 'Interactive Map' ) }
role="region"
>
<form className="inline-input" onSubmit={ onSubmitQuery }>
<TextControl
className="query-pp"
tagName="input"
placeholder={ __(
'Type a query and press Enter/Return.',
'wp_myplugin'
) }
onChange={ input => setAttributes( { query: input } ) }
/>
</form>
<figure>
<div
className="map-pp"
id="myplugin-map"
data-lat={ lat }
data-lon={ lon }
data-zoom={ zoom }
data-mb-key={ mb_key }
data-maki={ maki }
data-maki-color={ maki_color }
data-basemap={ basemap }
/>
<TextareaControl
rows="2"
className="map-caption-pp"
tagName="figcaption"
placeholder={ __(
'Type a caption for the map (optional).',
'wp_myplugin'
) }
value={ caption }
onChange={ onChangeCaption }
/>
</figure>
</div>
);
},
save( props ) {
const className = getBlockDefaultClassName( 'myplugin/block-map-location' );
const { attributes } = props;
return (
<div
className={ props.className }
aria-label={ __( 'Interactive Map' ) }
role="region"
>
<figure>
<div
className="map-pp"
id="myplugin-map"
data-lat={ attributes.lat }
data-lon={ attributes.lon }
data-zoom={ attributes.zoom }
data-mb-key={ attributes.mapbox_key }
data-maki={ attributes.maki_markers }
data-maki-color={ attributes.maki_markers_color }
data-basemap={ attributes.basemap }
/>
<figcaption className="map-caption-pp">
{ attributes.caption }
</figcaption>
</figure>
</div>
);
},
} );
This is how the scripts are currently enqueued…
function myplugin_blocks_cgb_block_assets() {
wp_enqueue_style(
'myplugin_blocks-cgb-style-css',
plugins_url( 'dist/blocks.style.build.css', dirname( __FILE__ ) ),
array( 'wp-editor' )
);
wp_enqueue_style(
'myplugin-leaflet-css',
'https://unpkg.com/leaflet@1.4.0/dist/leaflet.css',
array()
);
wp_enqueue_script(
'myplugin-leaflet-js',
'https://unpkg.com/leaflet@1.4.0/dist/leaflet.js',
array()
);
wp_enqueue_script(
'myplugin-location',
plugins_url( 'myplugin-location.js', __FILE__ ),
array()
);
}
add_action( 'enqueue_block_assets', 'myplugin_blocks_cgb_block_assets' );
1 Answer
I ended up putting an empty svg in an img
tag with an onload
event. This is probably not ideal, but it does work.
<img // onload hack fires when block is added
className="onload-hack-pp"
height="0"
width="0"
onLoad={ onBlockLoad }
src="data:image/svg+xml,%3Csvg xmlns="http://www.w3.org/2000/svg" viewBox='0 0 1 1' %3E%3Cpath d=''/%3E%3C/svg%3E"
/>