3

We're slowly migrating our wordpress theme development process over to the Gutenberg editor and I made quit a few custom Gutenberg blocks now, either with the Wordpress Block API or with the ACF Blocks. I try to use the wp core blocks as much as possible for the best user experience when editing.

On a custom post type project I want to have a leading paragraph, followed by a normal paragraph. Next to this I want to list some meta data as a static list, coming from a few ACF fields on the post type. This data is used elsewhere in my theme so I want to keep these as ACF fields.

So I'm creating a custom Gutenberg block for this using:

function registerBlocks() {
    wp_register_script(
        'domain/opening-paragraph-editor',
        asset('scripts/opening-paragraph.editor.js')->uri(),
        ['sage/vendor.js']
    );

    wp_register_style(
        'domain/opening-paragraph',
        asset('styles/opening-paragraph.css')->uri()
    );

    register_block_type(
        'domain/opening-paragraph', [
            'api_version'     => 2,
            'editor_script'   => 'domain/opening-paragraph-editor',
            'editor_style'    => 'domain/opening-paragraph',
            'render_callback' => function($block_attributes, $content) {
                wp_enqueue_style('domain/opening-paragraph');
                return $content;
            }
        ]
    );
}
add_action('init', 'registerBlocks');

My scripts/opening-paragraph.editor.js:

import React  from 'react';
import {__} from '@wordpress/i18n';
import {registerBlockType} from '@wordpress/blocks';
import {RichText, InnerBlocks, useBlockProps} from '@wordpress/block-editor';

const ALLOWED_BLOCKS = ['core/paragraph'];
const TEMPLATE = [
    ['core/paragraph', {
        className: 'lead'
    }],
    ['core/paragraph']
];

registerBlockType('domain/lead-project', {
    apiVersion: 2,
    title: __('Lead Project'),
    description: __('Opening lead paragraph with project meta block.'),
    icon: 'editor-paragraph',
    category: 'text',
    attributes: {
        projectClient: {
            type: 'string'
        },
        projectDate: {
            type: 'string'
        },
        projectLocation: {
            type: 'string'
        }
    },
    example: {
        attributes: {
            projectClient: 'Awakenings',
            projectDate: '23 MAR 2014',
            projectLocation: 'Gashouder, Amsterdam'
        }
    },
    edit: ({attributes, setAttributes}) => {
        const blockProps = useBlockProps();
        const {projectClient, projectDate, projectLocation} = attributes;

        return (
            <div { ...blockProps }>
                <div className="wp-block-domain-lead-project__wrap">
                    <div className="wp-block-domain-lead-project__inner">
                        <InnerBlocks
                            allowedBlocks={ALLOWED_BLOCKS}
                            template={TEMPLATE}
                            templateLock="all"
                        />
                    </div>
                    <div className="wp-block-domain-lead-project__summary">
                        <RichText
                            tagName="p"
                            value={projectClient}
                            contentEditable={false}
                            onChange={text => setAttributes({projectClient: text})}
                        />
                        <RichText
                            tagName="p"
                            value={projectDate}
                            contentEditable={false}
                            onChange={text => setAttributes({projectDate: text})}
                        />
                        <RichText
                            tagName="p"
                            value={projectLocation}
                            contentEditable={false}
                            onChange={text => setAttributes({projectLocation: text})}
                        />
                    </div>
                </div>
            </div>
        );
    },
    save: ({attributes}) => {
        const blockProps = useBlockProps.save();
        const {projectClient, projectDate, projectLocation} = attributes;

        return (
            <div { ...blockProps }>
                <div className="wp-block-domain-lead-project__wrap">
                    <div className="wp-block-domain-lead-project__inner">
                        <InnerBlocks.Content/>
                    </div>
                    <RichText.Content
                        tagName="p"
                        value={projectClient}
                    />
                    <RichText.Content
                        tagName="p"
                        value={projectDate}
                    />
                    <RichText.Content
                        tagName="p"
                        value={projectLocation}
                    />
                </div>
            </div>
        );
    }
});

And for locking this block to the post type and passing the data as attributes, I'm creating a Block template:

function registerBlockTemplates() {
    $post_id = isset($_GET['post']) ? $_GET['post'] : null;

    if (get_post_type($post_id) == 'project') {
        $project_object   = get_post_type_object('project');
        $projectClient    = get_field('project_client', $post_id);
        $projectDateStart = get_field('project_dates_start', $post_id);
        $projectDateEnd   = get_field('project_dates_end', $post_id);
        $projectLocation  = get_field('project_location', $post_id);

        $project_object->template = [
            ['domain/lead-project', [
                'projectClient'   => $projectClient ?: '',
                'projectDate'     => $projectDateStart ? $projectDateStart . ($projectDateEnd ? ' – ' . $projectDateEnd : '') : '',
                'projectLocation' => $projectLocation
            ]],
            ['domain/inner-blocks']
        ];
        $project_object->template_lock = 'all';
    }
}
add_action('init', 'registerBlockTemplates', 999, 0);

So far so good, this works great! When I start editing a project post, the meta data is populated with the ACF fields from that project and I can add/save content to the core/paragraph blocks.

The content is saved in the database and displayed correctly in my frontend. However, whenever I change one of the ACF fields, these changes are not reflected in my Gutenberg block, not even on a page refresh.

Instead I get the message:

The content of your post doesn’t match the template assigned to your post type.

When I log the attribute values in javascript, these are the old values. Even on a page refresh. I can only see that the post content is still saved with the old attributes:

<!-- wp:domain/lead-project {"projectClient":"Testing","projectDate":"07 Jul 2013 – 04 Mar 2017","projectLocation":"Ibiza, Amsterdam"} -->

So my question basically is; what is the best practice to use/display static data that doesn't have to be editable in the Gutenberg editor, but should update itself whenever it's value changes?

Instead of using the RichText block, I also tried using plain jsx tags for displaying the data, but it has the same result.

Any thoughts, tips? Thanks a lot!

EDIT: I did found this Use Post Meta Data tutorial, but that just appends the data to the end of a block, without control over the markup plus it's not visible in the editor.

Twansparant
  • 171
  • 2
  • 13

0 Answers0