Gutenberg: InnerBlocks component saves only first nested block

Created on 9 Aug 2018  Â·  8Comments  Â·  Source: WordPress/gutenberg

Issue Overview

Hello, I've come across a weird issue with the InnerBlocks component on Gutenber, tried to create a custom tabs block with the use of InnerBlocks, and I can make use of the editor so it creates nested "TAB" blocks within my "TABS" block. However if I add multiple nested elements (e.g. 2+), the editor saves the blocks on publish/update in post and can display my output on the frontend.

If I refresh the editor I only see the first nested block and the others are scratched. Since the usage of InnerBlocks and the syntax is quite limited, I'm having hard time figuring out where the problem resides.

Code Examples

Tabs.js edit & save functions

//no arguments set in this block, not necessary at this point I think
edit(props) {
    return(
            <div className={props.className}>
                <h4>Tabs</h4>
                <div className={'Tabs'}>
                    <div className={'Tabs-wrap'}>
                        <InnerBlocks template={[['gity/tab', {}]]} layout={{}} />
                    </div>
                </div>
            </div>
        )
    }, 
save(props) {
        return(
            <div className={'Tabs'}>
                <div className={'Tabs-nav'}></div>
                <div className={'Tabs-wrap'}>
                    <InnerBlocks.Content />
                </div>
            </div>
        )
    }

tab.js

registerBlockType( 'gity/tab', {
...
parent: ['gity/tabs'],
    attributes: {
        title: {
            type: 'string',
            source: 'text',
            selector: '.Tabs-title'
        },
        body: {
            type: 'array',
            source: 'children',
            selector: '.Tabs-body'
        }
    },
    supports: {
        ...
    },
    edit(props) {
        let content = props.attributes
    return(
            <div className={props.className}>
                <strong>
                    <PlainText
                        onChange={ content => props.setAttributes({title: content}) }
                        value={ content.title }
                        placeholder="Titulek záložky"
                        className="Tabs-title"
                        formattingControls={ [] }
                        isSelected={props.isSelected}
                    />
                </strong>
                <RichText
                    onChange={ content => props.setAttributes({body: content}) }
                    value={ content.body }
                    multiline="p"
                    placeholder="Obsah záložky"
                    className="Tabs-body"
                    isSelected={props.isSelected}
                />
            </div>
        )
    },
    save(props) {
        let content = props.attributes
        return(
            <div className={props.className}>
                <span className={'Tabs-title'}>{content.title}</span>
                <div className={'Tabs-body'}>{content.body}</div>
            </div>
        )
    }

Steps to Reproduce (for bugs)

  1. Create a new parent block tabs.js
  2. Create a child block tab.js
  3. Make sure values are being saved
  4. Add multiple nested elements in editor
  5. Update post & refresh
  6. Only the top nested element is now available for edit...
  7. View post
  8. Values from other nested elements appear to be saved but can't be edited in editor
  9. update post & view post
  10. other nested values are missing...

    Screenshots / Video

Step 1

screenshot from 2018-08-09 18-08-07

Step 2

screenshot from 2018-08-09 18-08-34

Step 3

screenshot from 2018-08-09 18-08-52

Step 4

screenshot from 2018-08-09 18-09-46

Step 5

screenshot from 2018-08-09 18-10-05

Step 6

screenshot from 2018-08-09 18-10-13

Step 7

screenshot from 2018-08-09 18-10-55

Any suggestions? Is this normal behaviour?

Needs Testing [Feature] Nested / Inner Blocks

Most helpful comment

<InnerBlocks template={[['gity/tab', {}]]} layout={{}} />

The template includes only 1 tab so the rest are removed.

Maybe there's some hack to determine how many tabs are added and set the template accordingly

All 8 comments

<InnerBlocks template={[['gity/tab', {}]]} layout={{}} />

The template includes only 1 tab so the rest are removed.

Maybe there's some hack to determine how many tabs are added and set the template accordingly

Interesting so if I assign no template nor layout it works.. But then it's quite difficult to add the nested component in editor whilst there's no "empty template" prepared after adding the parent block into the editor.

Anyways thanks for help.

By replacing:
<InnerBlocks template={[['gity/tab', {}]]} layout={{}} /> for simply <InnerBlocks />, I'm able to work with the other nested tabs. How did I not try this...

I'm seeing this issue too – if I set a template on an InnerBlocks, any content that doesn't match the template gets cleared out on editor reload. I think this is happening in componentDidMount.

This doesn't feel like intended behaviour to me. I understand templates to be a default initial state - that's the way we're using them. The templateLock option suggests that any other blocks or changes should be respected and saved.

My thinking is that if the "reset to match the template" behaviour is needed, it should be optional, or perhaps on-demand. Maybe this option could be provided through templateLock parameter?

@hazari The way I currently do it is I remove the template and templateLock after the template has loaded for the first time.

For example I have this block that displays options to pick from multiple presets. When a preset is picked it outputs InnerBlocks with the selected template locked in and then in componentDidUpdate I toggle a boolean resulting in a re-render of just InnerBlocks without a template or lock, but the previous template's content remains, allowing the user to edit/add/remove anything.

Here's the gist of it:

const blockAttributes = {
    ...
    preset: {
        type: 'string',
    },
    templateLock: {
        type: 'boolean',
        default: true,
    },
};

class Edit extends wp.element.Component {

    componentDidUpdate(prevProps) {
        /**
         * Remove templateLock after loading a preset.
         */
        if(this.props.attributes.templateLock) {
            if(typeof prevProps.attributes.preset === 'undefined' && this.props.attributes.preset) {
                this.props.setAttributes({templateLock: false});
            }
        }
    }

    render() {
        const {attributes} = this.props;
        const {
            preset,
            templateLock,
        } = attributes;

        return [
            <div>
                {preset && (
                    templateLock ? (
                        <wp.editor.InnerBlocks
                            template={getPresetTemplate(preset)}
                            templateLock='all'
                        />
                    ) : (
                        <wp.editor.InnerBlocks />
                    )
                )}
                {!preset && (
                    <React.Fragment>
                        {/* displays preset picking options here */}

                        {/* this clears any previous innerblocks content after preset has been cleared */}
                        <div style={{display:'none'}}>
                            <wp.editor.InnerBlocks
                                template={[]}
                                templateLock='all'
                            />
                        </div>
                    </React.Fragment>
                )}
            </div>
        ];
    }

}

@websevendev Thanks for that idea. I'm hoping this can be supported directly in core, but that's an interesting a fallback option.

The following PR should (hopefully) fix this issue.

https://github.com/WordPress/gutenberg/pull/9674

After spending a lot of times, I found this. So yay, my code works, it's a bug (or a behavior for some reason?) Thanks for reporting. :)

Fixed up by #9674

Was this page helpful?
0 / 5 - 0 ratings

Related issues

spocke picture spocke  Â·  3Comments

nylen picture nylen  Â·  3Comments

wpalchemist picture wpalchemist  Â·  3Comments

maddisondesigns picture maddisondesigns  Â·  3Comments

aaronjorbin picture aaronjorbin  Â·  3Comments