Problem/Motivation

Component library is missing when it is not rendered via Twig.

If rendered with Drupal JavaScript API - such as a custom Drupal.theme.myComponent for instance - the component's library is not loaded.

Steps to reproduce

  • Create a single-directory component in a module
  • Render the component programmatically
  • Attach the output in drupalSettings
  • Create a custom Drupal.theme.myComponent function and consume the output
  • Call the Drupal.theme
  • Result: the component Twig is rendered but the library is missing (i.e. no CSS no JS)

Proposed resolution

TBD

Remaining tasks

TBD

Comments

matthieuscarset created an issue. See original summary.

matthieuscarset’s picture

The scenario which leads me to this issue was the following: How to use a SDC to theme Drupal messages individually?

Since Drupal 10.3 and this bug in particular, there is no way for individual message to use a Twig template. Each Drupal messages are rendered via Drupal.theme.message.

Consequently there is no other choice than implementing a custom Drupal.theme.message method in a custom theme or a custom module and - if one wants to use a SDC to render messages - I don't see any other way than to pass the component output to drupalSettings to be reused in JS.

Example code in this snippet: https://git.drupalcode.org/-/snippets/214

pdureau’s picture

Each Drupal messages are rendered via Drupal.theme.message.

So, maybe we need to fix the root cause instead of introducing a workaround: #3456176: 10.3 upgrade now missing status-message theme suggestions

matthieuscarset’s picture

This is "the root cause" for this specific scenario (e.g. using SDC to theme Messages). The feature request is still relevant for any other use cases such as printing mycomponent:xyz using a custom Drupal.theme.xyz in JS.

If developers needs to print a single-directory component via JavaScript, the component fails its promises because it is missing its library.

The current workaround is to manually attach the library (see snippet above and code below) but the purpose of this issue is to find a sustainable solution in core.


/**
 * Implements hook_page_attachments().
 */
function mymodule_page_attachments(array &$attachments) {
  // ... see snippet for a complete example.
  $attachments['#attached']['drupalSettings']['mymodule']['components'] = $rendered_component_output;

  // Attach libraries here otherwise no CSS and no JS 😅.
  $component = \Drupal::service('plugin.manager.sdc')->createInstance($plugin_id, $configuration);
  $attachments['#attached']['library'][] = $component->getLibraryName() ?? '';
}
pdureau’s picture

So, we need to add attach the asset library from ComponentElement render element in preRenderComponent() method?

The asset library is already attached in ComponentNodeVisitor:

    $print_nodes[] = new PrintNode(new FunctionExpression(
      new TwigFunction('attach_library', [$env->getExtension(TwigExtension::class), 'attachLibrary']),
      new Node([new ConstantExpression($component->getLibraryName(), $line)]),
      $line
    ), $line);

It would be the dream to remove ComponentNodeVisitor and move all the logic to ComponentElement but it is not possible yet because it will be a compatibility break for the poor souls using Twig include or embed to call a component template.

So, if we add asset library attachment in ComponentElement, the library will be attached twice. It is supposed to be OK because the asset library API takes care of this, but we need to be sure there is no side effects.

quietone’s picture

Version: 11.0.x-dev » 11.x-dev

Changes are made on on 11.x (our main development branch) first, and are then back ported as needed according to our policies.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.