Problem/Motivation

The issue happens when the module ui_patterns is enabled.

I use a custom component to embed a form :

$schema: https://git.drupalcode.org/project/sdc/-/raw/1.x/src/metadata.schema.json
name: Modal
description: Modal
group: Organisms
status: experimental
slots:
  modal_body:
    title: Modal body
props:
  type: object
  required:
    - modal_id
  properties:
    modal_id:
      type: string
      title: Modal id
      description: The id used in javascript to show/hide the modal. MUST be unique across the DOM
    modal_header_title:
      type: string
      title: Text in the modal header
    modal_content:
      type: string
      title: Text in the modal
    additional_classes:
      type: array
      items:
        type: string
      title: Additional classes

This component is used as render array in a plugin block to include a form (Core user login form) in the modal_content property :

/**
 * My custom login modal block.
 */
#[Block(
  id: "my_module_header_mon_compte_block",
  admin_label: new TranslatableMarkup("My module : Header MonCompte block"),
)]
class MonCompteModalBlock extends BlockBase implements ContainerFactoryPluginInterface {
...

/**
   * Create the custom login modal block.
   *
   * @return array
   *   A render array.
   */
  #[\Override]
  public function build(): array {
      return [
        '#type' => 'component',
        '#component' => 'my:modal',
        '#props' => [
          'modal_id' => $modal_id,
          'modal_header_title' => $this->t('Mon espace'),
          'modal_content' => [
            '#theme' => 'modal_mon_espace',
            '#user_login_form' => $this->formBuilder->getForm(UserLoginForm::class),
          ],
          'additional_classes' => ['-user'],
        ],
      ],
  }
...

Then this form is altered for ajaxified purpose :

/**
 * Implements hook_form_alter().
 */
function my_module_form_alter(array &$form, FormStateInterface $form_state, string $form_id): void {
  ...
  
    $form['#prefix'] = '<div id="my_module_form_login_wrapper"><div id="my_module_form_login_wrapper_errors"></div>';
    $form['#suffix'] = '</div>';
    $form['actions']['submit']['#ajax'] = [
      'wrapper' => 'my_module_form_login_wrapper',
      // AJAX submit for checking any errors from core user login form validations. 
      'callback' => ['_my_module_ajax_submit_login_form'],
      'event' => 'click',
    ];
    $form['#attributes']['class'][] = '-modal-form';

  ...
}

Result => ajax does not seem to be applied. None related JS events bind to the submit handler.

Steps to reproduce

  1. Enable ui_patterns module
  2. Go to the page where block containing modal form component is displayed
  3. Ajax does not seem to be applied
  4. Disable ui_patterns module
  5. Go again to the page where block containing modal form component is displayed
  6. This time, ajax submit is working

Comments

a.depret created an issue. See original summary.

a.depret’s picture

Issue summary: View changes
a.depret’s picture

Issue summary: View changes
smovs’s picture

Assigned: Unassigned » smovs
smovs’s picture

Assigned: smovs » Unassigned
Status: Active » Reviewed & tested by the community

Hi @a.depret!

I tested your issue locally, and the problem is that modal_content is declared in props rather than in slots. Props are meant for scalar values (strings, numbers, booleans), while slots are meant for renderable content like render arrays, and that distinction matters here.

When you put a render array into a string prop, ui_patterns has to coerce it into a string. That coercion happens in UiPatternsNormalizer::convertArrayToScalar(), which calls:
$value = (string) $this->renderer->renderInIsolation($array);
The catch is that renderInIsolation() renders in a sandbox on purpose - it does not bubble #attached (JS libraries, drupalSettings, cache metadata) up to the page.

When modal_content is a slot instead, the value goes through SlotPropType::normalize(), which keeps the render array intact. Drupal then renders it in the normal render context alongside the rest of the component, so #attached bubbles up the way it normally would, the AJAX library and settings reach the page, and the submit handler binds correctly.

So the fix is just to move modal_content out of props properties and into slots.
Your block code doesn't need to change, 'modal_content' => [render array] is exactly what a slot expects. After drush cr, AJAX should work again.

Hope this helps! Let me know if you still have an issue with that.

smovs’s picture

Status: Reviewed & tested by the community » Postponed (maintainer needs more info)
a.depret’s picture

Priority: Normal » Minor
Status: Postponed (maintainer needs more info) » Closed (works as designed)

Hi @smovs !

Thanks a lot for your support.
With your full explanations, it worked like a charm !

My knowledge of the components is very limited, so sorry to having disturbed you.

Now that this issue is closed, review the contribution record.

As a contributor, attribute any organization that helped you, or if you volunteered your own time.

Maintainers, credit people who helped resolve this issue.