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

Drupal 9中如何移除节点添加表单的首个初始段落元素?

Drupal 9 段落字段动态必填与添加按钮修复方案

核心问题分析

直接移除段落字段的初始空项会破坏Drupal段落组件的JS逻辑,导致"添加更多"按钮依赖的DOM节点丢失,从而按钮消失。正确的思路是隐藏初始空段落而非删除,同时通过前后端配合实现动态必填规则。

具体实现步骤

1. 在form_alter中隐藏初始空段落并调整字段设置

通过hook_form_alter修改节点表单,隐藏初始空段落,移除字段的强制必填属性,同时挂载自定义JS控制逻辑:

function yourmodule_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  // 替换为你的节点表单ID和段落字段机器名
  if ($form_id === 'node_article_form' && isset($form['field_my_paragraph'])) {
    $widget = &$form['field_my_paragraph']['widget'];
    
    // 隐藏初始空段落项,而非直接删除
    if (!empty($widget[0]) && empty($widget[0]['#default_value'])) {
      $widget[0]['#attributes']['class'][] = 'visually-hidden';
      $widget[0]['#attributes']['data-hidden-initial'] = 'true';
    }
    
    // 移除段落字段的全局必填设置,交给动态逻辑控制
    unset($form['field_my_paragraph']['#required']);
    
    // 挂载自定义JS库
    $form['#attached']['library'][] = 'yourmodule/paragraph-dynamic-handler';
  }
}

2. 自定义JS实现动态必填与按钮显示控制

编写JS监听段落内目标字段的输入状态,动态切换必填属性,同时确保"添加更多"按钮始终可见:

首先在yourmodule.libraries.yml中定义库:

paragraph-dynamic-handler:
  js:
    js/paragraph-dynamic.js: {}
  dependencies:
    - core/jquery
    - core/drupal.behaviors

然后创建js/paragraph-dynamic.js文件:

(function ($, Drupal) {
  Drupal.behaviors.paragraphDynamicHandler = {
    attach: function (context, settings) {
      const $paragraphField = $('.field--name-field-my-paragraph', context);
      const $addBtn = $paragraphField.find('.paragraphs-add-more');
      const $initialPara = $paragraphField.find('[data-hidden-initial="true"]');
      const targetFieldClass = '.field--name-field_target_in_paragraph input';

      // 确保添加按钮始终显示
      $addBtn.show();

      // 监听目标字段输入,动态切换必填状态
      $paragraphField.on('input', targetFieldClass, function () {
        const $currentPara = $(this).closest('.paragraphs-item');
        const isTargetFilled = $(this).val().trim() !== '';
        const hasFilledTargets = $paragraphField.find(targetFieldClass).filter(el => $(el).val().trim() !== '').length > 0;

        // 控制段落字段的全局必填
        if (hasFilledTargets) {
          $paragraphField.attr('required', 'required');
          $currentPara.find('[required]').attr('required', 'required');
        } else {
          $paragraphField.removeAttr('required');
          $paragraphField.find('[required]').removeAttr('required');
        }
      });

      // 添加新段落时,默认移除内部字段的必填属性
      $addBtn.on('click', function () {
        setTimeout(() => {
          $paragraphField.find('.paragraphs-item:last [required]').removeAttr('required');
        }, 500);
      });

      // 初始化检查:如果初始段落已有内容,取消隐藏并设置必填
      if ($initialPara.find(targetFieldClass).val().trim() !== '') {
        $initialPara.removeClass('visually-hidden').removeAttr('data-hidden-initial');
        $paragraphField.attr('required', 'required');
      }
    }
  };
})(jQuery, Drupal);

3. 后端验证确保数据合法性

前端控制可被绕过,必须通过后端验证保障逻辑:

function yourmodule_form_node_article_form_validate(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  $paraValues = $form_state->getValue('field_my_paragraph');
  $hasValidPara = FALSE;

  foreach ($paraValues as $delta => $paraData) {
    // 跳过空段落项
    if (empty($paraData['field_target_in_paragraph'][0]['value'])) continue;
    
    $hasValidPara = TRUE;
    // 验证段落内其他必填字段(如果有)
    if (empty($paraData['field_other_required'][0]['value'])) {
      $form_state->setErrorByName(
        "field_my_paragraph][$delta][field_other_required",
        t('请填写段落内的[其他必填字段]')
      );
    }
  }

  // 如果有目标字段已填写,但无有效段落数据,抛出错误
  if ($hasValidPara && empty(array_filter($paraValues))) {
    $form_state->setErrorByName('field_my_paragraph', t('请完成段落内容的填写'));
  }
}

关键注意事项

  • 替换代码中所有字段机器名、内容类型ID为项目实际值
  • 使用Drupal自带的visually-hidden类隐藏元素,保障可访问性
  • 务必覆盖所有测试场景:初始状态、添加段落、填写/清空目标字段、表单提交等

内容的提问来源于stack exchange,提问作者Julio Alves

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.21 08:54:24