How to do store, retrieve and write attributes in the registerFormatType method in Guttenberg?

I'm trying to add a format that has multiple, independent attributes to a custom a tag. The attributes are href= and data-caption= and data-set= while I use custom client side JavaScript to process the data attributes.

Here's my shoddy, work in progress code:

    var el = wp.element.createElement;
    var components = wp.components;

    var withSelect  = wp.data.withSelect;
    var ifCondition = wp.compose.ifCondition;
    var compose     = wp.compose.compose;

    var caption;
    var setName;

    wp.richText.registerFormatType(
        'test/set-link',
        {
            attributes: {
                url: 'href',
                caption: 'data-caption',
                setName: 'data-set',
            },
            title: 'Sets Link',
            tagName: 'a',
            className: 'sets-link',
            edit: (props) = {
                var attributes = props.attributes; // console declares this as undefined.
                return [
                    el(wp.blockEditor.RichTextToolbarButton, {
                        icon: 'block-default',
                        title: __('Sets Link'),
                        isActive: true,
                        onClick: () = {
                            props.onChange(
                                wp.richText.toggleFormat( props.value, {
                                    type: 'test/sets-link'
                                })
                            )           
                        }
                    }),
                    props.isActive  ( el( components.Popover, 
                        {
                            position: 'bottom center',
                            headerTitle: 'Sets Attributes',
                        }, 
                        [
                            el( components.TextControl, {
                                placeholder: 'Set Name',
                                value: attributes.setName,
                                onChange:  (newSetName) = {
                                    props.setAttributes( { setName: newSetName } 
 ) // TypeError: can't access property setName, attributes is undefined
                                },
                            }),
                            el( components.TextControl, {
                                placeholder: 'Set Caption',
                                value: attributes.caption,
                                onChange: (newCaption) = {
                                    props.setAttributes( { caption: newCaption } ) // TypeError: can't access property setName, attributes is undefined
                                }
                            }),
                            el( components.Button, {
                                className: 'button button-large',
                                onClick: () = {
                                    //TODO: here
                                }

                            },
                                'Set'
                            )
                        ]
                    )),
                    compose(
                        withSelect( ( select ) = {
                            return {
                                selectBlock: select( 'core/block-editor' ).getSelectedBlock()
                            }
                        } ),
                        ifCondition( ( props ) = {
                            return(
                                props.selectBlock
                            )
                        } )
                    )
                ]
            },
        }
    )

Well, surprisingly, it works to a good degree. A Popover appears to accept input but it cannot store them in memory. Second, how can it export the stored attributes as output attributes.

I know that using registerBlockType method would be easier and more defined but the reason I'm not using it is that the custom links, a tag, may be mixed in with the rest of the RichText of the Guttenberg Editor.

I really appreciate all your help on this interesting conundrum.

Topic block-editor formatting Wordpress

Category Web


So inline formats don't have a state as such, they aren't discrete objects like blocks. Instead you apply a format to a slice of text, and the UI is generated on the fly.

That UI is then given attributes that are currently active, an activeAttributes array is passed that contains those attributes. Note only active attributes are passed. If you simply apply a format to text, you have not set any attributes yet, so this array will be empty. So you cannot rely on the attributes always being present, or having default values.

When you want to change those attributes or add them, you can't just update or set them, instead you need to call applyFormat with the value ( available in props ), specifying which format, and the full list of attributes to apply ( it doesn't update, it overwrites ). Then you need to call onChange with the new value that applyFormat returned afterwards.

Here is an example that slims yours down to just a name attribute:

wp.richText.registerFormatType(
    'test/set-link',
    {
        attributes: {
            name: 'data-set',
        },
        title: 'Sets Link',
        tagName: 'a',
        className: 'sets-link',
        edit: (props) => {
            return [
                el(wp.blockEditor.RichTextToolbarButton, {
                    icon: 'block-default',
                    title: 'Sets Link',
                    isActive: props.isActive,
                    onClick: () => {
                        props.onChange(
                            wp.richText.toggleFormat( props.value, {
                                type: 'test/set-link'
                            })
                        )
                    }
                }),
                props.isActive && ( el( components.Popover,
                    {
                        position: 'bottom center',
                        headerTitle: 'Sets Attributes',
                    },
                    [
                        el( components.TextControl, {
                            placeholder: 'Set Name',
                            value: props.activeAttributes.name ? props.activeAttributes.name : '',
                            onChange:  (newName) => {
                                newval = wp.richText.applyFormat(
                                    props.value,
                                    {
                                        type: 'test/set-link',
                                        attributes: {
                                            name: newName
                                        }
                                    }
                                );
                                props.onChange( newval );
                            },
                        }),
                    ]
                ))
            ]
        },
    }
)

Note that it would be much smaller if you used the WP Scripts tool, which would allow you to use JSX/React directly without bundling the libraries.

I also fixed a typo, and a bug where isActive was always set to true on the toolbar button

About

Geeks Mental is a community that publishes articles and tutorials about Web, Android, Data Science, new techniques and Linux security.