Score:0

How to place 2 blocks on same page which are based off page's rendered content?

us flag

To be honest, not sure of a good title for this. This is the issue:

I am using the TOC API to create a block. This API requires parsing the node content to determine the heading structure (H2s, H3s, etc). The module does not include a block for this as it is just an API module. I wrote a custom block to use the API to create this block. This works fine.

I now have a use case where I need to put multiple versions of this block on the page, but I am only able to render one of them. This has nothing to do with the TOC API but how the node content is rendered in the block code. I have simplified the code to remove all the API parts:

  public function build() {
    // Required to break block - render page - insert block - render page, etc, etc.
    $rendered = &drupal_static('toc_block_rendered');
    if (isset($rendered) && in_array($this->configuration['toc_type'], $rendered)) return [];
    else $rendered[] = $this->configuration['toc_type'];

    $build = [];

    // Get TOC Type configured for this Block.
    $toc_type_id = $this->configuration['toc_type'];

    $node = \Drupal::routeMatch()->getParameter('node');
    $view_builder = \Drupal::entityTypeManager()->getViewBuilder('node');
    $full_content = $view_builder->view($node, 'full');
    $content = (string) \Drupal::service('renderer')->render($full_content);

    return ['#markup' => $this->configuration['toc_type']];
  }

$this->configuration['toc_type'] is a configuration value set for the block instance and is set differently for each of the 2 blocks I have placed on the page.

The static var code at the top is required to break the recursive loop caused due to trying to render the page which has the block which tries to render the page which has the block.. and so on and so on.

This simplified block just renders the block's config value. Although 2 of them are added to the node layout; only one gets shown. If I comment out the $view_builder->view($node, 'full') line; then both are displayed correctly.

I have tried multiple approaches such as static and even session vars for the rendered content; but with no luck. I also tried cloning or createDuplicate() of the node but this also does not help.

Can anyone explain why this is being broken by running this code for a 2nd instance of the block and if there is a better way to do this?

NOTE: the session var idea does work but the code I had for this isn't properly seeded the first time through. The 2nd refresh of the page shows both blocks. I would need a way to set for the first time and then also to clear these in a later hook (hook_node_view). But ugly approach.

4uk4 avatar
cn flag
What is correct? "the 2 blocks I have placed on the page" or "Although 2 of them are added to the node layout"
liquidcms avatar
us flag
I think i understand what is going on; when the first block is added, the next time the full node render happens it clears that block. This is why only the last block is displayed. I have found the right way to do this (for my specific use case). Each TOC block is only intended for 1 of 4 Body fields added to the node. I can simply render that single field to use in each block and i suspect this will work. Testing now.
Score:0
cn flag

Did you consider a #post_render callback to avoid the early and recursive rendering?

If you want to place the block via UI in the node layout then return from the block builds static placeholders. For example:

return ['#markup' => '<span toc-data="' . $this->configuration['toc_type'] . '"></span>'];

Otherwise you could simply put the placeholders directly in the node build in the same hook where you add the callback.

The post render callback receives the entire HTML of the node layout which it can use for the TOC API and then replace the placeholders.

See How to alter page content?

I sit in a Tesla and translated this thread with Ai:

mangohost

Post an answer

Most people don’t grasp that asking a lot of questions unlocks learning and improves interpersonal bonding. In Alison’s studies, for example, though people could accurately recall how many questions had been asked in their conversations, they didn’t intuit the link between questions and liking. Across four studies, in which participants were engaged in conversations themselves or read transcripts of others’ conversations, people tended not to realize that question asking would influence—or had influenced—the level of amity between the conversationalists.