最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Serialize custom block with InnerBlock

programmeradmin0浏览0评论

I have a custom gutenberg block with arbitrary InnerBlocks. For reasons of a custom javascript library I am using I cannot use the <InnerBlocks> component but want to iterate over all blocks and display the blocks with their InnerBlocks in my block's edit function.

I want to write a slider block with the swiper library. My efforts to really make the InnerBlocks (with Toolbars and other UI) work did not work out well. First, swiper needs a special DOM structure, that I could not get with the InnerBlocks component as it has a predifined wrapper and div structure. Second, swiper clones slides when you loop it, which eventually caused react errors when the whole gutenberg block ui was cloned. So what I figured out to work best is to have a button in my block, that switches between "edit mode" and "preview mode", where you see the InnerBlocks with all the UI for adding, moving in edit, and where you see the slider in preview mode.

I am using the code (simplified):

{ innerBlocks.map( ( block ) => {
    return (
        <div
            key={ block.clientId }
            dangerouslySetInnerHTML={ { __html: wp.blocks.getBlockContent( block ) } }
        >
        </div>
    );
} ) }

This works for most innerBlocks like core/heading, core/paragraph, ... But for embeds e.g. it just displays the embed url but not the embedded media content, e.g. from Youtube. I found wp.blocks.serialize and wp.blocks.rawHandler but cannot get it to work either. What am I doing wrong.

I am looking for answers that make embeds work without <InnerBlocks> or for a solution that works with swiper (loop enabled) and <InnerBlocks>.

I have a custom gutenberg block with arbitrary InnerBlocks. For reasons of a custom javascript library I am using I cannot use the <InnerBlocks> component but want to iterate over all blocks and display the blocks with their InnerBlocks in my block's edit function.

I want to write a slider block with the swiper library. My efforts to really make the InnerBlocks (with Toolbars and other UI) work did not work out well. First, swiper needs a special DOM structure, that I could not get with the InnerBlocks component as it has a predifined wrapper and div structure. Second, swiper clones slides when you loop it, which eventually caused react errors when the whole gutenberg block ui was cloned. So what I figured out to work best is to have a button in my block, that switches between "edit mode" and "preview mode", where you see the InnerBlocks with all the UI for adding, moving in edit, and where you see the slider in preview mode.

I am using the code (simplified):

{ innerBlocks.map( ( block ) => {
    return (
        <div
            key={ block.clientId }
            dangerouslySetInnerHTML={ { __html: wp.blocks.getBlockContent( block ) } }
        >
        </div>
    );
} ) }

This works for most innerBlocks like core/heading, core/paragraph, ... But for embeds e.g. it just displays the embed url but not the embedded media content, e.g. from Youtube. I found wp.blocks.serialize and wp.blocks.rawHandler but cannot get it to work either. What am I doing wrong.

I am looking for answers that make embeds work without <InnerBlocks> or for a solution that works with swiper (loop enabled) and <InnerBlocks>.

Share Improve this question edited Jan 10, 2021 at 21:30 user9 asked Jan 8, 2021 at 19:33 user9user9 1644 silver badges17 bronze badges 11
  • 1 This won't work as those blocks won't be interactive or have their hooks fire. You'll also get their saved content rather than their edit components. This approach is a dead end. Can you share your original problem? There's a good chance that your problem can be either solved, or sidestepped, or that a misunderstanding has been made and you can achieve your goal by other means. Additionally, expand your code example to include the entire component – Tom J Nowell Commented Jan 8, 2021 at 20:11
  • @TomJNowell thanks for the answer, I extended my question. I do not mind that being a "dead end" in that way the user cannot edit blocks anymore, because as I described I have that edit mode available. My problem is really just the hooks(?) not being fired if that is what makes embeds work and I wonder how that would be possible. It is also a "dynamic block" with a php frontend, but I figured it was way faster and more performant to not use a <ServerSideRender> in edit, or would that be the only way? – user9 Commented Jan 9, 2021 at 11:39
  • I do not believe that is true, that library has react support swiperjs/react all you need to do is wrap the innerblocks component in a <Swiper> component, then wrap the children in <SwiperSlide> components. You can do this easily with a Swiper block and a Swiper Slide block, much like how core has a columns and a column block. Do that and your problem and all the workarounds and hacks disappear into thin air, it even provides a nice way to add new slides as you can make sure an inserter is always showing, and your inner blocks are always editable live – Tom J Nowell Commented Jan 9, 2021 at 23:34
  • @TomJNowell I tried that and alot of other approaches. The Problem with what you suggest is, that if I do <Swiper><InnerBlocks></Swiper> where all inner blocks are wrapped in <SwiperSlide>, the innerblocks add two wrapper divs which make swiper unusable. Also I tried the new useInnerBlocksProps hook, to get rid of the wrapper divs however then the parent <Swiper> does not recognize it SwiperSlides. For me it ends up in trying it the way I am asking for above. – user9 Commented Jan 10, 2021 at 16:02
  • If each inner block is a slide then that is not the appropriate method of doing that. You cannot have just a swiper block, you need a swiper slide block, as well as a prop to restrict the inner blocks to only be swiper slide blocks, in the same way that the columns block doesn't just wrap the contents, it instead has individual column blocks. This can not and should not be done with a single block – Tom J Nowell Commented Jan 10, 2021 at 17:29
 |  Show 6 more comments

1 Answer 1

Reset to default 6 +200

What you asked for is not possible for blocks that rely on frontend javascript, this includes embeds.


It's also not possible using the Swiper react library. The Swiper library made the questionable choice of manually inspecting child components for the SwiperSlide component name, preventing it from ever being used with child blocks.

Edit: Actually maybe it is! Since writing this, a PR was merged that may fix this and allow the code below to work as desired


It isn't possible using jQuery either with inner blocks as it conflicts with the way React works, and will cause virtual DOM problems.

The Closest You Can Get

Instead, if we use the React Swiper library, useBlockProps, and useInnerBlocksProps, we can implement this, and have editable slides, while keeping the slider.

However, Swiper will not see the specific arrangement of components it needs and will place them after the slider container instead of inside the wrapper.

The important thing to note, is this snippet:

        const blockProps = useBlockProps();
        const innerBlocksProps = useInnerBlocksProps( blockProps, {
            allowedBlocks: [ 'tomjn/swiper-slide' ],
            template: TEMPLATE,
        });
        const params = {...};
        return (
            <div { ...innerBlocksProps }>
                <Swiper { ...params }>
                    { innerBlocksProps.children }
                </Swiper>
            </div>
        );

By using innerBlocksProps.children we eliminate the 2 containing <div> tags.

Here's my demo:

src/index.js:

import { registerBlockType } from '@wordpress/blocks';
import {
    InnerBlocks,
    useBlockProps,
    __experimentalUseInnerBlocksProps as useInnerBlocksProps
} from '@wordpress/block-editor';

import SwiperCore, { Navigation, Pagination, Scrollbar, A11y } from 'swiper';
import { Swiper, SwiperSlide } from 'swiper/react';

// Import Swiper styles
import 'swiper/swiper-bundle.css';

// install Swiper components
SwiperCore.use([Navigation, Pagination, Scrollbar, A11y]);

const TEMPLATE = [
    [ 'tomjn/swiper-slide', {}, [
        [ 'core/paragraph', { placeholder: 'Enter slide content...' } ],
    ] ],
    [ 'tomjn/swiper-slide', {}, [
        [ 'core/paragraph', { placeholder: 'Enter slide content...' } ],
    ] ]
];

registerBlockType( 'tomjn/swiper', {
    apiVersion: 2,
    title: 'Swiper',
    category: 'layout',
    edit: () => {
        const blockProps = useBlockProps();
        const innerBlocksProps = useInnerBlocksProps( blockProps, {
            allowedBlocks: [ 'tomjn/swiper-slide' ],
            template: TEMPLATE,
        });
        const params = {
            init: true,
            paceBetween: 50,
            slidesPerView: 2,
            loop: true,
            navigation: {
                clickable: true,
            },
            spaceBetween: 30,
            pagination: {
                clickable: true,
            }
        };
        return (
            <div { ...innerBlocksProps }>
                <Swiper { ...params }>
                    { innerBlocksProps.children }
                </Swiper>
            </div>
        );
    },
    save: () => {
        return (
            <div className="swiper-container">
                <div className="swiper-wrapper">
                    <InnerBlocks.Content />
                </div>
                <div className="swiper-pagination"></div>
                <div className="swiper-button-prev"></div>
                <div className="swiper-button-next"></div>
            </div>
        );
    },
} );

registerBlockType( 'tomjn/swiper-slide', {
    apiVersion: 2,
    title: 'Swiper Slide',
    category: 'layout',
    edit: () => {
        const blockProps = useBlockProps();
        return (
            <SwiperSlide>
                <div {...blockProps}>
                    <InnerBlocks />
                </div>
            </SwiperSlide>
        );
    },
    save: () => {
        return (
            <div class="swiper-slide">
                <InnerBlocks.Content />
            </div>
        );
    },
} );

package.json:

{
    "dependencies": {
        "swiper": "^6.4.5"
    },
    "devDependencies": {
        "@wordpress/scripts": "^12.6.1"
    },
    "scripts": {
        "build": "wp-scripts build",
        "check-engines": "wp-scripts check-engines",
        "check-licenses": "wp-scripts check-licenses",
        "format:js": "wp-scripts format-js",
        "lint:css": "wp-scripts lint-style",
        "lint:js": "wp-scripts lint-js",
        "lint:md:docs": "wp-scripts lint-md-docs",
        "lint:md:js": "wp-scripts lint-md-js",
        "lint:pkg-json": "wp-scripts lint-pkg-json",
        "packages-update": "wp-scripts packages-update",
        "start": "wp-scripts start",
        "test:e2e": "wp-scripts test-e2e",
        "test:unit": "wp-scripts test-unit-js"
    }
}

plugin.php:

<?php
/**
 * Plugin Name: Toms Swiper Plugin
 * Plugin URI: https://tomjn
 * Description: Swiper no swiping.
 * Requires at least: 5.5
 * Requires PHP: 7.3
 * Version: 1.0.0
 * Author: Tom J Nowell
 */

defined( 'ABSPATH' ) || exit;

add_action(
    'enqueue_block_editor_assets',
    function() {
        $asset = include plugin_dir_path( __FILE__ ) . 'build/index.asset.php';

        wp_register_script(
            'tomjn-swiper-block-js',
            plugins_url( 'build/index.js', __FILE__ ),
            $asset['dependencies'],
            $asset['version'],
        );
        wp_register_style(
            'tomjn-swiper-block-css',
            plugins_url( 'build/index.css', __FILE__ ),
            [],
            $asset['version'],
        );
    }
);

/**
 * Registers all block assets so that they can be enqueued through Gutenberg in
 * the corresponding context.
 */
function tomjn_swiper_register_block() {

    if ( ! function_exists( 'register_block_type' ) ) {
        // Gutenberg is not active.
        return;
    }

    register_block_type(
        'tomjn/swiper',
        [
            'editor_script' => 'tomjn-swiper-block-js',
            'editor_style'  => 'tomjn-swiper-block-css',
        ]
    );
    register_block_type(
        'tomjn/swiperslide',
        [
            'editor_script' => 'tomjn-swiper-block-js',
            'editor_style'  => 'tomjn-swiper-block-css',
        ]
    );

}
add_action( 'init', 'tomjn_swiper_register_block' );

发布评论

评论列表(0)

  1. 暂无评论