In Drupal, sometimes you may want to render a node's field inside a block while still respecting the view mode settings. In this blog post, we will demonstrate how to achieve this using Drupal 10.
Using the view() Method
To render a single field with the display setting of a view mode, you can use the view() method of the field. Here's an example of rendering an image as it's configured in the node's teaser view-mode:
$build['image'] = $node->field_image->view('teaser');
Or the body as it's configured in the node's full view-mode:
$build['body'] = $node->body->view('full');
If you want to have more fine-grained control, you can programmatically recreate how Drupal is doing view-modes:
$display_options = [
'label' => 'hidden',
'type' => 'entity_reference_entity_view',
'settings' => [
'view_mode' => 'some_media_image_view_mode',
],
];
$build['image'] = $node->field_image->view($display_options);
You can figure out the type/settings by inspecting the HTML on the view-modes form (e.g. /admin/structure/types/manage//display/full) or export the configuration and inspect the YAML files.
Using the ViewBuilder
Another approach is using the ViewBuilder to render fields separately. Here's an example:
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;
}
This code uses the $viewBuilder->viewField method to render any fields separately as needed. Note that you might need to add caching depending on the view mode settings, but that would be a separate topic.