Drupal - How can I programmatically render a node's field respecting the view mode settings?

To render a single field with the display setting of a view mode you can use the view() method of the field:

Example for rendering the image in teaser format:

$build['image'] = $node->field_image->view('teaser');

Or the body in full:

$build['body'] = $node->body->view('full');

This answer builds on https://drupal.stackexchange.com/a/208061/394

// Designate the field we want to render.
$field_name = 'body';
// Retrieve a render array for that field with the given view mode.
$render_array = $entity->$field_name->view('full');
// Render the result.
\Drupal::service('renderer')->renderRoot($render_array);

To completely programmatically render the field you finish by calling renderRoot(), which establishes a separate render context from what typical page responses would use -- a single render context for a request or sub-request. We could also use renderPlain(), but then it would escape all the things.

In the Drush repl but not in normal page execution, this threw a warning for me:

PHP warning:  DOMDocument::loadHTML(): Tag drupal-entity invalid in Entity, line: 1 in /drupal/core/lib/Drupal/Component/Utility/Html.php on line 286

Thanks to Rainer Feike's answer I came to the solution:

<?php

public function build() {
  $node = \Drupal::routeMatch()->getParameter('node');
  $build = array();
  $markup = array();

  $fieldsToRender = array(
    'field_node_ref', 'field_foo', 'field_bar',
  );

  $viewmode = 'default';
  $entityType = 'node';
  $display = entity_get_display($entityType, $node->getType(), $viewmode);
  $viewBuilder = \Drupal::entityTypeManager()->getViewBuilder($entityType);

  foreach ($fieldsToRender as $field_name) {
    if (isset($node->{$field_name}) && $field = $node->{$field_name}) {
      $fieldRenderable = $viewBuilder->viewField($field, $display->getComponent($field_name));
      if (count($fieldRenderable) &&! empty($fieldRenderable)) {
        $markup[] = \Drupal::service('renderer')->renderRoot($fieldRenderable);
      }
    }  
  }

  if (count($markup)) {
    $build = array(
      '#type' => 'markup',
      '#markup' => implode("", $markup),
    );
  }

  return $build;

}

Using $viewBuilder->viewField I can render any fields separately I need. I just need to find out how to add caching depending on the view mode settings, but this is another question :)

Tags:

Entities

8