Drupal 9:在节点编辑页面上自动注入段落形式
前几天,我试图做一些我认为很简单的事情,但事实证明,要想真正做到这一点真的很困难。我有一个安装了Paragraphs的Drupal9网站,我希望用户单击节点编辑表单上的按钮,然后将一个特定的Paragraph注入到Paragraph字段中。
我找到了解决此问题的2种解决方案,它们以略有不同的方式解决了该问题。
ggy回现有事件
在最初尝试使此方法生效之后,我决定使用背负式方法。这实质上是在侦听用户交互,然后触发将段落插入字段中的“段落添加”事件。我正在听的用户交互是用户在选择列表中选择不同的元素。
为了使此工作正常进行,我在页面上添加了一些JavaScript,并将其附加到名为“field_type”的选择列表字段中。
function my_module_form_node_page_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state) { $form['field_type']['widget']['#attached']['library'][] = 'my_module/node'; }
这
node: js: js/piggy-back.js: {} dependencies: - core/drupal
这将加载到名为piggy-back.js的JavaScript文件中。
如下所示,该文件用于将附加到不同“段落”添加按钮的不同事件汇总在一起。当用户更改字段类型选择列表时,将触发一个事件,然后触发一个段落事件。
(function ($, Drupal, drupalSettings) { 'use strict'; Drupal.behaviors.bform = { attach : function(context, settings) { //加载现有的更改事件。 var paragraphDetailEvents = $._data($("#field-paragraph-detail-add-more")[0], "events"); var paragraphHeaderEvents = $._data($("#field-paragraph-header-add-more")[0], "events"); $('#edit-field-type').change(function(event) { let changeValue = $(this).val(); if (changeValue == 1) { $.each(paragraphDetailEvents.mousedown, function () { this.handler(event); }); } if (changeValue == 2) { $.each(paragraphHeaderEvents.mousedown, function () { this.handler(event); }); } }); } }; })(jQuery, Drupal);
您可能可以修改此代码以适合您的需求,但这是对实际产生预期结果的问题的简单解决方案。
注入段落按钮Ajax回调
尽管搭载方法有效,但我真正想做的是完全控制ajax流和所得到的响应。浏览“段落”模块使我想到了各种不同的代码,这些代码似乎并没有真正产生所需的结果。在运行了几次代码之后,我能够将所需的组件提取到单个模块中。
这里需要的第一件事是一个表单元素,以控制将Paragraph实体表单注入到页面中。这由提交按钮处理,该提交按钮使用hook_form_node_page_form_alter()挂钩插入到节点编辑表单中。该按钮被注入到名为“field_type”的字段旁边,但是我已经对此进行了抽象,以便您可以将其移动到所需的任何位置。段落字段名称还需要在顶部抽象为一个变量,因此您可以将其指向所需的任何段落字段。对“添加更多”字段和类名称的引用将与Paragraph表单本身集成在一起。
function my_module_form_node_page_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state) { $paragraphFieldName = 'field_paragraph'; $injectParagraphFieldName = 'field_type'; $paragraphField = NestedArray::getValue($form, [$paragraphFieldName]); $injectParagraphField = NestedArray::getValue($form, [$injectParagraphFieldName]); $injectParagraphField['inject_paragraph'] = [ '#type' => 'submit', '#name' => 'field_type_my_module', '#value' => t('Inject Paragraph'), '#attributes' => ['class' => ['field-set-selection-submit']], '#limit_validation_errors' => [array_merge($paragraphField['#parents'], [$paragraphFieldName, 'add_more'])], '#submit' => [['\Drupal\my_module\InjectParagraph', 'addParagraphSubmit']], '#ajax' => [ 'callback' => ['\Drupal\my_module\InjectParagraph', 'addParagraphAjax'], 'wrapper' => str_replace('_', '-', $paragraphFieldName) . '-add-more-wrapper', 'effect' => 'fade', 'progress' => [ 'type' => 'fullscreen', ] ], ]; NestedArray::setValue($form, [$injectParagraphFieldName], $injectParagraphField); }
该按钮将充当用户输入,并在元素周围包装Submit和ajax处理程序。为了使此提交按钮正常工作,需要三件事。
提交callack,在提交按钮后调用。在上面的字段中,已将其创建为静态方法,然后放入类中。
一个limit_validation_errors。顾名思义,这将限制表单将产生的验证错误的数量。在这种情况下,我们告诉Drupal仅验证段落字段中是否存在错误,并在表单的其余部分执行“部分提交”。
在提交处理程序之后,在提交按钮时调用的Ajax回调。同样,它已被抽象到一个单独的类中。
包含addParagraphSubmit和addParagraphAjax处理程序的类是执行操作的地方。这是完整的课程,我添加了很多评论以显示正在发生的事情。
setRebuild(); } public static function addParagraphAjax(array $form, FormStateInterface $form_state) { //设置有问题的段落字段。 $paragraphFieldName = 'field_paragraph'; //从表单中提取段落字段。 $element = NestedArray::getValue($form, [$paragraphFieldName, 'widget']); //使用所需的Ajax格式更新字段。 $delta = $element['#max_delta']; $element[$delta]['#prefix'] = '' . (isset($element[$delta]['#prefix']) ? $element[$delta]['#prefix'] : ''); $element[$delta]['#suffix'] = (isset($element[$delta]['#suffix']) ? $element[$delta]['#suffix'] : '') . ''; //从段落模块中清除添加更多增量。 NestedArray::setValue( $element, ['add_more', 'add_more_delta', '#value'], '' ); //返回段落元素。 return $element; } public static function getWidgetState(array $parents, $field_name, FormStateInterface $form_state) { return NestedArray::getValue($form_state->getStorage(), array_merge(['field_storage', '#parents'], $parents, ['#fields', $field_name])); } public static function setWidgetState(array $parents, $field_name, FormStateInterface $form_state, array $field_state) { NestedArray::setValue($form_state->getStorage(), array_merge(['field_storage', '#parents'], $parents, ['#fields', $field_name]), $field_state); } }
如果您熟悉“段落”模块,那么您会认出其中的一些代码,因为这些部分已从“段落”模块中获取。我需要复制和粘贴此处所需的一些代码,因为它们在Paragraph小部件中被设置为私有或受保护的方法,因此无法访问。我在这里省略了一些细节,因为代码只会将一种类型的Paragraph注入到表单中。但是,如果您要这样做的话,这应该是足够的信息,可以助您一臂之力。表单状态被传递给两个函数,因此您应该能够对其进行检查并查看已选择的内容,然后相应地修改代码。
本质上,事件的顺序如下。
用户单击节点表单上的新“插入段落”按钮。
对Drupal进行了ajax调用。
Drupal表单处理开始。
触发名为addParagraphSubmit的提交处理程序。在这一步中,我们使用具有“selected_bundle”属性的占位符小部件为要添加到表单中的段落实体注入占位符。我们添加的段落实体称为“详细信息”。
调用内部的Paragraph渲染函数,然后使用空白的Paragraph实体表单填充表单元素。
触发了名为addParagraphAjax的ajax处理程序。在此步骤中,将显示空的Paragraph实体表单,只是将其包装在某些HTML中,以便该表单的行为与普通的Paragraph表单相同。
如您所见,在这里既需要提交处理程序,也需要ajax处理程序,以便正确插入Paragraph实体表单。如果没有将提交处理程序称为ajax处理程序,则它什么也不会做。确保同时调用这两个处理程序,这取决于我们注入的表单元素中的limit_validation_errors设置。如果缺少此元素,则仅调用ajax处理程序,该过程太迟了,无法添加Paragraph实体。我以前没有使用过此设置,因此不确定它做了什么,因此我什至可以就此设置撰写一篇完全独立的文章,因为它极大地改变了ajax回调的工作方式。
经过数小时的调试,实验和调整,以上所有代码均已组合在一起。希望对您有用。