Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gutenberg Custom Block: Add elements by innerBlocks length

Context: I'm working on a Custom Gutenberg plugin. One of the blocks I'm trying to create is a Carousel (using Bootstrap's Carousel).

I'm trying to optional add indicators based on the number of child blocks. I'm getting the error: Block validation: Block validation failed

It definitely has something to do with me adding in the indicators. Here's my block. Can anyone help?

/**
 * BLOCK: Laboratory Blocks Carousel
 */
import classNames from 'classnames';
import { Fragment } from 'react';
import CarouselOptions, {
  CarouselOptionAttributes,
} from './options';
import heightClass from './classes';

const { registerBlockType } = wp.blocks;
const { InspectorControls, InnerBlocks } = wp.editor;
const { select, dispatch } = wp.data;
const { __ } = wp.i18n;
const ALLOWED_BLOCKS = [];

registerBlockType('laboratory-blocks/carousel', {
  title: __('LB Carousel'),
  icon: 'slides',
  category: 'laboratory-blocks',
  description: __('A Bootstrap Carousel'),
  supports: { anchor: true },
  keywords: [
    __('Carousel'),
    __('Slider'),
  ],
  attributes: {
    ...CarouselOptionAttributes,
    test: {
      type: 'object',
    },
    clientId: {
      type: 'string',
      default: 'laboratory-carousel',
    },
  },

  edit(props) {
    const { setAttributes, isSelected } = props;
    const { clientId } = props;
    // find innerBlocks, add "active" to the first slide, set attribute slideLength
    const children = select('core/editor').getBlocksByClientId(clientId)[0].innerBlocks;
    const firstChild = children[0] || false;
    if (firstChild) {
      dispatch('core/editor').updateBlockAttributes(firstChild.clientId, { className: 'active' });
      setAttributes({ clientId });
    }

    return [
      !!isSelected && (
        <InspectorControls key="inspector">
          <CarouselOptions {...props} />
        </InspectorControls>
      ),
      <Fragment>
        <p>Laboratory Blocks Carousel:&nbsp;
          <small>Include a unique ID under &quot;Carousel Options&quot;</small>
        </p>
        <hr />
        <InnerBlocks allowedBlocks={ALLOWED_BLOCKS} />
      </Fragment>,
    ];
  },

  save(props) {
    const { className, attributes, innerBlocks } = props;
    const {
      carouselHeight,
      hasControls,
      hasIndicators,
      interval,
      pauseOnHover,
      carouselId,
    } = attributes;
    const height = heightClass(attributes);
    const classes = classNames(className, 'carousel', 'slide', height);
    const styles = carouselHeight ? { height: carouselHeight } : {};
    const timing = interval || false;
    const hover = pauseOnHover ? 'hover' : false;

    const containerTags = {
      ID: carouselId,
      className: classes,
      style: styles,
      'data-ride': 'carousel',
      'data-interval': timing,
      'data-pause': hover,
    };

    let Indicators;
    if (innerBlocks && 0 < innerBlocks.length) {
      Indicators = innerBlocks.map((block, i) => {
        const c = (0 === i) ? 'active' : '';
        return (
          <li
            key={`${carouselId}-trigger-${block.clientId}`}
            data-target={`#${carouselId}`}
            data-slide-to={i}
            className={c}
          />
        );
      });
    }

    return (
      <div {...containerTags}>
        <InnerBlocks.Content />
        {
          hasControls && (
            <Fragment>
              <a claclassName="carousel-control-prev" href={`#${carouselId}`} role="button" data-slide="prev">
                <span className="carousel-control-prev-icon" aria-hidden="true" />
                <span className="sr-only">Previous</span>
              </a>
              <a claclassName="carousel-control-prev" href={`#${carouselId}`} role="button" data-slide="next">
                <span className="carousel-control-next-icon" aria-hidden="true" />
                <span className="sr-only">Next</span>
              </a>
            </Fragment>
          )
        }
        {
          hasIndicators && (
            <ol className="carousel-indicators">
              {Indicators}
            </ol>
          )
        }
      </div>
    );
  },
});
like image 510
Ricardo Avatar asked Dec 17 '25 17:12

Ricardo


1 Answers

My solution:

  1. Move the indicators (the dots of the carousel) into a seperate component for code-reuse.
  2. Used it in both the edit and save methods.
  3. Saved the number of innerBlocks in the edit function and set it as an attribute to be used in the save function.

```

registerBlockType('laboratory-blocks/carousel', {...},
    edit: {
      const { clientId } = props;

      ...

      // find number of children and set it as an attribute
      const innerCount = select('core/editor').getBlocksByClientId(clientId)[0].innerBlocks.length;
      setAttributes({ clientId, innerCount });

      return [
        <div>
          ...
          <Indicators count={attributes.innerCount} clientId={clientId} />
        </div>
      ];
    }
    save: {
      const { clientId, attributes } = props;
      const { innerCount } = attributes;
      ...

      return [
        <div>
          ...
          <Indicators count={innerCount} clientId={clientId} />
        </div>
      ];
    }
}

```

like image 165
Ricardo Avatar answered Dec 20 '25 15:12

Ricardo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!