I'm working on an AEM 6 project that uses Sightly as the templating language. I'm facing a use case, in which I want to show or hide certain parts of the markup depending on the presence of a Sling selector.
For example, a request to /content/my-project/my-page.html
should yield a basic page view and when a request is made to /content/my-project/my-page.ubermode.html
, Sling should return the same content represented by a slightly different HTML document.
According to the Sling Cheat Sheet, it should be possible to use a different script.
I managed to implement this in a component by placing two Sightly scripts, mycomponent.html
and ubermode.html
(named after the selector)
/apps/(...)/mycomponent
|- .content.xml
|- _cq_editConfig.xml
|- dialog.xml
|- mycomponent.html
|- ubermode.html
This does require some code duplication when it comes to the HTML structure but it works fine.
However, in this particular case, I need to do the same think at the renderer level (let's call this myapp/core/renderers/fancyPageRenderer
). What's more, the renderer has a different renderer as its sling:resourceSuperType
(let's call this parent renderer myapp/core/renderers/genericPageRenderer
) and relies on a moderately complex series of includes (data-sly-include
).
In fancyPageRenderer
, I override one of the scripts initially defined and included in genericPageRenderer
. This is the part that I'd like to be different when the ubermode
selector is used. Let's call this script mainColumn.html
I've tried different naming conventions to match the selector but none of them worked in a satisfactory way.
/apps/(...)/renderers/fancyPageRenderer
|- .content.xml
|- mainColumn.html //this overrides a script included by a parent renderer
/apps/(...)/renderers/fancyPageRenderer
|- .content.xml
|- mainColumn.uber.html
|- mainColumn.html
This simply didn't work and mainColumn.html
would be included every time.
/apps/(...)/renderers/fancyPageRenderer
|- .content.xml
|- uber.html
|- mainColumn.html
This caused the uber.html
script to be used but the resulting page did not contain any markup defined in the other scripts included in the genericPageRenderer
I imagine I could just copy all the relevant scripts and includes to fancyPageRenderer
but that would result in massive and completely unacceptable code duplication.
I also know that it's possible to manually add, remove or replace selectors using data-sly-resource
or just have the original selectors used but in my case, it's data-sly-include
and not data-sly-resource
that is widely used.
Is there an elegant way to work around this problem?
Eventually, I gave up on using script naming conventions to solve this problem and exposed a very simple Sling Model in the Sightly script of my renderer.
Here's the current structure of fancyPageRenderer
(which didn't change from the original):
/apps/(...)/renderers/fancyPageRenderer
|- .content.xml
|- mainColumn.html //this overrides a script included by the parent renderer
Here's what I used in the mainColumn.html
Sightly script:
<div class="fancy main-column" data-sly-use.uberMode="com.foo.bar.myapp.fancy.UberMode">
<div data-sly-test="uberMode.enabled" >Uber-mode-only-content</div>
<!-- Lots of markup here -->
<div data-sly-test="!uberMode.enabled" >Explicitly non-uber-mode content</div>
<div>Common content (but some uber-mode-dependend, nested divs as well, rendered the same way as above)</div>
</div>
and the underlying Sling Model, UberMode
@Model(adaptables = SlingHttpServletRequest.class)
public class UberMode {
@Inject
SlingHttpServletRequest request;
private boolean enabled = false;
@PostConstruct
public void postConstruct() {
if (request != null) {
List<String> selectors = Arrays.asList(request.getRequestPathInfo().getSelectors());
enabled = selectors.contains("ubermode");
}
}
public boolean isEnabled() {
return enabled;
}
}
This allows me to avoid code duplication in Sightly and makes the selector-based logic unit-testable. Also, relying on naming conventions would get really tricky in a situation when I'd require more than one selector to decide what to render. Adding support for another relevant selector to this class would be quite straightforward in comparison.
It also leaves me a lot of refactoring options. I could switch from using a selector to a query parameter or a header and only write a couple lines of code without even touching the Sigthly script that's effectively the client code of my class.