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
相关产品推荐
相关产品推荐

