3

In Drupal 8, how do I add contextual edit links in a block template?

This is for block entities created at /block/add, not block plugins created by a class in a custom module.

I want an edit link to display on the front end. I want an edit link for the block, not for the block content type (bundle). For example, a link to /block/123.

I believe it's supposed to be output by

{{ title_prefix }}

But I don't see the links showing up and I'm not sure where to turn them on.

I was able to get them to render with this code, but then realized this only works for block plugins, not block entities:

function exampletheme_preprocess_block(&$variables) {

  if (!empty($variables['elements']['#contextual_links'])) {
    $variables['elements'] = ContextualLinks::preRenderLinks($variables['elements']);
    $variables['links'] = array(
      '#prefix' => '<div class="contextual-links-wrapper">',
      '#suffix' => '</div>',
      '#theme' => 'links__contextual',
      '#links' => $variables['elements']['#links'],
      '#attributes' => array('class' => array('contextual-links')),
      '#attached' => array(
        'library' => array(
          'contextual/drupal.contextual-links',
        ),
      ),
      '#weight' => -10,
    );
    $variables['attributes']['class'][] = 'contextual-links-region';
  }

}

This added some links to edit the content type (although I never got them to render).

Finally, I just rolled my own:

$entity_view = $render_controller->view($block);

$entity_wrapper = [
  '#type' => 'container',
  '#attributes' => [
    'id' => ['block-' . $block->id()],
    'class' => ['example-container-class'],
  ],
];

$entity_wrapper['content'] = [];

$edit_link = $block->toLink()->toRenderable();
$edit_link['#title'] = t('Edit');
$edit_link['#attributes'] = [
  'class' => ['edit-link'],
];
$entity_wrapper['content'][] = $edit_link;
$entity_wrapper['content'][] = $entity_view;

$build[] = $entity_wrapper;

After that, I dug around a little and found a way to render the built-in contextual links for block_content entities. You have to do a little legwork while building up your render array:

use Drupal\contextual\Element\ContextualLinks;

First load up your block_content entity:

In this case, I'm only expecting one of this bundle or type:

 $blocks = \Drupal::entityManager()->getStorage('block_content')->loadByProperties(['type' => 'footer_middle']);
    if (!empty($blocks)) {
      $block = array_shift($blocks);

}

Now we render the entity using the entity manager:

  $render_controller = \Drupal::entityManager()->getViewBuilder('block_content');
  $footer_middle = $render_controller->view($block);

The really tricky part is that block_content entities don't have a theme function or template, so you have to add the contextual links to a parent item, such as a container:

   $build['footer_middle'] = [
      '#type' => 'container',
      '#attributes' => [
        'id' => 'footer-middle',
        'class' => ['footer-container'],
      ],
    ];

      $build['footer_middle']['#contextual_links'] = $footer_middle['#contextual_links'];


Now we need to generate the links:

      $footer_middle = ContextualLinks::preRenderLinks($footer_middle);

Then set up the render array on the parent container, passing in the rendered links:

  $build['footer_middle']['links'] = array(
  '#prefix' => '<div class="contextual-links-wrapper">',
  '#suffix' => '</div>',
  '#theme' => 'links__contextual',
  '#links' => $footer_middle['#links'],
  '#attributes' => array('class' => array('contextual-links')),
  '#attached' => array(
    'library' => array(
      'contextual/drupal.contextual-links',
    ),
  ),
  '#weight' => -10,
);

And now add the rendered entity as a child of the container:

$build['footer_middle'][] = $footer_middle;

And now you have contextual links for your block_content entity!

Just make sure your container.html.twig is outputting

  {{ title_suffix }}

Since that's where the contextual drupal module puts it.

1
  • Don't like using the pencil?
    – No Sssweat
    Jun 2, 2016 at 23:13

1 Answer 1

1

You probably want to have a look at contextual_preprocess.

If you add this to the render element or variables array before contextual_preprocess is called, it will handle the rest and add the stuff to title_prefix. Note I haven't tried this as an alter, only on entities I've created myself.

$render_element_or_variables['#contextual_links']['ENTITY_ID'] = [
  'route_parameters' => ['ENTITY' => $ID_OF_ENTITY],
];

If it's a render element, you should add it to the element key otherwise it should be the root of the array in the process/preprocess function you use.

1
  • Thanks, I have looked at contextual_preprocess, and hook_preprocess in theme.api.php. I see this data in the array, but it isn't outputting. I think you'd have to copy what's in hook_preprocess in theme.api.php, but in the data structure in hook_preprocess varies, sometimes the key #contextual_links is in $variables['elements'], sometimes it's in $variables['element'], and it doesn't render out of the box. This answer isn't sufficient, unfortunately, there's still a missing step. For example, on an entity reference field displaying blocks, this data is there, but it doesn't render.
    – oknate
    Jun 6, 2016 at 15:45

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.