I'm not sure if this is a bug or I'm just doing something wrong so I've tagged it as support for now.
I'm trying to make a custom views filter plugin that uses ajax in the form. It's a multi-vocabulary term select - the user selects a vocabulary then selects a term from that vocabulary from an ajax-populated list.
However, I'm encountering several bizarre issues while trying to set it up. If I provide the ajax callback as [$this, 'updateTerms'] it gives me this ajax error in preview:
A fatal error occurred: Recursion detected
Ajax never even runs in the full view. In this case the widget is not rendered at all and the error occurs even on the first load of the widget.
If I provide the ajax callback as either '::updateTerms' or 'Drupal\de_vocabulary_level_views_filter\Plugin\views\filter::updateTerms' it gives me this error in the views preview:
A fatal error occurred: The specified #ajax callback is empty or not callable.
and, inexplicably, this error on the full view:
An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (8 MB) that this server supports.
This issue occurs even on a fresh minimal install of Drupal 8 core with no extra modules or configuration besides enabling taxonomy, views, views_ui and the custom module itself and adding the filter to a view.
The form is pretty simple and works outside of views.
The form is provided in the valueForm function (though I've had the same results inside buildExposedForm) which is overridden like so:
/**
* {@inheritdoc}
*/
protected function valueForm(&$form, FormStateInterface $form_state) {
$vocabularies = $this->vocabularyStorage->loadMultiple();
$vocab_options = array();
foreach ($vocabularies as $voc) {
$vocab_options[$voc->id()] = $voc->label();
}
$form['value'] = [
'vocab' => [
'#title' => $this->t('Vocabulary'),
'#type' => 'select',
'#options' => $vocab_options,
'#empty_option' => $this->t('- Any -'),
'#empty_value' => NULL,
'#default_value' => !empty($form_state->get('vocab')) ? $form_state->get('vocab') : NULL,
'#ajax' => [
'event' => 'change',
'callback' => 'Drupal\de_vocabulary_level_views_filter\Plugin\views\filter::updateTerms',
'wrapper' => 'de-vocabulary-level-views-filter-terms-wrapper'
],
],
'de_vocabulary_level_views_filter' => [
'#title' => $this->t('Term'),
'#type' => 'select',
'#empty_option' => $this->t('- Any -'),
'#empty_value' => NULL,
'#default_value' => !empty($form_state->get('term')) ? $form_state->get('term') : NULL,
],
'#prefix' => '<div id="de-vocabulary-level-views-filter-terms-wrapper">',
'#suffix' => '</div>',
];
if (!empty($form_state->getValue('vocab'))) {
$term_query = $this->entityQuery->get('taxonomy_term')
->condition('status', 1)
->condition('vid', $form_state->getValue('vocab'));
$term_query_result = $term_query->execute();
$terms = $this->termStorage->loadMultiple($term_query_result);
$term_options = [];
foreach ($terms as $term) {
$term_options[$term->id()] = $term->label();
}
$form['value']['de_vocabulary_level_views_filter']['#options'] = $term_options;
}
}
There's a small ajax callback:
public function updateTerms(&$form, FormStateInterface $form_state) {
return $form[$this->options['expose']['identifier']];
}
Currently it reloads a section of the form from the base of the filter form but the same results occur treating it as if it was just the bit from valueForm.
Comments
Comment #2
lendudeMoving to the core Views queue.
Comment #3
robert ngoSubscribe
Comment #5
superlolo95 commentedSame issue I have with a settings form for a view area using drupal 8.3.7.
Comment #8
steven buteneers commentedExperiencing this problem as well in the latest 8.6-dev when trying use #ajax in buildOptionsForm() for a @ViewsRow plugin.
Comment #9
Andrei Tyuhai commentedComment #10
Andrei Tyuhai commentedComment #11
yonailoSame problem as #8 here.
I haved tried patch #77 from #2842525 and it does not solve this issue.
Comment #13
gonssalI've been hit by this same issue when implementing a custom complex filter that needs AJAX functionality.
The main problem here is that when you use
[$this, 'ajaxCallback']or'::ajaxCallback', it's important to understand that when the form processes the callback,$thisor the::shortcut will be the form's class and not your filter plugin's class, hence the "The specified #ajax callback is empty or not callable" error. The function is in your filter's plugin class, not on the views exposed form's one.I really needed to do this so I found a way. First you probably have to apply patch #83 (at the time of writing) from #2842525: Ajax attached to Views exposed filter form does not trigger callbacks
Then you have to set your AJAX callback like this in your filter plugin class:
'callback' => [self::class, 'ajaxCallback'],Your
ajaxCallbackfunction should be static or you will get a warning.Now the trick to get your filter plugin's class instance is to pass it through the form's state. I'm doing it like such:
$form_state->set(self::class . '_instance', $this);And then in your
ajaxCallback, you do...$instance = $form_state->get(self::class . '_instance');... and you use that instead of
$this.Comment #14
thetwentyseven commentedI have the same problem. The latest patch #2842525: Ajax attached to Views exposed filter form does not trigger callbacks mentioned by @gonssal does not apply correctly for me with the Drupal version: 8.9.19
Comment #19
cilefen commented#13 seems to be a full answer. Thank you for that.