I have some fields exposed in filter criteria, and my exposed form style is input required.
So as expected, when i visit the view path i see only the exposed form and no results from the view, until i submit (or apply the filters). What i don't want is to be able to submit an empty form and see all the results from my view, so i check the fields i need to have a value as required but they automatically receive an .error class (so they get a red margin) before i apply the filters (or submit). I would like to have this validation after the form is submitted not before, since, in my understanding, the whole meaning of having input required is to wait for the filters to be applied (and maybe do some validation if something is marked as required) then show results.

This also happens when the form is exposed in a block.
What i wanted is to have my filters exposed in a block and loaded in a region on my home page, then submit the form and see the results in my view. It works as expected with this one little drawback, the required fields in the exposed form receive the .error class before the form is submitted even though the validation should be done after submission

Files: 
CommentFileSizeAuthor
#26 7.x-3.13-views-plugin_exposed_form_check_input-1877678-26.patch1.58 KBIRuslan
#11 1877678_views_plugin_exposed_form_check_input_2.patch2.37 KBMrHaroldA
FAILED: [[SimpleTest]]: [MySQL] 1,604 pass(es), 23 fail(s), and 0 exception(s). View
#7 1877678_views_plugin_exposed_form_check_input.patch815 bytesMrHaroldA
FAILED: [[SimpleTest]]: [MySQL] 1,580 pass(es), 23 fail(s), and 0 exception(s). View

Comments

rockaholiciam’s picture

facing the same problem and not sure if to simply override it using js or dive deeper into the module (which might end up consuming hell lot of time)

rockaholiciam’s picture

facing the same problem and not sure if to simply override it using js or dive deeper into the module (which might end up consuming hell lot of time)

drupnewb’s picture

Well the only hint i have is in
sites/all/modules/views/plugins/views_plugin_exposed_form.inc line 151 in function render_exposed_form
its using this function to build the form, $form = drupal_build_form('views_exposed_form', $form_state);
and drupal_build_form does the processing, validation and submission all in one go (from here
http://api.drupal.org/api/drupal/includes!form.inc/function/drupal_build... )

On the other hand, when you build forms in a module you can just use drupal_get_form, and provide the function that builds the render array to it. Then have a _validate function and it runs as expected, you get the form, and on submission it's validated (also shows validation error messages if it doesn't pass). Example here
http://codekarate.com/daily-dose-of-drupal/drupal-7-module-development-p...

ericclaeren’s picture

Hi, have the same issue, don't know how to fix this, but this definitely needs some work, because this behavior is incorrect.

MrHaroldA’s picture

Version: 7.x-3.5 » 7.x-3.x-dev

Bug confirmed in views-7.x-3.5+37-dev on two separate websites using required exposed textfields and the "exposed form style" set to either "input required" or "basic".

Here's a simple view to test it:

$view = new view();
$view->name = 'exposed_test';
$view->description = '';
$view->tag = 'default';
$view->base_table = 'node';
$view->human_name = 'Exposed test';
$view->core = 7;
$view->api_version = '3.0';
$view->disabled = FALSE; /* Edit this to true to make a default view disabled initially */

/* Display: Master */
$handler = $view->new_display('default', 'Master', 'default');
$handler->display->display_options['title'] = 'Exposed test';
$handler->display->display_options['use_more_always'] = FALSE;
$handler->display->display_options['access']['type'] = 'perm';
$handler->display->display_options['cache']['type'] = 'none';
$handler->display->display_options['query']['type'] = 'views_query';
$handler->display->display_options['exposed_form']['type'] = 'basic';
$handler->display->display_options['pager']['type'] = 'full';
$handler->display->display_options['pager']['options']['items_per_page'] = '10';
$handler->display->display_options['style_plugin'] = 'default';
$handler->display->display_options['row_plugin'] = 'node';
/* Field: Content: Title */
$handler->display->display_options['fields']['title']['id'] = 'title';
$handler->display->display_options['fields']['title']['table'] = 'node';
$handler->display->display_options['fields']['title']['field'] = 'title';
$handler->display->display_options['fields']['title']['label'] = '';
$handler->display->display_options['fields']['title']['alter']['word_boundary'] = FALSE;
$handler->display->display_options['fields']['title']['alter']['ellipsis'] = FALSE;
/* Filter criterion: Content: Title */
$handler->display->display_options['filters']['title']['id'] = 'title';
$handler->display->display_options['filters']['title']['table'] = 'node';
$handler->display->display_options['filters']['title']['field'] = 'title';
$handler->display->display_options['filters']['title']['operator'] = 'contains';
$handler->display->display_options['filters']['title']['exposed'] = TRUE;
$handler->display->display_options['filters']['title']['expose']['operator_id'] = 'title_op';
$handler->display->display_options['filters']['title']['expose']['label'] = 'Title';
$handler->display->display_options['filters']['title']['expose']['operator'] = 'title_op';
$handler->display->display_options['filters']['title']['expose']['identifier'] = 'title';
$handler->display->display_options['filters']['title']['expose']['required'] = TRUE;
$handler->display->display_options['filters']['title']['expose']['remember_roles'] = array(
  2 => '2',
  1 => 0,
  3 => 0,
);

/* Display: Page */
$handler = $view->new_display('page', 'Page', 'page');
$handler->display->display_options['defaults']['hide_admin_links'] = FALSE;
$handler->display->display_options['path'] = 'exposed-test';
$handler->display->display_options['menu']['type'] = 'normal';
$handler->display->display_options['menu']['title'] = 'Exposed';
$handler->display->display_options['menu']['weight'] = '0';
$handler->display->display_options['menu']['context'] = 0;
$handler->display->display_options['tab_options']['weight'] = '0';
MrHaroldA’s picture

The error class was introduced in Drupal 7.17. I managed to trace it to this patch in form.inc.

Here's the (reopened) issue: #1785436: Submission of #required elements without #title and empty value does not display any error

MrHaroldA’s picture

Priority: Normal » Major
FileSize
815 bytes
FAILED: [[SimpleTest]]: [MySQL] 1,580 pass(es), 23 fail(s), and 0 exception(s). View

(semi-crosspost with #1785436: Submission of #required elements without #title and empty value does not display any error #22)

Hmmm... Further investigation points to this piece of code in views/plugins/views_plugin_exposed_form.inc->render_exposed_form()

    $form_state = array(
      'view' => &$this->view,
      'display' => &$this->display,
      'method' => 'get',
      'rerender' => TRUE,
      'no_redirect' => TRUE,
      'always_process' => TRUE,
    );

...

    $form_state['exposed_form_plugin'] = $this;
    $form = drupal_build_form('views_exposed_form', $form_state);
    $output = drupal_render($form);

In this case 'always_process' => TRUE triggers form validation, which < Drupal 7.17 did throw an error(!), but without the error class. I now believe the patch from this issue reveiled the error in the views exposed filter plugin instead of causing it ...

The attached patch only processes the form if there actually is input. This seems to be a really straight-forward fix for a long time error which became visible with the fix in Drupal 7.17, but it also changes the way you have to set default values for exposed filters.

Before the patch:

if (!isset($form_state['view']->exposed_input['my_field'])) {
  $form_state['input']['my_field'] = 'my value';
}

after the patch:

$form['my_field']['#default_value'] = 'my value';

You now can omit the if () because 'always_process' is triggered when there's input available from $_GET (or session, etc) and will override whatever you set as default value with the input.

Bumping it to 'major' since a lot of websites will encounter this bug/misbehaviour when updating to Drupal 7.17+.

MrHaroldA’s picture

Status: Active » Needs review

Note to self: do not only check if you've actually attached the patch, also check if you set the status to 'needs review' ;)

Status: Needs review » Needs work

The last submitted patch, 1877678_views_plugin_exposed_form_check_input.patch, failed testing.

MrHaroldA’s picture

Not only did my patch fail testing, it's also not good enough.

I only check if there's input, not if the input matches the exposed filters in this view. This means that all forms are processed when there's input and the error class returns for all exposed filters on that page.

This also explains this function in views_plugin_exposed_form_input_required.inc

  function exposed_filter_applied() {
    static $cache = NULL;
    if (!isset($cache)) {
      $view = $this->view;
      if (is_array($view->filter) && count($view->filter)) {
        foreach ($view->filter as $filter_id => $filter) {
          if ($filter->is_exposed()) {
            $identifier = $filter->options['expose']['identifier'];
            if (isset($view->exposed_input[$identifier])) {
              $cache = TRUE;
              return $cache;
            }
          }
        }
      }
      $cache = FALSE;
    }

    return $cache;
  }

A revisited patch will probably move that function into views_plugin_exposed_form.inc and use it instead of $exposed_input = $this->view->get_exposed_input();

MrHaroldA’s picture

Status: Needs work » Needs review
FileSize
2.37 KB
FAILED: [[SimpleTest]]: [MySQL] 1,604 pass(es), 23 fail(s), and 0 exception(s). View

Here's the revised patch which moves get_exposed_input() into the views_plugin_exposed_form class. I had to get rid of the static cache because it had to be cached for each identifier.

Really hoping someone looks into this ...

Status: Needs review » Needs work

The last submitted patch, 1877678_views_plugin_exposed_form_check_input_2.patch, failed testing.

MrHaroldA’s picture

Title: Exposed form with style input required » Exposed filter gets error class without submitting it

Changing the title to reflect the problem. Still hoping someone looks into this.

MrHaroldA’s picture

Status: Needs work » Needs review
Issue tags: -exposed filter

Status: Needs review » Needs work
Issue tags: +exposed filter

The last submitted patch, 1877678_views_plugin_exposed_form_check_input_2.patch, failed testing.

nodecode’s picture

Any news on this?

I see that the other related issue #1785436: Submission of #required elements without #title and empty value does not display any error is still sort of unresolved and the errors related to the patch in #11 seem mostly (if not all) unrelated to the work MrHaroldA has done. I would be happy to test but not if the patch cannot be applied.

nodecode’s picture

I tested the patch in #11 against 7.x-3.7, which applied cleanly, and it works!

The .error class is now gone from the filter, however, for those using "required" exposed filters read on....

This patch solves the above issue but reveals another issue #1169092: "required" flag on exposed filters ignored. Since I'm using views with Search API module, my exposed filters are "required" because I don't want results showing when the user hasn't even hit the search button (exposed filter button) yet. But due to the issue I mentioned, ALL results are showing when I visit the view page.

Here's how to use this patch successfully if you are using "required" exposed filters...

1. There is an untested patch https://drupal.org/node/1169092#comment-4932422 which may solve the problem.
2. While "required" filters are still broken in Views you can use the following workaround http://www.agentic.ca/blog/suppressing-views-exposed-filter-output-until...

almc’s picture

This issue is definitely worth fixing and inclusion in the core code, looks like it has been open for almost one year.
Thanks for the link on the workaround, will try to use it meanwhile ...

nodecode’s picture

Issue summary: View changes

Does anyone know how to debug the Simpletest failures that are holding up this patch? I'd do the leg work if I knew where to start.

anrikun’s picture

I've tested patch #11 and it works even when using "required" for me.

MrHaroldA’s picture

Version: 7.x-3.x-dev » 8.x-3.x-dev

Let's try to get this into D8 first; I believe that's the prefered way of changing things in Views these days ...

Thanks for bumping it! ;)

anrikun’s picture

Actually #17 seems right: patch #11 fixes some views but breaks others. I'll try to figure out what's going wrong.

anrikun’s picture

Here's my workaround for a simple search filter when you don't want results to be displayed until user has actually submitted keywords:
- Use "required input" as exposed form plugin
- Do not check "required" for the keywords exposed filter
- Do not check "remember" either
- Add this code in a custom module:

<?php
/**
 * Implements hook_form_FORM_ID_alter() for views_exposed_form().
 */
function MYMODULE_form_views_exposed_form_alter(&$form, &$form_state, $form_id) {
  // Make sure keywords were entered on search, without using #required that
  // has unwanted side effects.
  if ($form_state['view']->name == 'SEARCH_VIEW_NAME') {
    array_unshift($form['#validate'], 'MYMODULE_search_exposed_form_validate');
  }
}

/**
 * Additional validate handler for search's exposed form.
 */
function MYMODULE_search_exposed_form_validate($form, &$form_state) {
  if (!isset($form_state['values']['KEYWORDS_FILTER_NAME']) || !strlen($form_state['values']['KEYWORDS_FILTER_NAME'])) {
    $form_state['view']->exposed_input = array();
  }
}
?>
Lendude’s picture

Version: 8.x-3.x-dev » 7.x-3.x-dev
Issue tags: -exposed filter

Moving this back to the 7.x-3.x queue since this has already been fixed in D8 (well it's working at least).

IRuslan’s picture

For me, the last patch does not work.
Also, I think better to use polymorphism for exposed_filter_applied() instead of moving it to parent class at all.

See my version of the patch.

Status: Needs review » Needs work
IRuslan’s picture

Status: Needs work » Needs review
d.novikov’s picture

Here is my workaround for 8.x-2.x (inspired by #24):

- Use "required input" as exposed form plugin
- Do not check "required" for the keywords exposed filter
- Do not check "remember" either
- Add this code in a custom module:


/**
 * Implements hook_form_FORM_ID_alter().
 */
function mymodule_form_views_exposed_form_alter(&$form, FormStateInterface $form_state) {
  if ($form['#id'] == 'views-exposed-form-search-page-1') {
    array_unshift($form['#validate'], 'mymodule_views_exposed_form_validate');
  }
}

/**
 * Views exposed form validation handler. Makes view ignore empty filter value.
 */
function mymodule_views_exposed_form_validate(&$form, FormStateInterface $form_state) {
  if ($form_state->isValueEmpty('your_keyword')) {
    $view = &$form_state->get('view');
    $input = $view->getExposedInput();
    unset($input['your_keyword']);
    $view->setExposedInput($input);
    $form_state->set('view', $view);
    // Prevent exposed input from being populated once more.
    \Drupal::request()->query->remove('your_keyword');
  }
}

sagesolutions’s picture

@d.novikov Thanks for the fix for drupal 8.2. It worked for me too.

Calar’s picture

Version: 7.x-3.x-dev » 8.x-3.x-dev

@d.novikov Thanks for the workaround! #29 Working in Drupal 8.3.2.

glycid’s picture

In D8 you can furthermore simply use hook_views_post_execute(), (in D7 maybe too)
in head of your MYMODULE.module file:
use Drupal\views\ViewExecutable;

then:

function MYMODULE_views_post_execute(ViewExecutable $view) {
  if ($view->current_display == 'MYVIEW_page_1') { // see view settings -> advanced -> others -> name of system
    if (empty($view->exposed_raw_input['MYFIELDNAME_value'])) {
      drupal_set_message(t('No filter input, no results'), 'error'); // set message if you want
      $view->result = array(); // return empty result array
    }
  }