1

I am using the templateLock property to lock down the template of an inner block:

edit: () => {
     return (
         <InnerBlocks
             template={ MY_TEMPLATE }
             templateLock="all"
         />
     );
 },

I am not allowing to insert new blocks or delete them.

However I would like to add or delete blocks programmatically, for example using:

wp.data.dispatch("core/block-editor").removeBlock( blockClientId );

This does not work because the template lock prevents all operations. Is it possible to lock the template but still be able to add/delete the child blocks programmatically?

I know it is possible to lock individual blocks with the new lock attribute, but this adds a lock icon to the blocks, also the possibility to uncheck locking. I do not want the user to see any add or delete block UI, or be confused by the lock icon.

4
  • I couldn't find a standard way of doing this, but I think you can use a custom (and private) block attribute, e.g. _lockTemplate, and basically set it to false when you want to programmatically add/remove a child block, then set it to true afterwards. Or have you already found a (different) solution?
    – Sally CJ
    Commented Jul 22, 2022 at 6:39
  • @SallyCJ, i did not find another solution. I was also thinking about turning on and off template lock when needed. However I did not know there are private block attributes. Are you able to please post your solution as an answer? This may be the only solution available.
    – CyberJ
    Commented Jul 22, 2022 at 7:31
  • There aren't any "private attributes", and by "private", I simply meant an attribute with a name that starts with _ (underscore) which is normally treated as private. And after a 2nd thought, I think you could actually use state and just modify the template value to add or delete child blocks. Would you like to see an example?
    – Sally CJ
    Commented Jul 23, 2022 at 0:09
  • 1
    @SallyCJ an example would definitely help!
    – CyberJ
    Commented Jul 23, 2022 at 7:49

1 Answer 1

2

I couldn't find a standard way to do this, but as I commented, an easy way to do it is by using a state which will store either the initial template you set or the inner blocks (i.e. their names and attributes) that were already added to the parent block. Which basically means, you will pass conditional template to the InnerBlocks component.

Working Example

  1. WordPress dependencies specific to this example, in addition to InnerBlocks etc.

    import { useSelect } from '@wordpress/data';
    import { useState } from '@wordpress/element';
    import { Button } from '@wordpress/components'; // for the demos; see below
    
  2. Now here's the edit() function where I added 2 buttons for testing the functionality you wanted to have:

    function edit( { clientId } ) {
        const innerBlocks = useSelect( select => {
            const { getBlocks } = select( 'core/block-editor' );
            return getBlocks( clientId );
        }, [ clientId ] );
    
        // If there are no inner blocks yet, use the initial template. Else, we use
        // the name and attributes of each (immediate) child block.
        const _template = ( innerBlocks.length < 1 ) ? MY_TEMPLATE :
            innerBlocks.map( block => [ block.name, block.attributes ] );
    
        // Add a state which ensures React re-renders our block when the template is
        // changed.
        const [ template, setTemplate ] = useState( _template );
    
        // Demo callback which adds a child block.
        const demoInsertChildBlock = () => // wrapped
            setTemplate( [ ...template, [ 'core/quote', {} ] ] );
    
        // Demo callback which removes a child block.
        const demoRemoveChildBlock = () => {
            const newTemplate = [ ...template ]; // copy current template
            newTemplate.pop();                   // remove the last child
            setTemplate( newTemplate );
        };
    
        return (
            <div { ...useBlockProps() }>
                Hello from the editor!
    
                <InnerBlocks
                    template={ template }
                    templateLock="all"
                />
    
                <Button text="Insert demo block" variant="primary" onClick={ demoInsertChildBlock } />
                <span>&nbsp;</span>
                <Button text="Remove last block" variant="primary" onClick={ demoRemoveChildBlock } />
            </div>
        );
    }
    

    So as you could see, you just need to call setTemplate() function to add/remove a child block.

4
  • PS: If you wanted to actually add/remove a child block before the element is rendered, then you may not need the state at all... but I thought you wanted to add/remove child blocks only after the element is rendered? Additionally, I defined MY_TEMPLATE outside the edit() function, however it shouldn't be a problem even if you (need to) define the constant inside that function.
    – Sally CJ
    Commented Jul 24, 2022 at 7:59
  • 1
    Thank you this is brilliant
    – CyberJ
    Commented Jul 27, 2022 at 7:39
  • But this only allows removing the last block, you can't remove blocks in the middle?
    – riv
    Commented Sep 26, 2022 at 12:30
  • @riv removing the last child is just an example. Presumably there's nothing stopping you from modifying or recreating the template however you see fit
    – ChrisM
    Commented Dec 2, 2023 at 11:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.