6

I am trying to hide a view block if it has one or less results, i am doing this using the MYTHEME_preprocess_views_view function.

Here is the code

// Removes product range if only one product is availiable!
if($variables['view']->id() == 'brand_product_range') {
  if(count($variables['view']->result) < 2) {
    // $variables['view']->destroy();
    // $variables['view']->setDisplay(false);
    // I want to hide block here!
  }
}

I managed to get the id of view block and number of results in the view, but I don't know how to hide the block, I tried the above methods but they are not working.

1

2 Answers 2

8

I assume your view is using a block display and you don't want the block to display at all if there is only 1 result.

In my opinion you are hooking in too late in the process to hide the view there. It is possible to hide it in the theme layer but I don't think it is the optimal approach.

There are a number of ways you can do it but here are a couple of ways using a hook in a custom module, not in a theme. (This is not an exhaustive list of options that you have.)

Option 1

  1. Hook into the view execution process and tell the process that execution has failed, so it will not render the view.

For example:

use Drupal\views\ViewExecutable;

function MODULE_NAME_views_post_execute(ViewExecutable $view) {
  // If there is only 1 result set the failure condition so that
  // execution will not continue and the view will not display.
  if ($view->id() == 'VIEW_NAME') {
    if (count($view->result) == 1) {
      $view->build_info['fail'] = TRUE;
    }
  }
}

Option 2

  1. Configure your view to have 'Hide block if the view output is empty' = Yes
  2. Hook into the view execution process and clear the results when you don't want it to display.

For example:

use Drupal\views\ViewExecutable;

/**
 * Implements hook_views_post_execute().
 */
function MODULE_NAME_views_post_execute(ViewExecutable $view) {
  // Clear the results if there is only 1 result.
  // The view will then hide as it is configured to hide the
  // block if empty.
  if ($view->id() == 'VIEW_NAME') {
    if (count($view->result) == 1) {
      $view->result = array();
    }
  }
}

Note: Depending on how you decide to hide the view, if your views block is the only block in your region then be aware of this outstanding bug, which may cause your region to still display even though your block doesn't: https://www.drupal.org/project/drupal/issues/953034

4
  • Thanks very much, this looks promising, I want to try this way, I created a new custom module ".info.yml file" but I don't know where to put this above code that you provided, does it have a certain file naming conventions and directory hierarchy, I don't think that I can simply put it in any file, Sorry Have not used custom modules that much and certainly first time to hook into the views rendering process. Jan 8, 2018 at 14:05
  • I tried adding the first option to a PHP file ViewExecutable.php in MY_MODULE/src/views folder. but it didn't work Jan 8, 2018 at 15:27
  • I figured it out, the .module file thanks very much its working greatly! Jan 8, 2018 at 15:43
  • 1
    Yep, in the case of Drupal hooks they can just go in the MODEUL_NAME.module file. Once you are writing classes then you start digging into the src folder.
    – rooby
    Jan 9, 2018 at 9:19
2

Build on @rooby's answer but for themes only.

It is possible to use MYTHEME_views_post_render() to hide the views block when Hide block if the view output is empty' = Yes. Note that any additional cache metadata is not bubbled for entities such as a block in the views header (if a change in the block should trigger a cache refresh).

use Drupal\Core\Cache\CacheableMetadata;
use Drupal\views\ViewExecutable;
use Drupal\views\Plugin\views\cache\CachePluginBase;
use Drupal\views\Plugin\views\area\Entity as ViewsAreaEntity;

/**
 * Implements hook_views_post_render().
 */
function mytheme_views_post_render(ViewExecutable $view, &$output, CachePluginBase $cache) {
  if (mycondition()) {
    // Hide the block.
    $view->result = [];
  }
  foreach ($view->header as $views_area_entity) {
    if ($views_area_entity instanceof ViewsAreaEntity) {
      $entity_type = $views_area_entity->definition['entity_type'];
      $target = $views_area_entity->options['target'];
      // Note: `$output` is a render array for views blocks, 
      // but for REST/JSON display applying to `$view->element`
      // is required as `$output` is string 
      // (Drupal\views\Render\ViewsRenderPipelineMarkup).
       $block_entity = \Drupal::entityTypeManager()->getStorage($entity_type)->load($target);
      $block_entity = $block_entity ? $block_entity : \Drupal::service('entity.repository')->loadEntityByConfigTarget($entity_type, $target);
      if ($block_entity && ($block_view = \Drupal::entityTypeManager()->getViewBuilder($entity_type)->view($block_entity))) {
        CacheableMetadata::createFromRenderArray($output)
          // OR use the block entity directly if required.
          // However view() will have a #lazy_builder for blocks & an un-rendered node #theme so shouldn't be any performance impact.
          // ->merge(CacheableMetadata::createFromObject($block_entity))
          // ->merge(CacheableMetadata::createFromObject($block_entity->getPlugin()))
          ->merge(CacheableMetadata::createFromRenderArray($block_view))
          ->applyTo($output);
      }
    }
  }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.