7

In D7 we used to create a view for nodes, with each field having an own display. Each display creates a block to place in any region of the site. But of course, Views are SQL-heavy.

Now, Drupal 8 is all fancy. What's the Drupal 8 way of showing a node's fields in different regions of the page?

Edit: my current attempt is to create a custom module which provides a block for each specified field.

7 Answers 7

4

Not exactly sure why you want to have every single field as a separate block? That's pretty inefficient, if nothing else.

To have one or two additional blocks, you can use an Entity view block provided by ctools module. It will take the current node and display it in a certain view mode. Of course that will be painful to maintain if every field is separate, but I don't really see why you would want to do that?

1
  • for instance, we have a field "sidebar" for nodes. of course, that field's content should be rendered outside of the normal "content", in a different region.
    – Alex
    Apr 28, 2016 at 14:02
4

Ctools in D8 comes with an experimental module called Chaos tools blocks that will let you do this in any page manager variant layout.

This is very useful if you are creating a node variant to display a content type.

After you enable it you can add any field from the node being viewed. You will see a full list of the fields available under "Content" when you click on add block.

The downside, the module is experimental and there is not much documentation available.

Here is an active thread about it https://www.drupal.org/node/2809213

Module page https://www.drupal.org/project/ctools

I've tried it myself and it works just fine.

Hope this helps you or anybody looking for a solution for this kind of scenario.

0
3
+50

Whats best practice to show a node's fields in different regions in Drupal 8?

I think, there is no best practice for that, maybe not even a good practice, but its not impossible to do, follow a couple of options

For me this is best option: You can create a block that load current node and show your desired node_field. This way, and you can manage via UI easily (Your block with 'node_type' select and 'field_name' select is easy and fast to do that).


Begin Edit 1
Here my implementation of that block, test and please comment the results

<?php
/**
 * @file
 * Contains \Drupal\ module_name\Plugin\Block\NodeFieldBlock.
 */

namespace Drupal\module_name\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Cache\Cache;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityDisplayRepositoryInterface;
use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
use Drupal\field\Entity\FieldConfig;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;

/**
 * Provides a Filter by vocabulary terms block.
 *
 * @Block(
 *   id = "node_field_block",
 *   admin_label = @Translation("Node Field")
 * )
 */
class NodeFieldBlock extends BlockBase implements ContainerFactoryPluginInterface {
  /**
   * The Entity Type Manager.
   *
   * @var Drupal\Core\Entity\EntityTypeManagerInterface
   */
  protected $entityTypeManager;

  /**
   * The Entity Field Manager.
   *
   * @var Drupal\Core\Entity\EntityFieldManagerInterface
   */
  protected $entityFieldManager;

  /**
   * The Entity Display Repository.
   *
   * @var Drupal\Core\Entity\EntityDisplayRepository
   */
  protected $entityDisplayRepository;

  /**
   * Dependency injection through the constructor.
   *
   * @param array $configuration
   *   A configuration array containing information about the plugin instance.
   * @param string $plugin_id
   *   The plugin ID for the plugin instance.
   * @param mixed $plugin_definition
   *   The plugin implementation definition.
   * @param \Drupal\Core\Entity\EntityManagerInterface $entity_type_manager
   *   The Entity Type Manager.
   * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
   *   The Entity Field Manager.
   * @param \Drupal\Core\Entity\EntityDisplayRepositoryInterface $entity_display_repository
   *   The Entity Display Repository.
   */
  public function __construct(array $configuration, $plugin_id, $plugin_definition,
  EntityTypeManagerInterface $entity_type_manager,
  EntityFieldManagerInterface $entity_field_manager,
  EntityDisplayRepositoryInterface $entity_display_repository) {
    parent::__construct($configuration, $plugin_id, $plugin_definition);
    $this->entityTypeManager = $entity_type_manager;
    $this->entityFieldManager = $entity_field_manager;
    $this->entityDisplayRepository = $entity_display_repository;
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    return new static(
      $configuration,
      $plugin_id,
      $plugin_definition,
      $container->get('entity_type.manager'),
      $container->get('entity_field.manager'),
      $container->get('entity_display.repository')
    );
  }

  /**
   * {@inheritdoc}
   */
  public function defaultConfiguration() {
    return array(
      'node_type' => array_keys(node_type_get_names())[0],
      'view_mode' => 'default',
      'field' => '',
    );
  }

  /**
   * {@inheritdoc}
   */
  public function blockForm($form, FormStateInterface $form_state) {
    $types = node_type_get_names();
    $config = $this->configuration;
    if ($node_type = $form_state->getValue(array('settings', 'node_type'))) {
      $config['node_type'] = $node_type;
    }

    $form['node_type'] = array(
      '#title' => $this->t('Content type'),
      '#type' => 'select',
      '#options' => $types,
      '#default_value' => $config['node_type'],
      '#ajax' => array(
        'callback' => array(get_class($this), 'updateFieldList'),
        'wrapper' => 'edit-node-wrapper',
      ),
    );

    $form['options'] = array(
      '#type' => 'container',
      '#prefix' => '<div id="edit-node-wrapper">',
      '#suffix' => '</div>',
    );

    $form['options']['view_mode'] = array(
      '#title' => $this->t('View mode'),
      '#type' => 'select',
      '#multiple' => FALSE,
      '#options' => $this->getViewModes($config['node_type']),
      '#default_value' => $config['view_mode'],
    );

    $form['options']['field_list'] = array(
      '#title' => $this->t('Field list'),
      '#type' => 'select',
      '#multiple' => FALSE,
      '#options' =>  $this->getFieldList($config['node_type']),
      '#default_value' => $config['field'],
    );

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function blockSubmit($form, FormStateInterface $form_state) {
    $this->configuration['node_type'] = $form_state->getValue('node_type');
    $this->configuration['view_mode'] = $form_state->getValue(array('options', 'view_mode'));
    $this->configuration['field'] = $form_state->getValue(array('options', 'field_list'));
  }

  /**
   * {@inheritdoc}
   */
  public function build() {
    $config = $this->configuration;
    $build = array();
    if ($node = \Drupal::routeMatch()->getParameter('node')) {
      if ($config['node_type'] == $node->getType()) {
        if ($field = $node->get($config['field'])) {
          $build['field'] = $field->view($config['view_mode']);
        }
      }
    }
    return $build;
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheTags() {
    if ($node = \Drupal::routeMatch()->getParameter('node')) {
      return Cache::mergeTags(parent::getCacheTags(), array('node:' . $node->id()));
    } else {
      return parent::getCacheTags();
    }
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheContexts() {
    return Cache::mergeContexts(parent::getCacheContexts(), array('route'));
  }

  /**
  * Função que cria uma lista de fields de um node type.
  *
  * @param string $node_type
  *   O id do node type.
  * @return array
  *   Retorna a lista de campos do node type.
  */
  protected function getFieldList($node_type) {
    if (!empty($node_type)) {
      $list = $this->entityFieldManager->getFieldDefinitions('node', $node_type);
      foreach ($list as $id => $field) {
        if ($field instanceof FieldConfig) {
          $list[$id] = $field->label();
        } else {
          unset($list[$id]);
        }
      }
      return $list;
    }
    return array();
  }

  /**
  * Função que cria uma lista de view modes de um node type.
  *
  * @param string $node_type
  *   O id do node type.
  * @return array
  *   Retorna a lista de view mode do node type.
  */
  protected function getViewModes($node_type) {
    return $this->entityDisplayRepository->getViewModeOptionsByBundle('node', $node_type);
  }

  /**
   * Handles switching the node type selector.
   */
  public static function updateFieldList(&$form, FormStateInterface &$form_state, Request $request) {
    return $form['settings']['options'];
  }
}

End Edit 1


Or... get your field in preprocess_region and load in a var (this is easy to demonstrate).

function THEME_preprocess_region(&$variables) {
  //TODO: change for you region name
  if ($variables['region'] == 'sidebar_right') {
    if ($node = \Drupal::routeMatch()->getParameter('node')) {
      //TODO: change for you node type
      if ($node->getType() == 'article') {
        //If you need a flag for this type
        $variables['is_article'] = TRUE;
        //Here is your field
        $variables['node_field'] = $node->get('field_descricao')->view();
      }
    }
  }
}

And use in your twig file

{% if node_field %} 
  {{ node_field }}
{% endif %}

CAUTION:
In the future you can't remove this field, if you remove, will break your page. Explanation: $node->get('field_descricao') will evaluate no null then null->view() = broke page. Even you take care of this, someone or even you can forget about this and will be a headache why that information not show anymore.

3
  • dude thats a sick answer! i will give it a try later
    – Alex
    May 6, 2016 at 7:21
  • how about performance and caching? is this performant?
    – Alex
    May 6, 2016 at 9:40
  • 1
    That is ok, on build phase just render one element (no lookups, no build lists... just check if exists), nothing else. Every route this block is build (getCacheContexts), if there is a node with particular type, then will render a field. And yes, if you visit for a second time cache will be used. If you change the node then your block will be rebuild (getCacheTags). In this case (block get node info), the block object must override cache info, if not your block will build only once and will be used for everywhere.
    – Vagner
    May 6, 2016 at 10:09
1

for instance, we have a field "sidebar" for nodes. of course, that field's content should be rendered outside of the normal "content", in a different region. - Alex 18 hours ago

Instead of using the theme's layout and side bar region, you could use the one of the following modules and create a panel or display suite layout with a side bar region. Then you can insert the node fields and other blocks you need on the sidebar and on other regions that you create.

Panels

The Panels module allows a site administrator to create customized layouts for multiple uses. At its core it is a drag and drop content manager that lets you visually design a layout and place content within that layout. Integration with other systems allows you to create nodes that use this, landing pages that use this, and even override system pages such as taxonomy and the node page so that you can customize the layout of your site with very fine grained permissions.

Display Suite

Display Suite allows you to take full control over how your content is displayed using a drag and drop interface. Arrange your nodes, views, comments, user data etc. the way you want without having to work your way through dozens of template files.

2
  • i know these modules. are they performant? views for example are pretty sql heavy since they do additional sql queries although the node is already loaded
    – Alex
    May 9, 2016 at 8:47
  • 1
    Historically those modules have improved with age. I haven't seen performance measures on the D8 versions yet, but they are both built and used by companies that worry a lot about performance. Therefore I expect that if they aren't as well built today as they could be, they will be soon.
    – acrosman
    May 9, 2016 at 15:38
1

The Field Block module does pretty much what you are asking. It enables any field in any entity type / bundle to be displayed as a block in the region of your liking.

1
  • unfortunately it creates a block for every content type and field which results in a lot of blocks :/
    – Alex
    Aug 10, 2016 at 7:50
0

If you want to use Quick Edit feature you may create and use different View Modes via UI (under Structure/Display Modes/View Modes). I.e. you are not limited to only, say, Teaser and Full view modes. And you may use your custom view modes in Views as well. And content rendered in such a way will have all the required contextual links.

2
  • But that means I would have to create a view mode for every field?
    – Alex
    Apr 27, 2016 at 14:29
  • No, you can create new view mode for nodes, and adjust it at "Manage Display" tab of content type. Each view mode (considered as a set of field displays) may be tuned separately from others. Apr 27, 2016 at 18:13
-1

In Drupal 7 I used to do exactly the same as Alex: Create views to place content in blocks. I found this thread looking for a way to make QuickEdit work.
Display Suite and ViewModes seems to be the answer.

This seems like the cleanest way to me: Using the Drupal 8 version of Display Suite, you can add fields from nodes to blocks.
Full instructions are on https://www.drupal.org/node/2754967. This method saves creating a bunch of views, and allows inline editing.

Your Answer

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

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