Problem/Motivation
\Drupal\entity_browser\Plugin\EntityBrowser\Widget\View::getForm() renders the view to a string at the end of the method:
$form['view']['view'] = [
'#markup' => $this->renderer->render($form['view']['view']),
];
Renderer::render() bubbles the view's bubbleable metadata (#attached libraries/settings and #cache) into the current render context, but the new #markup element that is stored in $form does not carry it.
On the first request this is not visible, because the metadata reaches the page through the render context. However, the entity browser form is cached by the Form API. When it is submitted and validation fails (for example "You can only select one item." when too many entities are selected for a field with cardinality 1), FormBuilder::buildForm() loads the form from the form cache and getForm() is not called again. The cached array only contains the #markup string, so every library the view attached is missing from the re-rendered page.
The result is that the view loses its styling (and any JS behaviors it needs) as soon as the user hits a validation error: grid layouts collapse into a single column, image/lazy-loading libraries are missing, etc.
On a real site we saw media_entity_browser/view, blazy/css and views/views.module disappear after the validation error, which turned the media grid into a full-width list. Comparing drupalSettings.ajaxPageState / the aggregated CSS before and after the failed submit confirms that only the view's own libraries are lost; libraries attached directly to the form (e.g. entity_browser/entity_browser) are kept.
The same code is present in 8.x-2.x-dev.
Steps to reproduce
- Install Drupal 11 (standard profile) with Entity Browser 8.x-2.x-dev and enable the
entity_browser_examplemodule - Create at least two nodes of any type, so the browser has something to list.
- Edit the view Nodes entity browser (
nodes_entity_browser), displayentity_browser_1, and change Format from Table to Grid (responsive). Save the view. (The responsive grid template attachesviews/views.responsive-gridviaattach_library(), so its CSS is only on the page if the view's attachments make it there.) - Change the field storage of field_nodes on the Entity browser test content type to an allowed number of values of 1
- Go to
/node/add/entity_browser_testand open the entity browser of the Nodes field. - Observe the grid layout.
views-responsive-grid.cssis loaded in the iframe. - Tick two nodes and press Select entities.
Expected: the error "You can only select one item." is shown and the view still renders as a responsive grid.
Actual: the error is shown, but views-responsive-grid.css is no longer loaded in the iframe and the grid falls back to unstyled markup. Any other library attached by the view (style plugins, field formatters, templates) is lost the same way.
Proposed resolution
Keep the view's bubbleable metadata on the element that replaces it, so it is stored in the form cache together with the markup:
$view_build = $form['view']['view'];
$form['view']['view'] = [
'#markup' => $this->renderer->render($view_build),
];
BubbleableMetadata::createFromRenderArray($view_build)->applyTo($form['view']['view']);
After render(), $view_build contains the bubbled #attached and #cache of the whole view, so applying it to the #markup element makes the cached form self-contained. Patch attached.
Remaining tasks
- Review the patch.
- Add test coverage: a FunctionalJavascript (or Functional) test that triggers a validation error in a View widget whose view attaches a library, and asserts that the library is still present after the failed submit.
User interface changes
None. The view keeps its styling after a validation error.
API changes
None.
Data model changes
None.
| Comment | File | Size | Author |
|---|---|---|---|
| #4 | 3632657 ajax.png | 410.31 KB | csakiistvan |
| #4 | 3632657 .png | 579.09 KB | csakiistvan |
| #2 | entity_browser-view-widget-keep-attachments-3632657.patch | 1.16 KB | id.conky |
Comments
Comment #2
id.conky commentedComment #3
csakiistvanComment #4
csakiistvan❓ Needs more info — could not reproduce before applying the patch: with
entity_browser_example,nodes_entity_browserswitched to Grid (responsive) andfield_nodesset to 1 value, ticking two nodes and pressing Select entities shows "You can only select one item." andviews-responsive-grid.cssis still loaded, the grid intact; same with AJAX and a pager enabled on the view. The entity browser form never landed in the form cache here, sogetForm()ran again on the failed submit. Which Drupal core version, admin theme and any other setup (e.g. media_entity_browser, form caching) does it need to show up?