TL3: expose self-closing model-variable processors' values to sibling markup#13
Conversation
…ling markup
The TL3 base-class fix (removeTags + setLocalVariable) only makes contributed
values visible to the host element's body. NamedOrderProcessor and the deprecated
GoogleAnalyticsProcessor are documented as self-closing elements whose value is
read by a following sibling (<span>/<script>), so a body-scoped local variable is
never visible. Add an addToRequest() helper that sets a request attribute (read
live by Thymeleaf's WebEngineContext as ${var}) and use it in those two
processors to restore page-wide visibility.
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
| structureHandler.setLocalVariable(key, value); | ||
| } |
There was a problem hiding this comment.
🚩 Other self-closing processors may also need the addToRequest pattern
Several other subclasses of AbstractModelVariableModifierProcessor still use addToModel (local variable scoping) and may also be used as self-closing elements whose values are consumed by sibling markup. For example:
ConfigVariableProcessor.java:60setsresultVarviaaddToModelBreadcrumbProcessor.java:66setsresultVarviaaddToModelDataDrivenEnumerationProcessor.java:91setsenumValuesviaaddToModelRatingsProcessor.java:66,75sets rating data viaaddToModelRelatedProductProcessor.java:85-88sets product data viaaddToModel
If any of these are used as self-closing elements (e.g., <blc:config ... />), the local variable will have no body to scope into and the value will be invisible to siblings. This is the same problem this PR aims to fix for NamedOrderProcessor and GoogleAnalyticsProcessor. Each processor's usage pattern in templates would need to be audited.
(Refers to lines 74-77)
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
Valid follow-up. This PR is deliberately scoped to the two processors whose javadoc explicitly documents self-closing + sibling usage:
NamedOrderProcessor:<blc:named_order orderVar="wishlist" .../>then<span th:text="${wishlist...}"/>GoogleAnalyticsProcessor:<blc:googleanalytics/>then<script th:utext="${analytics}"/>
The base-class addToRequest() helper is now available for any others confirmed to need it. The remaining candidates split two ways:
ConfigVariableProcessor,BreadcrumbProcessor,DataDrivenEnumerationProcessorlive in thecommonmodule, outside this task'sbroadleaf-framework-webscope.RatingsProcessor,RelatedProductProcessorare inbroadleaf-framework-web, but their javadocs don't document a self-closing+sibling pattern, and the prior merged fix intentionally kept them on the body-scopedremoveTags()approach.
Confirming which are actually used as self-closing elements requires the demo-site templates, which aren't in this repo, so I'd recommend a dedicated template-usage audit as the follow-up rather than blindly switching them. Note addToRequest is strictly broader than a body-local (a request attribute is visible both inside the body and to siblings), but it persists for the whole request, so local-variable scoping remains preferable wherever a body actually consumes the value.
Use the codebase's established BroadleafRequestContext.getRequest() pattern
(as in ContentProcessor/ResourceBundleProcessor after TL3.1 removed
IWebContext#getHttpServletRequest) to set the request attribute, instead of
casting the Thymeleaf context. WebEngineContext resolves ${var} from request
attributes, so siblings see the value.
Summary
Follow-up to the Java 21 / Thymeleaf 3 migration. The merged base-class fix in
AbstractModelVariableModifierProcessorswitchedremoveElement()→removeTags()so a contributedsetLocalVariablestays visible to the host element's body. That correctly handles processors used as wrappers, but two processors are documented as self-closing elements whose value is read by a following sibling, where a body-scoped local variable is never visible:In TL3 a local variable is scoped to the element body, so for a self-closing element it can't reach a sibling. The page-wide equivalent of TL2's old "add to model" is a request attribute: Thymeleaf's
WebEngineContext.getVariable(name)resolves live fromwebExchange.getAttributeValue(name), so a request attribute is readable as${name}anywhere later in the page.Change
New base-class helper (pure addition; does not change
addToModel/removeTagsbehavior used by the wrapper-style processors):NamedOrderProcessorandGoogleAnalyticsProcessornow calladdToRequest(context, ...)instead ofaddToModel(structureHandler, ...), restoring their documented sibling-visible behavior. The other subclasses (CategoriesProcessor,ProductOptionsProcessor,RatingsProcessor,RelatedProductProcessor) are unchanged and keep the merged body-scoped approach.Verification
export JAVA_HOME=/usr/lib/jvm/java-21-openjdk-amd64 && mvn -q -pl common,core/broadleaf-framework-web -am clean install -Pblc-development -DskipTests→ BUILD SUCCESS.commonunit tests pass.broadleaf-framework-webintroduces no new test failures; the only failing tests are the 3 pre-existing*.taglib.*tests (CategoryBreadcrumbTagTest,CategoryTagTest,GetProductsByCategoryIdTagTest) that fail under JPMS because EasyMockclassextension/cglibcan'tsetAccessibleonClassLoader.defineClass(InaccessibleObjectException) — identical to the already-mergedbroadleaf-profileBatchRetrieveDaoTestfailure and unrelated to this change.No tests, license-header, jacoco, or animal-sniffer gates were weakened.
Link to Devin session: https://partner-workshops.devinenterprise.com/sessions/b9de3eef877542ac889ca59866907d66
Requested by: @mbatchelor81