admin管理员组

文章数量:1201993

I want to get the block ID of my widgets (e.g. #block-1, which is displayed on the frontend after saving a widget) on the widgets screen in the backend. What I try to achieve is to display the ID of the widget in the toolbar so that you can easily copy it.

I currently use a custom API endpoint to receive the ID but it will trigger a request on every render (I already debounced it, but am searching for a better way).

My idea was than to subscribe to the save action of the widgets, since the ID is first available after saving the widgets screen:

const widgetIdToolbar = createHigherOrderComponent( ( BlockEdit ) => {
    if ( select( 'core/editor' ).getCurrentPostType() ) {
        return BlockEdit;
    }
    
    return ( props ) => {
        if ( props.name === 'core/widget-area' || props.name === 'core/legacy-widget' ) {
            return ( <BlockEdit { ...props } /> );
        }
        
        const [ widgetId, setWidgetId ] = useState( 0 );

        subscribe( () => {
            const isAutosavingPost = select( 'core/editor' ).isAutosavingPost();
            const isSavingPost = select( 'core/editor' ).isSavingPost();
                
            if ( isSavingPost || isAutosavingPost ) {
                // do stuff
            }
        } );
        
        return (
            <>
                <BlockEdit { ...props } />
            </>
        );
    }
}, 'widgetIdToolbar' );

addFilter( 'editor.BlockEdit', 'rh-widget-toolbar/add-toolbar', widgetIdToolbar );

Unfortunately, both isAutosavingPost and isSavingPost always return false. It seems they don’t apply to widgets. How to get the similar functionality for “on widgets save”?

I want to get the block ID of my widgets (e.g. #block-1, which is displayed on the frontend after saving a widget) on the widgets screen in the backend. What I try to achieve is to display the ID of the widget in the toolbar so that you can easily copy it.

I currently use a custom API endpoint to receive the ID but it will trigger a request on every render (I already debounced it, but am searching for a better way).

My idea was than to subscribe to the save action of the widgets, since the ID is first available after saving the widgets screen:

const widgetIdToolbar = createHigherOrderComponent( ( BlockEdit ) => {
    if ( select( 'core/editor' ).getCurrentPostType() ) {
        return BlockEdit;
    }
    
    return ( props ) => {
        if ( props.name === 'core/widget-area' || props.name === 'core/legacy-widget' ) {
            return ( <BlockEdit { ...props } /> );
        }
        
        const [ widgetId, setWidgetId ] = useState( 0 );

        subscribe( () => {
            const isAutosavingPost = select( 'core/editor' ).isAutosavingPost();
            const isSavingPost = select( 'core/editor' ).isSavingPost();
                
            if ( isSavingPost || isAutosavingPost ) {
                // do stuff
            }
        } );
        
        return (
            <>
                <BlockEdit { ...props } />
            </>
        );
    }
}, 'widgetIdToolbar' );

addFilter( 'editor.BlockEdit', 'rh-widget-toolbar/add-toolbar', widgetIdToolbar );

Unfortunately, both isAutosavingPost and isSavingPost always return false. It seems they don’t apply to widgets. How to get the similar functionality for “on widgets save”?

Share Improve this question edited May 12, 2022 at 6:21 KittMedia asked May 11, 2022 at 8:44 KittMediaKittMedia 2411 silver badge9 bronze badges 13
  • widgets are implemented as a legacy widget block that wraps around the widget to provide a compatibility layer, though in this case it shouldn't matter, the root problem is that the widgets screen is not a post editor so there is no main post to autosave. As an aside, what are you trying to do that requires this? – Tom J Nowell Commented May 11, 2022 at 10:18
  • also is this on the edit widgets screen, or the customizer screen? – Tom J Nowell Commented May 11, 2022 at 10:27
  • As I said, I want to get the block ID of the widgets in the backend, which is then displayed as ID in the frontend. This ID is first available after saving the widgets and not before (except for blocks that use server-side rendering). Thus I want to subscribe to the save action to get the ID directly afterwards. It’s the dedicated widgets screen I’m talking about. Will update the question accordingly. – KittMedia Commented May 11, 2022 at 10:29
  • I see, what are you trying to do when this happens that requires it though? Needing the block ID is usually a sign that you're doing something wrong, or that a much simpler solution is present that you're unaware of. If you can provide the context then those can be shared, or it'll avoid unnecessary questions. It's also possible that someone will provide an answer that while technically correct, can't be used in your situation. – Tom J Nowell Commented May 11, 2022 at 10:39
  • Also, where did you put the subscribe code in your question? Is it enqueued on that page or is it included some other way only when that widget runs? Or on a particular event? Are you trying to use the block ID to traverse upwards or across or to get the sidebar ID? – Tom J Nowell Commented May 11, 2022 at 11:09
 |  Show 8 more comments

1 Answer 1

Reset to default 0

If the goal is to display the block ID so that the user can get it easily, instead try using a block editor filter to add a panel in the sidebar:

From: https://developer.wordpress.org/block-editor/reference-guides/filters/block-filters/#editor-blockedit

const { createHigherOrderComponent } = wp.compose;
const { Fragment } = wp.element;
const { InspectorControls } = wp.blockEditor;
const { PanelBody } = wp.components;
 
const withInspectorControls = createHigherOrderComponent( ( BlockEdit ) => {
    return ( props ) => {
        return (
            <Fragment>
                <BlockEdit { ...props } />
                <InspectorControls>
                    <PanelBody>My custom control</PanelBody>
                </InspectorControls>
            </Fragment>
        );
    };
}, 'withInspectorControl' );
 
wp.hooks.addFilter(
    'editor.BlockEdit',
    'my-plugin/with-inspector-controls',
    withInspectorControls
);

Then inside the panel, use props to retrieve the blocks ID and display it, perhaps in an input with the disabled property. Specifically clientId.

It might look like this (untested):

const { createHigherOrderComponent } = wp.compose;
const { Fragment } = wp.element;
const { InspectorControls } = wp.blockEditor;
const { PanelBody } = wp.components;
 
const withWidgetIDPanel = createHigherOrderComponent( ( BlockEdit ) => {
    return ( props ) => {
        const widgetID = props.__internalWidgetId ? props.__internalWidgetId : 0;
        return (
            <Fragment>
                <BlockEdit { ...props } />
                <InspectorControls>
                    <PanelBody>
                        <input
                            class="components-text-control__input"
                            type="text"
                            disabled
                            value={ props.widgetID }
                        />
                    </PanelBody>
                </InspectorControls>
            </Fragment>
        );
    };
}, 'withWidgetIDPanel' );
 
wp.hooks.addFilter(
    'editor.BlockEdit',
    'tomjn/display-widget-id',
    withWidgetIDPanel
);

This will give you the widget ID if it's available in the sidebar for every legacy widget if enqueued on the widgets page, and provides a useful debug panel for non-widget editors too. It also gives you a place to use react hooks and other WordPress APIs in the context of a specific block, without needing to figure out which widget belongs to which block ID.

Note that when the widget ID changes, React will re-render the component automatically, no subscription is necessary. Note that no local state is in use.

Frontend Block ID

If however you want the ID that gets used on the frontend, there's an attribute named __internalWidgetId. When this changes React will re-render the edit component because its props have changed, so no subscription to the store is necessary.

Before the widget is saved, this __internalWidgetId attribute does not exist, and will be null if accessed, and when it comes into existence again the edit component will re-render because changed props are how React knows to re-render a DOM element.

Storing the widget ID in local state is not necessary and could lead to problems. Just pass the raw attribute down if it's not null to any child components that need it or use the internal widget ID directly.

As a bonus, you can select on the core/edit-widgets store to grab the widget data directly.

本文标签: block editorisSavingPost() for widgets