【问题标题】:How to pass parameter to ajax callback from hook_form_alter in Drupal 8如何从 Drupal 8 中的 hook_form_alter 将参数传递给 ajax 回调
【发布时间】:2020-05-07 21:11:53
【问题描述】:

我正在尝试实现一个hook_form_alter方法,通过ajax回调在节点的表单显示中修改一个字段的行为。

这个想法是当我从选择列表字段(field_country)中选择一个选项时,修改其他字段列表(field_laws)的值。具体来说,当我选择一个国家时,钩子方法通过ajax回调将此值(当前)传递给changeLawsData。此回调获取一个外部服务,该服务返回一组由先前选择的国家/地区过滤的值。

问题出在回调方法内部,我无法访问包含先前 hook_form_alter 的 $form 和 $form_state 对象。

我的问题是:是否可以通过参数将此对象传递给回调?有了这个,我可以处理表单的状态及其字段,例如。

类似这样的:

    $form['field_country']['widget']['#ajax'] = array(
        'callback' => [$this,'changeLawsData'],
        'event' => 'change',
        'disable-refocus' => FALSE,
        **'arguments' = array($form, $form_state)**
      );

这里是这个实现的完整代码。

<?php

namespace Drupal\obs_urban_system\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\hook_event_dispatcher\HookEventDispatcherInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
/**
 * Our event subscriber class.
 */
class NodeUrbanSystemFormAlterEventSubscriber implements EventSubscriberInterface {
    public static function getSubscribedEvents() {
        return [
            HookEventDispatcherInterface::FORM_ALTER => 'hookFormAlter'
        ];
    }

    /**
     * Implements hook_form_alter
     */
    public function hookFormAlter($event) {

        if($event->getFormId() == 'node_urban_system_edit_form') {
            $form = $event->getForm();
            $country = $form['field_country']['widget']['#default_value'];
            $form['field_laws']['widget'][0]['value']['#options'] = \Drupal::service('custom_services.law')->getLawsByContent($country, 'country');
            $form['field_law_articles']['widget'][0]['value']['#options'] = \Drupal::service('custom_services.law')->getLawArticlesByCountry($country);
            $form['field_country']['widget']['#ajax'] = array(
                'callback' => [$this,'changeLawsData'],
                'event' => 'change',
                'disable-refocus' => FALSE
              );
            $event->setForm($form);
        }
    }

    /**
     * @param $form
     * @param \Drupal\Core\Form\FormStateInterface $form_state
     * @return \Drupal\Core\Ajax\AjaxResponse
     */
    function changeLawsData(&$form, FormStateInterface $form_state) {
<!--- HERE IM USING THE $form object --->
        $country = $form['field_country']['widget']['#default_value'];
<!---                                --->
        $laws = \Drupal::service('custom_services.law')->getLawsByContent($country, 'country');

        foreach ($laws as $key => $value) {
            $option .= "<option value='" . $key . "'>" . $value . " </option>";
        }

        $response = new AjaxResponse();
        $response->addCommand(new HtmlCommand('#edit-field-laws-0-value', $option));
        return $response;
    }

}

非常感谢大家。

【问题讨论】:

  • TLDR:基本上,所有的表单操作都应该在 form_alter 函数中进行。我从来没有见过像你这样以 OO 方式实现的 form_alter ......但通常,当想要根据提交的 ajax 值向表单添加项目时,你会在 form_alter 中完成所有操作。当 ajax 被提交时,表单会被重建,所以在 form_alter 函数中,您将检查提交的值并根据需要更改表单。您的 ajax 响应应该只返回这个新的表单项(或您的情况下的命令)。
  • 另外,您为什么要通过事件订阅者实现您的方式?像 D7 中的简单 hook_form_alter 函数在 D8 中仍然有效。编辑...啊,我看到你正在使用Hook Event Dispatcher 模块。

标签: drupal drupal-8 drupal-forms drupal-hooks


【解决方案1】:

您需要在 form_alter 中完成所有表单操作。
当 ajax 回调被触发时,表单将被重建并且表单的当前值将在 form_state 中可用。
您的 ajax 回调应该只返回前端需要的内容,它不应该实际操作表单数组。

这是您的代码示例(仅作为示例,未经测试)

<?php

namespace Drupal\obs_urban_system\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Drupal\hook_event_dispatcher\HookEventDispatcherInterface;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\HtmlCommand;
/**
 * Our event subscriber class.
 */
class NodeUrbanSystemFormAlterEventSubscriber implements EventSubscriberInterface {
    public static function getSubscribedEvents() {
        return [
            HookEventDispatcherInterface::FORM_ALTER => 'hookFormAlter'
        ];
    }

    /**
     * Implements hook_form_alter
     */
    public function hookFormAlter($event) {

        if($event->getFormId() == 'node_urban_system_edit_form') {
            $form = $event->getForm();
            $country = $form['field_country']['widget']['#default_value'];

            // Get the form state object.
            $form_state = $event->getFormState();
            // Here we should check if a country has been selected.
            $country = $form_state->getValue('country');
            if ($country) {
              // populate the options from service here.
              $form['field_laws']['widget']['#options'] = \Drupal::service('custom_services.law')->getLawsByContent($country, 'country');
            } else {
              // Populate with default options.
              $form['field_laws']['widget']['#options'] = [];
            }


            $form['field_law_articles']['widget'][0]['value']['#options'] = \Drupal::service('custom_services.law')->getLawArticlesByCountry($country);
            $form['field_country']['widget']['#ajax'] = array(
                'callback' => [$this,'changeLawsData'],
                'event' => 'change',
                'disable-refocus' => FALSE
              );
            $event->setForm($form);
        }
    }

    /**
     * @param $form
     * @param \Drupal\Core\Form\FormStateInterface $form_state
     * @return \Drupal\Core\Ajax\AjaxResponse
     */
    function changeLawsData(&$form, FormStateInterface $form_state) {
        $response = new AjaxResponse();
        $response->addCommand(new HtmlCommand('#edit-field-laws', $form['field_laws']));
        return $response;
    }

}

请记住以上只是一个例子……

【讨论】:

  • hook_form_alter 得到 3 个参数,而不是 1 个 -> &$form, FormStateInterface $form_state, $form_id
  • OP 实际上并没有使用钩子,而是订阅了一个事件。无论如何,我给出了一个例子,说明你应该在 alter 函数而不是 ajax 回调中操作表单。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-11-14
相关资源
最近更新 更多