Load script after block is inserted

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
1

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"
/>

Leave a Comment