Drupal 11使用HTMX创建节点显示模式预览表单

Drupal 11:使用 HTMX 的节点显示模式预览表单

这是探讨 Drupal 中 HTMX 的系列文章的第五部分,相关文章列表会在本文末尾列出。

我在思考如何在 Drupal 中演示 HTMX 时,实现了无限滚动、标签式界面和级联选择表单等功能,基本是在 Drupal 模块开发中重现了我在非 Drupal 的 HTMX 里做过的事。

后来我有了个想法,创建一个对 Drupal 开发者日常工作可能真正有用的东西,也就是以不同视图模式显示节点。

本文将探讨创建一个简单表单,允许用户输入节点 ID 和视图模式,查看以该视图模式渲染的节点。

本文包含的所有代码都能找到,这里会详细介绍代码功能以及它为生成内容所做的操作。

和其他关于 HTMX 的文章一样,我从定义路由开始。

一、路由

这里所需的路由只需将路径 /htmx-examples/display-mode-preview 指向我们的表单类。

  
    drupal_htmx_examples_display_mode_preview_form:
      path: "/htmx-examples/display-mode-preview"
      defaults:
        _form: '\Drupal\drupal_htmx_examples\Form\DisplayModePreviewForm'
        _title: "HTMX 显示模式预览表单"
      requirements:
        _permission: "access content"
  

这个路由没什么特别的,就是个常规的表单路由。接下来为这个路由创建表单。

二、表单

表单类有几个注入的依赖项,如下:

  • entity_type.manager - 用于从数据库中加载节点,并加载视图构建器以渲染节点。
  • entity_display.repository - 用于加载节点可用的视图模式列表。

以下是表单的骨架,buildForm() 方法稍后介绍。

  
    entityTypeManager = $container->get('entity_type.manager');
        $instance->entityDisplayRepository = $container->get('entity_display.repository');
        return $instance;
      }

      public function getFormId() {
        return 'display_mode_preview_form';
      }

      public function submitForm(array &$form, FormStateInterface $form_state): void {
        // 不做任何操作。
      }

    }
  

buildForm() 方法较复杂,本质上要完成以下操作:

  • 接受表单中的用户输入(未设置输入时,设置合理默认值)。
  • 将节点字段设为数字字段。
  • 获取实体类型的可用视图模式列表,用该列表创建选择元素,供用户选择视图模式。
  • 用户选择节点后,从数据库中加载该节点。
  • 用实体类型的视图构建器为所选视图模式(默认为“full”)中的节点生成渲染数组。
  • 创建 ID 为“node-preview-output”的 HTML div 元素,将渲染数组作为子元素传入,让渲染器在 div 元素内渲染节点渲染数组。

以下是表单操作的代码。

  
    public function buildForm(array $form, FormStateInterface $form_state) {
      // 获取用户输入(如果设置了),并设置一些合理的默认值。
      $nid = $form_state->getValue('node_id', '');
      $viewMode = $form_state->getValue('view_mode', 'full');

      // 节点 ID 输入字段。
      $form['node_id'] = [
        '#type' => 'number',
        '#title' => $this->t('节点 ID'),
        '#default_value' => $nid,
      ];

      // 加载节点的视图模式。
      $viewModes = $this->entityDisplayRepository->getViewModes('node');
      $viewModesSelection = [];
      foreach ($viewModes as $id => $mode) {
        $viewModesSelection[$id] = $mode['label'];
      }

      // 创建视图模式选择字段。
      $form['view_mode'] = [
        '#title' => $this->t('视图模式'),
        '#type' => 'select',
        '#empty_option' => $this->t('- 选择 -'),
        '#options' => $viewModesSelection,
        '#default_value' => $viewMode,
      ];

      // 为节点输出创建一个占位符。
      $form['output'] = [
        '#markup' => '
', ]; if ($nid === '') { // 如果未设置节点 ID,则在此返回。 return $form; } // 使用输入的 ID 加载节点。 $nodeStorage = $this->entityTypeManager->getStorage('node'); $node = $nodeStorage->load($nid); if (!$node) { // 如果没有找到节点(用户可能输入了一个不存在的节点 ID),则在此返回一个简单的消息。 $form['output'] = [ '#type' => 'html_tag', '#tag' => 'div', '#value' => $this->t('未找到节点'), '#attributes' => [ 'id' => 'node-preview-output', ], ]; return $form; } // 如果有节点,则使用所选视图模式渲染它,并将其注入到输出表单元素的 'children' 元素中。 // 这样,在渲染表单元素时,节点也会被渲染。 $viewBuilder = $this->entityTypeManager->getViewBuilder('node'); $renderArray = $viewBuilder->view($node, $viewMode); $form['output'] = [ '#type' => 'html_tag', '#tag' => 'div', '#attributes' => [ 'id' => 'node-preview-output', ], 'children' => $renderArray, ]; return $form; }

ID 为 node-preview-output 的 div 元素在表单逻辑中添加了三次,但任何时候只渲染其中一个。这个元素用于在用户与表单交互时,将表单内容注入页面。默认页面加载时,该元素只是空白占位符。

若没有 HTMX,这个表单什么也做不了,submitForm() 方法无需做任何事,因为所有操作都由 HTMX 处理,甚至没有提交处理程序。接下来给表单添加 HTMX 功能。

三、添加 HTMX

为添加交互性,需将 HTMX 添加到 node_idview_mode 字段中。

从 node_id 字段开始,为该字段添加以下 HTMX 设置。

  
    $form['node_id'] = [
      '#type' => 'number',
      '#title' => $this->t('节点 ID'),
      '#default_value' => $nid,
    ];

    (new Htmx())
      ->post()
      ->trigger('input delay:0.5s')
      ->target('#node-preview-output')
      // 选择是必需的,以便从我们的响应中挑选出正确的元素,响应将包含整个表单。
      ->select('#node-preview-output')
      ->swap('outerHTML')
      ->applyTo($form['node_id']);
  

这会为表单添加以下 HTMX 属性。

  • data-hx-post - 向表单发送 POST 请求。因未给该方法加参数,会自动使用当前路由。
  • data-tx-trigger - 告诉 HTMX 用户在字段输入内容时向表单发请求,发请求前有 0.5 秒延迟。
  • data-hx-target - 告知 HTMX 将响应提取的元素放哪里。这里将响应目标设为 ID 为 node-preview-output 的 div 元素。
  • data-hx-select - 在 Drupal 处理 HTMX 表单时必不可少。之前文章提到,发 HTMX 请求会从服务器得到整个表单,所以要告诉 HTMX 从响应中挑出所需元素。这里用选择器字符串 "#node-preview-output",让 HTMX 找含渲染节点的该 ID 元素。
  • data-hs-swap - 将交换策略设为 outerHTML,让 HTMX 用响应所选值完全替换目标元素。

view_mode 字段用以下 HTMX 设置。

  
    $form['view_mode'] = [
      '#title' => $this->t('视图模式'),
      '#type' => 'select',
      '#empty_option' => $this->t('- 选择 -'),
      '#options' => $viewModesSelection,
      '#default_value' => $viewMode,
    ];

    (new Htmx())
      ->post()
      ->target('#node-preview-output')
      // 选择是必需的,以便从我们的响应中挑选出正确的元素,响应将包含整个表单。
      ->select('#node-preview-output')
      ->swap('outerHTML')
      ->applyTo($form['view_mode']);
  

这会为该字段添加类似的 HTMX 属性集合。这里没添加触发属性,因为该元素是选择字段,值更改时有触发机制。

本质上是让 HTMX 监听这两个字段,向表单发请求,将 ID 为 node-preview-output 的 div 元素替换为渲染响应中相同元素的内容。

四、HTMX 工作流程

HTMX 元素的工作方式如下。

为便于阅读和理解,简化并删除了不少标记。

这里 HTMX 工作流程很简单,从表单的三个元素(两个表单元素和一个 div 元素)开始。

  
    

用户与表单交互,如输入节点 ID 42 和视图模式“摘要”,HTMX 会向服务器发请求,用该信息重新渲染表单。返回的响应含整个页面及新构建表单,从中提取 node-preview-output 元素,替换页面上相同 ID 的元素。

由于 HTMX 在 Drupal 中的工作方式,div 元素中的节点会用正确的 CSS 完全渲染。核心 HTMX 库中有个名为 htmx-assets.js 的文件,用于从响应中提取 CSS 和 JavaScript 文件并添加到当前页面头部。意味着即使节点用原始页面请求中不存在的单个目录组件渲染,整个节点也能正确渲染。

以下是该表单使用时的演示,加载了节点预览。

在 Drupal 中使用 HTMX 时,掌握表单处理中的选择和交换组合很重要。这里从我公司之前文章中复制在 Drupal 中构建 HTMX 表单时必须记住的两点。

  • Drupal 的响应总是包含整个表单,所以必须从表单响应中挑选出想要的元素。不这样做会看到表单出现在表单内部,比较混乱。
  • 表单必须有一致的状态。即不能随意向表单添加元素就期望它正常工作。无论有无 HTMX 请求,表单状态都要以相同方式重新构建表单。从表单提交流程看,表单先构建再提交,若元素在构建步骤不存在,就不会成为提交处理程序中提交值的一部分。

这个表单的优点是,渲染后的表单也适用于单个目录组件。我公司已在几个网站上测试,它通过 HTMX 回调在任何视图模式下渲染任何节点都表现良好,在 Drupal 开发和 Drupal 升级过程中都能发挥不错的作用。对于 Drupal11 版本,同样具有实用性。

实际上,这几乎能成为一个独立模块。若你认为它足够有用,可成为完整模块,请联系我公司,我公司会创建这个项目。