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.

Answers:

Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.

Method 1

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


All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0

0 0 votes
Article Rating
Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments
0
Would love your thoughts, please comment.x
()
x