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 :)