You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Gutenberg区块更新/发布后Toggle Control功能失效

Gutenberg区块Toggle Control保存后失效问题

功能正常场景

  • 添加新页面时
  • 添加新区块时
  • 开启Toggle更改区块布局时
  • 保存后区块在编辑器和前端的外观均正常变化

功能失效场景

  • 编辑已保存的同一区块时,Toggle显示为关闭状态,但之前的布局更改已保存到前端
  • 尝试再次切换Toggle开关时,无任何反应

相关代码

edit.js

import { __ } from '@wordpress/i18n';
import {
  RichText,
  InspectorControls,
  MediaUpload,
  useBlockProps,
  InnerBlocks,
} from '@wordpress/block-editor';
import { PanelBody, Button, ToggleControl } from '@wordpress/components';

const ALLOWED_BLOCKS = ['core/button'];

import './editor.scss';

const Edit = ({ attributes, setAttributes }) => {
  const { toggleOn, customClass, align } = attributes;

  const onToggleChange = (newValue) => {
    setAttributes({ toggleOn: newValue });
  };

  const onSelectBlockBackground = (newBlockBackground) => {
    setAttributes({
      blockBackground: newBlockBackground.sizes.full.url,
    });
  };

  const blockProps = useBlockProps({
    className: `wp-block-create-block-callout-block ${customClass} ${toggleOn ? 'toggle-on-horizontal-cta' : ''}`,
    'data-align': align,
  });

  return (
    <div {...blockProps}>
      <InspectorControls>
        <PanelBody title={__('Toggle on horizontal or vertical layout')}>
          <ToggleControl
            label={__('Toggle On')}
            checked={toggleOn}
            onChange={onToggleChange}
          />
        </PanelBody>
        <PanelBody title={__('Select Background Image')}>
          <p>
            <strong>{__('Select a Background Image:')}</strong>
          </p>
          <MediaUpload
            onSelect={onSelectBlockBackground}
            type="image"
            value={attributes.blockBackground.url}
            render={({ open }) => (
              <Button
                className="editor-media-placeholder__button is-button is-default is-large"
                icon="upload"
                onClick={open}
              >
                {__('Select Image')}
              </Button>
            )}
          />
        </PanelBody>
      </InspectorControls>

      <div
        className={`callout-cta`}
        style={{
          backgroundImage: attributes.blockBackground !== '' ? `url("${attributes.blockBackground}")` : 'none',
          backgroundSize: 'cover',
          backgroundPosition: 'center',
          backgroundRepeat: 'no-repeat',
        }}
      >
        <RichText
          tagName="h2"
          value={attributes.mainHeading}
          onChange={(mainHeading) => setAttributes({ mainHeading })}
          placeholder={__('Main Heading')}
          className="main-heading"
        />
        <RichText
          tagName="p"
          value={attributes.mainContent}
          onChange={(mainContent) => setAttributes({ mainContent })}
          placeholder={__('Main Content')}
          className="main-content"
        />
        <InnerBlocks allowedBlocks={ALLOWED_BLOCKS} />
      </div>
    </div>
  );
};

export default Edit;

index.js

import { registerBlockType } from '@wordpress/blocks';
import { __ } from '@wordpress/i18n';
import { RichText, InspectorControls, MediaUpload, useBlockProps } from '@wordpress/block-editor';
import { PanelBody, Button } from '@wordpress/components';
import './style.scss';
import Edit from './edit';
import save from './save';

const validAlignments = ['full'];

registerBlockType('create-block/callout-block', {
    title: __('Callout Block', 'callout-block'),
    supports: {
        html: false,
        align: true,
    },
    attributes: {
        mainHeading: {
            type: 'string',
            source: 'html',
            selector: '.main-heading',
        },
        mainContent: {
            type: 'string',
            source: 'html',
            selector: '.main-content',
        },
        blockBackground: {
            type: 'string',
            default: '',
        },
        align: {
            type: 'string',
            default: 'full',
        },
        toggleOn: {
          type: 'boolean',
          default: false,
          source: 'attribute',
          attribute: 'data-toggle-on', // Use the attribute name that corresponds to the class you're toggling
        },
    },
    getEditWrapperProps( attributes ) {
      const { align } = attributes;
      if (-1 !== validAlignments.indexOf( align ) ) {
        return { 'data-align': align };
      }
    },
    edit: Edit,
    save,
});

save.js

import { __ } from '@wordpress/i18n';
import {
  RichText,
  InspectorControls,
  MediaUpload,
  useBlockProps,
  InnerBlocks,
} from '@wordpress/block-editor';
import { PanelBody, Button } from '@wordpress/components';

export default function save({ className, attributes }) {
  const { toggleOn } = attributes;

  const blockProps = useBlockProps.save({
    'data-align': attributes.align, // Add the data-align attribute with the align value
    className: `wp-block-create-block-callout-block ${className} ${toggleOn ? 'toggle-on-horizontal-cta' : ''}`,
  });

  return (
    <div className={`wp-block-create-block-callout-block ${className} ${toggleOn ? 'toggle-on-horizontal-cta' : ''}`} {...blockProps}>
      <div
        className="callout-cta"
        style={{
          backgroundImage: attributes.blockBackground !== '' ? `url("${attributes.blockBackground}")` : 'none',
          backgroundSize: 'cover',
          backgroundPosition: 'center',
          backgroundRepeat: 'no-repeat',
        }}
      >
        <RichText.Content
          tagName="h2"
          value={attributes.mainHeading}
          className="main-heading"
        />
        <RichText.Content
          tagName="p"
          value={attributes.mainContent}
          className="main-content"
        />
        <InnerBlocks.Content {...blockProps} />
      </div>
    </div>
  );
}

问题根源

问题出在toggleOn属性的配置上:

  • 在index.js中给toggleOn设置了source: 'attribute'和attribute: 'data-toggle-on',意味着Gutenberg会从保存的HTML元素的data-toggle-on属性中读取该值,但save.js里并没有在根元素上添加这个属性,导致编辑器重新加载时无法读取到之前保存的toggleOn值,只能使用默认的false。
  • 同时,由于source设置为attribute,当尝试通过setAttributes更新toggleOn时,Gutenberg会试图同步到DOM的对应属性,但DOM中不存在该属性,导致更新失效,开关无法正常切换。

解决方案

1. 修改index.js中的toggleOn属性定义

移除source和attribute配置,让Gutenberg将该属性存储在区块内部数据中(而非从HTML提取):

toggleOn: {
  type: 'boolean',
  default: false,
  // 移除source和attribute配置
},

2. 优化save.js的代码

  • 移除重复的className定义(useBlockProps.save会自动添加区块默认className)
  • 简化InnerBlocks.Content的调用(不需要传递blockProps):
import { __ } from '@wordpress/i18n';
import {
  RichText,
  useBlockProps,
  InnerBlocks,
} from '@wordpress/block-editor';

export default function save({ attributes }) {
  const { toggleOn } = attributes;

  const blockProps = useBlockProps.save({
    'data-align': attributes.align,
    className: toggleOn ? 'toggle-on-horizontal-cta' : '',
  });

  return (
    <div {...blockProps}>
      <div
        className="callout-cta"
        style={{
          backgroundImage: attributes.blockBackground !== '' ? `url("${attributes.blockBackground}")` : 'none',
          backgroundSize: 'cover',
          backgroundPosition: 'center',
          backgroundRepeat: 'no-repeat',
        }}
      >
        <RichText.Content
          tagName="h2"
          value={attributes.mainHeading}
          className="main-heading"
        />
        <RichText.Content
          tagName="p"
          value={attributes.mainContent}
          className="main-content"
        />
        <InnerBlocks.Content />
      </div>
    </div>
  );
}

3. 优化edit.js中的blockProps

移除手动添加的区块默认className(useBlockProps会自动处理):

const blockProps = useBlockProps({
  className: `${customClass} ${toggleOn ? 'toggle-on-horizontal-cta' : ''}`,
  'data-align': align,
});

效果验证

修改后重新保存区块,再次编辑时:

  • Toggle会正确显示之前保存的状态
  • 开关可以正常切换,布局会实时更新,且保存后状态不会丢失

内容的提问来源于stack exchange,提问作者Chris Brennan

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 21:38:08