Рейтинг:1

AJAX in a plugin form results in "The specified #ajax callback is empty or not callable."

флаг in

I have a configuration form for a Condition plugin I want to have ajax in. When selecting from this field, I want to get a list of displays for that View:

  public function buildConfigurationForm(array $form, FormStateInterface $form_state) {
    $form = parent::buildConfigurationForm($form, $form_state);
    $views = $this->entityTypeManager->getStorage('view')->loadMultiple();
    $options = [];

    /** @var \Drupal\views\Views $view */
    foreach ($views as $view) {
      $options[$view->id()] = $view->label();
    }

    $form['#prefix'] = '<div id="views-settings">';
    $form['#suffix'] = '</div>';

    $view_id = $form_state->getValue('view_id');

    if (empty($view_id)) {
      $view_id = $form_state->getUserInput()["conditions"]["sfp_condition_view_not_empty"]["view_id"] ?? NULL;
    }

    if (empty($view_id)) {
      $view_id = $this->configuration['view_id'] ?? NULL;
    }

    $displays = isset($view_id) ? $this->getViewsDisplays($view_id) : [];

    $form['view_id'] = [
      '#type' => 'select',
      '#title' => $this->t('View'),
      '#required' => TRUE,
      '#options' => $options,
      '#default_value' => $view_id ? $options[$view_id] : '',
      '#ajax' => [
        'wrapper' => 'views-settings',
        'callback' => '::updateViewsDisplay',
        'event' => 'change',
      ],
    ];

    if (!empty($displays)) {
      $form['view_display'] = [
        '#type' => 'select',
        '#title' => $this->t('View display'),
        '#required' => TRUE,
        '#options' => $displays,
        '#default_value' => $displays[$this->configuration['view_display']] ?? $displays['default'],
      ];
    }

    return $form;
  }

      /**
       * Trigger form rebuild.
       *
       * @param array $form
       * @param \Drupal\Core\Form\FormStateInterface $form_state
       * @return array
       */
      public function updateViewsDisplay(array $form, FormStateInterface $form_state) {
        return $form;
      }

Xdebug hits the method fine, but I get this AJAX error:

"The specified #ajax callback is empty or not callable."

and nothing happens after that.

I checked elsewhere where I have done this and the only difference I can see is the ones that worked are regular Drupal Form API forms, and this is a plugin form passed from buildConfigurationForm. Outside of that, I am not seeing the issue.

флаг cn
Есть ли шанс, что вы используете это в форме контекстного пользовательского интерфейса (в отличие, например, от видимости блока)? Я помню, что у меня были ужасные проблемы с попыткой заставить AJAX работать с модулем Context.
Kevin avatar
флаг in
Да, я именно там. Изменение обратного вызова на процедурную функцию в .module работает, но я не знаю, почему. Разве это не должно быть доступным методом, если сделать его статическим? Казалось, ничего не работает. Что странно, так это то, что xdebug никогда не подводил. Состояние формы также не всегда имеет значение selected, в отличие от обычных форм.
флаг cn
Это было давно, но IIRC имел какое-то отношение к пользовательскому интерфейсу контекста, имеющему собственную точку входа для формы AJAX, он не использует `/system/ajax`. Я уверен, что помню, что происходило то же самое, обратный вызов определенно сработал, но результаты не были включены в новую форму. Все, что я сделал, чтобы обойти это, находится где-то в этом модуле, если это поможет: https://www.drupal.org/project/request_data_conditions
Kevin avatar
флаг in
Ух ты. Ага. Кто знал? Публикация ответа.
Kevin avatar
флаг in
Это фактически исправило AJAX, но в итоге получило ошибку «обнаружен незаконный выбор». Я просто отключил AJAX и предоставил весь массив в виде списка.
флаг cn
Не вините вас, я только что перечитал бессвязный комментарий, который я написал много лет назад, пытаясь объяснить, что происходит (строка 152 BaseCondition), и он вернулся обратно. Вот драконы.
Рейтинг:3
флаг in

Благодаря ссылке Clives, по причинам, по которым я не на 100% нахожусь в формах конфигурации для плагина Context, обратный вызов AJAX должен сделать это:

  /**
   * Запустить перестроение формы.
   *
   * @param массив $form
   * @param \Drupal\Core\Form\FormStateInterface $form_state
   * @возвратный массив
   */
  общедоступная функция updateViewsDisplay (массив $ form, FormStateInterface $ form_state) {
    $triggering_element = $form_state->getTriggeringElement();
    $parents = array_slice($triggering_element['#array_parents'], 0, -1);
    вернуть NestedArray::getValue($form, $parents);
  }

Потом это начало работать.

Кроме того, объявление AJAX должно быть:

'обратный вызов' => [$this, 'updateViewsDisplay'],

Я предполагаю, что это связано с тем, что это «форма в форме».

4uk4 avatar
флаг cn
Да, объявление формы в плагине обычно не является автономным, это поддерево, которое код, использующий плагин, может разместить в своей собственной форме. Вы видите этот трюк с нарезкой массива также в виджетах полей. Кстати, использовать обратный вызов на основе $this не рекомендуется, вы можете не получить ожидаемый экземпляр формы. Я бы использовал `'[static::class, 'updateViewsDisplay']`, если `'::updateViewsDisplay'` не работает. Если вам затем нужно сослаться на объект формы, вы можете получить его из $form_state.
Kevin avatar
флаг in
Я вижу, также обратите внимание, что static::class требует, чтобы обратный вызов также был статическим, иначе ошибка сохраняется.

Ответить или комментировать

Большинство людей не понимают, что склонность к познанию нового открывает путь к обучению и улучшает межличностные связи. В исследованиях Элисон, например, хотя люди могли точно вспомнить, сколько вопросов было задано в их разговорах, они не чувствовали интуитивно связи между вопросами и симпатиями. В четырех исследованиях, в которых участники сами участвовали в разговорах или читали стенограммы чужих разговоров, люди, как правило, не осознавали, что задаваемый вопрос повлияет — или повлиял — на уровень дружбы между собеседниками.