Wordpress Gutenberg supports dynamic block use-case (through the use of render_callback ) where developers are capable of doing the rendering on the backend rather than saving it as HTML for the frontend.

This allows us to accommodate scenarios where the custom blocks are likely to change in the future and will be used on hundreds of pages.

Since Gutenberg natively stores the content as HTML, there's no easy way to systematically update the content's HTML without opening the individual nodes and re-saving the content.

Is this something that you're willing to support in the future? I'm happy to help with its implementation if it's something that fits with the goals of the project.

My rough plan for how this would be implemented is:

  • Declare the list of blocks and their render callbacks in a hook. If a callback isn't specified, it can fallback to a default callback declared in the module which returns a theme render array for the dynamic block (with theme suggestions based on the namespace and block name so it can be specifically targeted by the theme)
  • Implement an editor filter similar to MediaEntityBlockFilter that picks up all the dynamic block definitions on the content, and then passes ($block_name, $block_properties) into the callback which will return a render array.

An example of the hook implementation would be:

/**
 * Add dynamic block definitions.
 *
 * Modules may implement this hook to add dynamic block definitions.
 *
 * @return array[]
 *   An array of dynamic block definitions to register, keyed by the full
 *   block ID including the namespace.

 * @see hook_gutenberg_dynamic_blocks_info_alter()
 */
function hook_gutenberg_dynamic_blocks_info_build() {
  $dynamic_blocks = [];

  // Render using a static callback.
  $dynamic_blocks['custom_module/block-name'] = [
    'render_callback' => '\Drupal\custom_module\GutenbergBlockRenderer::callback',
  ];
  // Render using a simple function.
  $dynamic_blocks['custom_module/block-name3'] = [
    'render_callback' => '_custom_module_gutenberg_render_block',
  ];
  // Render from a service function.
  $dynamic_blocks['custom_module/block-name2'] = [
    'render_callback' => 'custom_module.custom_gutenberg_block_renderer_service::callback',
  ];

  return $dynamic_blocks;
}

The callback would be resolved using an API call like ControllerResolver::getControllerFromDefinition

Thoughts on this @marcofernandes? There's probably a better API in the module that would be better serve for this.

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

codebymikey created an issue. See original summary.

marcofernandes’s picture

Well, we already have support for Drupal blocks, which roughly are the equivalent of Wordpress's dynamic blocks. What is not supported is the block parameters/configuration - https://www.drupal.org/project/gutenberg/issues/3000499
Drupal blocks on Gutenberg are dynamically rendered, there's filter for it.
Still, I'm wondering if it would be good to have this particular type of blocks specific for Drupal Gutenberg.

Anyway, I think we would gain much more value if the block configuration (parameters) was available when using Drupal blocks on Gutenberg editor.

codebymikey’s picture

Thanks for the quick update!, my implementation is for a slightly different use case though.

The use case is to have the admin UI preview be rendered in React/JavaScript (edit()), but for the frontend HTML (save()) to be rendered in the backend by Drupal without having to create/use a Drupal block, especially if it has configuration settings that might be difficult to easily integrate into the editor.

The main components would be JS (for the edit preview) + twig (for the frontend html). And it'd be able to work with any block declared by the developer as supporting dynamic rendering.

Exposure of this API would also open up integration with the ServerSideRender API.

I'm sure this could potentially be done as a third party module, but my wish is for the main Drupal module to provide most of the same features as the Wordpress one without having to rely on a different module.

I guess I'm just looking for an answer as to whether this feature is something you'd be willing to merge into the project if implemented (and whether my API hook approach is fine, or would it be better declared in module.gutenberg.yml?).

codebymikey’s picture

Hi @marcofernandes,

I've implemented the dynamic render feature and took the liberty to attempt to improve the block processing architecture so that it can be easily extended by other themes/modules in the future.

The API approach initially suggested in the issue has been abandoned in favour of declaring them in the .gutenberg.yml file as:

dynamic-blocks:
  namespace/block-name: {}

And then declaring a relevant theme template like gutenberg-block--namespace-block-name.html.twig which will have access to the block attributes and inner html content and render accordingly. The blocks can also be preprocessed by modules and themes as needed.

I've attached the patch as a proof of concept and waiting on feedback from the maintainers/community.

The changes of note are:

  1. Use the WordPress parser so that blocks are easier to work with by making sure they are in a standardized format. It also avoids handling weird edge-cases where the regex doesn't match because of the inner content structure.
  2. Add gutenberg_block_processor tag services which act upon the blocks in a single-pass (ordered by priority) without having to enable multiple filters. Block processing will all be handled by a single Gutenberg filter. Currently when a new filter functionality is added, it needs to be manually enabled after updating as there's no hook_update_N for it - with this approach, new processors can be added as services without needing the UI.
  3. Integrate the drupalmedia block with the dynamic render block API so that the output and twig.html can be extended by themes/modules.
  4. Use a GutenbergLibraryManager Plugin Manager to manage all the .gutenberg.yml definitions, so they're cached and the definition API can be extended in the future.
  5. Inherit .gutenberg.yml theme library definitions from their base themes.
  6. Update gutenberg.schema.yml to include the new Gutenberg filter settings.
  7. Add demo of the dynamic render API in the example_block module.

Couple of TODOs:

  1. Reimplement OEmbedProcessor as a gutenberg_block_processor tag service after getting input from @marcofernandes. Thoughts on relying on the media module to do the heavy lifting here?
  2. Run the ReusableBlockProcessor under the Gutenberg text format filter so that dynamic blocks like embeds and media-entities are functional.
  3. Remove all the other filters (as well as an update hook) after the feature has landed.
  4. Remove usages of _gutenberg_get_default_theme_settings and _gutenberg_get_all_modules_settings, then delete them.
codebymikey’s picture

Status: Active » Needs review
codebymikey’s picture

Title: Thoughts on adding support for dynamic blocks? » Dynamic render blocks support (API rework using WordPress block parser rather than regex)
Category: Plan » Feature request

  • codebymikey committed 119603d on 8.x-1.x-3107797-dynamic-render
    Issue #3107797 by codebymikey: Add dynamic render block support...
remydenton’s picture

I have the same use case described in #3 (i.e. I'd like to handling rendering of the frontend HTML with Drupal's theming system). The ideas of using the WordPress parser and consolidating filters in a single Gutenberg filter both seem beneficial for the module as a whole regardless of this particular use case. That said, I've just started using this module-- curious to hear from maintainers about the underlying approach put fourth in #4.

codebymikey’s picture

We'll be going with approach #4, and technically works, but it's still currently under development as it involves a lot of code changes, so would advice against using that branch for production sites.

But once finalized and tested, it'll be merged into the main branch and scheduled for a release.

  • codebymikey committed cc8c062 on 8.x-1.x-3107797-dynamic-render
    Issue #3107797 by codebymikey: Use the new oEmbed resolver for the...
codebymikey’s picture

Assigned: Unassigned » codebymikey

I believe the https://git.drupalcode.org/project/gutenberg/-/tree/8.x-1.x-3107797-dyna... branch is now stable enough for testing and merging into the 1.x branch.

Since it's a big rewrite, I'd like other users to test as well to ensure that certain feature's haven't regressed.

I'll look into applying the dynamic rendering refactor and bugfixes on the 2.x branch as well.

edit: The dynamic rendering functionality should be part of the 2.x release rather than 1.x due to the amount of internal API changes.

  • codebymikey committed 119603d on 8.x-2.x-3107797-dynamic-render
    Issue #3107797 by codebymikey: Add dynamic render block support...
  • codebymikey committed cc8c062 on 8.x-2.x-3107797-dynamic-render
    Issue #3107797 by codebymikey: Use the new oEmbed resolver for the...

  • codebymikey committed 8293e1c on 8.x-2.x-3107797-dynamic-render
    Issue #3107797 by codebymikey: Use the new filter on config/install.

  • codebymikey committed 119603d on 8.x-2.x
    Issue #3107797 by codebymikey: Add dynamic render block support...
  • codebymikey committed 8293e1c on 8.x-2.x
    Issue #3107797 by codebymikey: Use the new filter on config/install.
  • codebymikey committed cc8c062 on 8.x-2.x
    Issue #3107797 by codebymikey: Use the new oEmbed resolver for the...
MauMau’s picture

Will this enhancement give the end user control like they have in Wordpress?
https://wpforthewin.com/remove-related-videos-wp-gutenberg-embed-blocks/
(I've also put this question to the people caring about the 8.x-1.11 branch: https://www.drupal.org/project/gutenberg/issues/3173760 )

szeidler’s picture

Version: 8.x-1.x-dev » 8.x-2.x-dev

I think we can consider this issue to be fixed? This has been included in 8.x-2.x for quite some time and seem to work.

codebymikey’s picture

Status: Needs review » Fixed

Yes, as this is currently part of the 2.0 release, I'd mark this as fixed.

@MauMau, yes, it'd make it easier to do stuff like that going forward (without using a filter).

It requires some Symfony/Drupal knowledge (something that's probably out of scope of this issue).

You'd have to register a block processor service (with a gutenberg_block_processor tag) here examples available here:

https://git.drupalcode.org/project/gutenberg/-/blob/ded5d2da/gutenberg.s...

https://git.drupalcode.org/project/gutenberg/-/blob/ded5d2da/src/BlockPr...

I think on further reflection, I think this API would work better via a plugin annotation rather than a service tag (something that can be addressed in a future release) since only one file would be required and the annotation permits itself to being easily altered via hooks.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.