Gutenberg: Passing data from parent to InnerBlocks child blocks

Created on 21 May 2019  路  3Comments  路  Source: WordPress/gutenberg

I'm looking for a good way to pass data from parent block to childs of InnerBlocks

So basically i have only 1 block now which is tinymce editor. I also created SectionEdit Component which I'm gonna use to wrap every block i need.

Note: Every block is rendered dynamically trough php controller that detects if block is using section and wraps it in html.

So i have helper Section.js where i export:

export const sectionAttributes = {
  sectionUse: {
    type: 'number'
  },
  sectionInside: {
    type: 'number'
  },
  ... other options that only adds classes to section
}

and SectionEdit

export class SectionEdit extends Component {
  constructor () {
    super(...arguments)
  }

  render () {
    const { attributes, setAttributes } = this.props._props

    // this is needed so php controller knows to wrap block in section
    setAttributes({ sectionUse: 1 })

    return (
      <Fragment>
        <InspectorControls>
          <PanelBody title={'Section Settings'} initialOpen={false}>
               ...controls for attributes
          </PanelBody>
        </InspectorControls>
        {this.props.children}
      </Fragment>
    )
  }
}

So now let's take a look on tinymce block. Main file index.js is where i import sectionAttributes . and edit Component. I merge attributes together.
It's literally copy of core Classic block wrapped in my SectionEdit;

import { sectionAttributes } from '../../helpers/Section'
import icon from './icon'
import edit from './edit'

const wp = window.wp
const { registerBlockType } = wp.blocks

let attributes = {
  content: {
    type: 'string'
  }
}
attributes = { ...sectionAttributes, ...attributes }

registerBlockType('wpbase-blocks/tinymce', {
  title: 'TinyMCE Editor',
  icon: icon,
  description: 'Classic TinyMCE Editor ',
  category: 'wpbase',
  keywords: ['classic', 'editor'],
  supports: {
    className: false,
    customClassName: false,
    reusable: false
  },
  attributes: attributes,
  edit: edit,
  save: function (props) {
    return null
  }
})

inside edit component i wrap tinymce elements in SectionEdit. You can see i pass all properties to SectionEdit trough _props

import { SectionEdit } from '../../helpers/Section'

const wp = window.wp
const { Component } = wp.element

export default class ClassicEdit extends Component {
  constructor (props) {
    super(props)
    this.initialize = this.initialize.bind(this)
    this.onSetup = this.onSetup.bind(this)
    this.focus = this.focus.bind(this)
  }

  {...}

  render () {
    const { clientId } = this.props

    return (
      <SectionEdit _props={this.props}>
        <div key="toolbar" id={`toolbar-${clientId}`}
             ref={(ref) => { this.ref = ref }}
             className="wpbase-tinymce__toolbar"
             onClick={this.focus}
             data-placeholder={'TinyMCE'}
             onKeyDown={this.onToolbarKeyDown}
        />

        <div key="editor" id={`editor-${clientId}`}
             className="wpbase-tinymce__editor"
        />
      </SectionEdit>
    )
  }
}

Tinymce block works like a charm, php rendering, using SectionEdit component too BUT

What I'm trying to achieve next is block collapsing-section where i use InnerBlocks. I wanna my SectionEdit to know when its in collapsing-section so it will render html differently. I allow all blocks except collapsing-section itself.

const wp = window.wp
const { Fragment } = wp.element
const { Toolbar } = wp.components
const { registerBlockType, getBlockTypes } = wp.blocks
const { InnerBlocks, BlockTitle } = wp.editor

let allowedBlocks = []
getBlockTypes().forEach(function (block) {
  allowedBlocks.push(block.name)
})
// allow all blocks except itself

registerBlockType('wpbase-blocks/collapsing-section', {
  title: 'Collapsing Header',
  icon: 'shield',
  description: 'Header wrapper that collapse up on scroll',
  category: 'wpbase',
  keywords: ['classic', 'editor'],
  supports: {
    className: false,
    customClassName: false,
    reusable: false
  },
  attributes: {
    sectionInside: {
      type: 'number'
    }
  },
  edit: function (props) {
    const { clientId, setAttributes } = props

    setAttributes({ sectionInside: 1 })
    //  this is what i want to pass to childs

    return (
      <Fragment>
        <Toolbar>
          <BlockTitle clientId={clientId}/>
        </Toolbar>
        <InnerBlocks allowedBlocks={allowedBlocks}/>
      </Fragment>
    )
  },
  save: function (props) {
    return (
      <InnerBlocks.Content/>
    )
  }
})

Inside collapsing-section I'm gonna have tinymce blocks. From collapsing-section i wanna pass attribute sectionInside with value 1 that i can access inside tinymce block and pass it to `SectionEdit.

PS: I hope this code will help someone out! If You see some bad practice or something that i use badly please let me know! I'm trying to make this work super automatically but I'm learning many concepts thanks to gutenberg like React comonents, es6 and others
PPS: Gutenberg documentation is really really really bad. It feels like it's 20% of what should it be and many things are missing. If You could first make good documentation and than go for phase 2 I'm sure community will be much more welcome to gutenberg and there will be much more contributors to the project.

[Type] Help Request

Most helpful comment

There's a very new block context feature that's relevant:
https://github.com/WordPress/gutenberg/blob/master/docs/designers-developers/developers/block-api/block-context.md

It's only available in the Gutenberg plugin at the moment.

All 3 comments

For anybody having problem like me this is the solution:
in container block edit:

  edit: function (props) {
    const { clientId } = props

    select('core/editor').getBlocksByClientId(clientId)[0].innerBlocks.forEach(function (block) {
      dispatch('core/editor').updateBlockAttributes(block.clientId, { sectionTag: 'div' })
    })

    return (
      <Fragment>
        <div className="editor-block-list__title">
          <BlockTitle clientId={clientId}/>
        </div>
        <InnerBlocks allowedBlocks={allowedBlocks}/>
      </Fragment>
    )
  },

and in my section i added those:

    if (typeof sectionUse === 'undefined')
      setAttributes({ sectionUse: 1 })
    if (typeof sectionTag === 'undefined')
      setAttributes({ sectionTag: 'section' })
    if (typeof sectionContainer === 'undefined')
      setAttributes({ sectionContainer: 1 })

Awesome! Thanks man, Helps a ton.

There's a very new block context feature that's relevant:
https://github.com/WordPress/gutenberg/blob/master/docs/designers-developers/developers/block-api/block-context.md

It's only available in the Gutenberg plugin at the moment.

Was this page helpful?
0 / 5 - 0 ratings