Display different markup in Sightly based on a Sling selector
aem, sightly, sling, sling-models
Solution
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.
Problem
The context 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. This was the initial structure ``` /apps/(...)/renderers/fancyPageRenderer |- .content.xml |- mainColumn.html //this overrides a script included by a parent renderer ``` Here's what I tried: ``` /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?