In lieu of there not yet being a D8 version of Views Selective Filters I'm trying to alter the options of an exposed filter of an EVA view (attached to taxonomy terms) using hook_form_views_exposed_form_alter to remove any terms that don't have the view's argument tid as a parent.

I've been doing something a bit like this:

function mymodule_form_views_exposed_form_alter(&$form, FormStateInterface $form_state, $form_id) {
  $view = $form_state->get('view');
  if ($view->id() == 'my_view_id') {
    $arg = $view->args[0];  // get first argument from view
    $vid = 'my_taxonomy';
    $terms =\Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadTree($vid);  // build term tree
    $options['All'] = 'All';  // add wildcard option
    foreach ($terms as $term) {
      if(in_array($arg, $term->parents)){  // check if argument is parent of term
       $options[$term->tid] = $term->name;
      }
    }
    $form['category']['#options'] = $options;
  }
}

However I only end up with the single hardcoded 'All' option. Deleting $arg and directly using a correct tid value does filter the options as expected.
If I do a ksm($view->args) using Devel inside the hook it seems to be called twice - once with the argument correctly filled, and then again with a NULL result. If I add additional logic to the hook to only operate on the form with populated arguments the exposed filter isn't altered at all.

Is there some reason arguments might not be passed correctly through the hook by EVA? When I add a Block or Page display to the same view (or alternatively use Viewfield to try accomplish the same thing as EVA) the hook works correctly (and is only called once).

Comments

Khalor created an issue. See original summary.

ahebrank’s picture

Do you have the EVA argument handling set to 'None' ("No special handing", not the default 'id' or 'token')? 'None' should leave the $args alone but the other two will overwrite them (e.g., https://github.com/ahebrank/eva/blob/8.x-1.x/eva.module#L138)

I'm guessing the hook running twice has to do with the extra initialization that checks whether the exposed form exists (so that it may be represented as an additional extra field). If that's true, and that check can be done more cleanly, it'd be great to not process the exposed form twice.

khalor’s picture

If I use 'None' for EVA argument handling and try populating it another way ('Provide default value: Taxonomy term ID from URL') the main body of the view receives the argument correctly (as it does when using EVA's 'id' option) but the double-firing of the hook now gets 2 NULLs for $arg instead of 1x NULL and 1x correct arg value.

After some digging it seems the exposed form isn't passed any arguments because we actually build it as a field before any of EVA's argument logic is performed (the exposed form is built on line 112 of eva.module, the arguments aren't set until line 123).

The double hook happens because we're actually building the exposed form twice: once as it's own field, then again as part of the view as it normally would be - it's just not displayed due to {{ exposed }} being removed from eva-display-entity-view.html.twig.

An easy fix for the arguments would be moving the exposed form field build until after all the other EVA argument logic has happened (a quick cut/paste didn't break anything immediately for me but there may be other issues I'm not aware of) - as for the double-hook firing perhaps we should be unsetting the exposed form from the view after the field is built instead of simply hiding it with the twig template?

ahebrank’s picture

The first change (building the exposed form later) makes sense to me -- can you post a patch or PR on github?

For the second (unsetting the form before the regular render) -- maybe some trickery in overloading usesExposed() in the display handler so that View::build ignores the form? Something along the lines of:

function usesExposedField() {
  return parent::usesExposed();
}

function usesExposed() {
  return FALSE;
}

and then call the first function in the EVA enumerator: https://github.com/ahebrank/eva/blob/8.x-2.x/eva.module#L82

khalor’s picture

Assigned: Unassigned » khalor
StatusFileSize
new1.42 KB

Here's a patch changing the order of where the exposed form is built - I'll have a go at your suggestion for changing usesExposed() and see where we end up!

sinn’s picture

Status: Active » Needs review
StatusFileSize
new1.71 KB

We have the same issue with Views Selective Filters.

I couldn't applied patch #5 on latest dev version, so new version is attached. Also variable for exposed form were added to template eva-display-entity-view.html.twig. It was added because we are stuck with it. It wasn't obvious why exposed filter is shown on front end and isn't shown in back end.

ahebrank’s picture

Status: Needs review » Needs work

I don't think we want the exposed filter displayed with the main View (template) since it's handled in a separate pseudofield. At least let's hold off on that idea until the rest of the patch appears to turn off the regular exposed form render.

khalor’s picture

Version: 8.x-1.x-dev » 8.x-2.x-dev

If we do

function usesExposedField() {
  return parent::usesExposed();
}

function usesExposed() {
  return FALSE;
}

in the display handler and call usesExposedField() in eva.module the we correctly fire the hook once, and if you have AJAX turned off the page will refresh and filter the view correctly. However with AJAX turned on the view will refresh but not perform any filtering.

ahebrank’s picture

If I've got the logic right, calling the form render function with $block = TRUE (as on https://github.com/ahebrank/eva/blob/8.x-2.x/eva.module#L115 already) should render the form while the default call from the view build would not... but only if some special block sauce is also set.

if (!$this->view->display_handler->displaysExposed() || (!$block && $this->view->display_handler->getOption('exposed_block'))) {
  // don't render it
}

So maybe the next thing to try would be to define the exposed_block display handler option to TRUE and leave it hidden from the config UI. I don't know whether there are side-effects associated with that option.

sinn’s picture

With patches 5 or 6 exposed form works with ajax enabled and without.

About #7: we are using {{ exposed }} to test EVA view display in back office. Otherwise testing of exposed filter is difficult.

ahebrank’s picture

Ah, OK, the template for preview. Hmm. I was thinking we'd aim to be consistent with how exposed filters for Block handlers work, so that would mean no preview if I'm remembering correctly.

I guess the question of whether or not to a) prevent the double form render (like the block plugin) or b) whether to continue the double-render and fix the preview could move to a separate issue and we could go ahead and merge the first part (rendering the separate exposed form field a little later).

If one of you can do #6 against 8.x-2.x (without the template change for now) I'll go ahead and merge it. I'll follow up here with a link to the new issue for the second part.

khalor’s picture

I've just done a PR for moving the exposed form build against 8.x-2.x on github.

ahebrank’s picture

StatusFileSize
new1.71 KB

Thanks, patch from PR in #12 attached. I'll go ahead and merge this.

ahebrank’s picture

Status: Needs work » Fixed
ahebrank’s picture

Status: Fixed » Needs work

This is linked in the sidebar, but let's figure out what to do with the rerender and preview here: https://www.drupal.org/project/eva/issues/2960656

ahebrank’s picture

Status: Needs work » Fixed

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.