Granite Render Condition in AEM
Hello, AEM devs ( Maybe just the newbies like me, the pros know it already) !!
If anyone is wondering what this Granite Render Condition is, it’s basically a way to conditionally show or hide a certain portion of the UI or Dialog in AEM based on a given condition. Ex- Maybe you want to hide a field on a page property when a certain template is being used to create the page or the opposite i.e. to show a certain field only when a certain template or any one of a few templates is being used. Or maybe you want to show a certain field only when the user has the required access right. Or perhaps you are just like me who needs nothing and just playing around with AEM to learn it.
Now let’s try to learn a bit more about these render conditions and how we will use them in our projects.
There are five different kinds of render conditions
For today’s article, we will focus only on “Simple” and “Privilege” and our custom render condition.
Below is an example of a simple render condition. I have added the render condition inside the homepageText node. The sling:resourceType inside the granite:rendercondition defines the kind of render condition to be used. The expression property defines the logic based on which a boolean response is achieved and used for the rendering.
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
jcr:primaryType="nt:unstructured"
jcr:title="Properties"
sling:resourceType="cq/gui/components/authoring/dialog">
...
<homepageText
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="HomePage Text"
name="./homepageText">
<granite:rendercondition jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/renderconditions/simple"
expression="${granite:containsIgnoreCase(requestPathInfo.suffix, '/en/home-page')}"/>
</homepageText>
...
</jcr:root>
This was the OOB simple render condition. Now we will write our custom render condition to show or hide the field in our component/page property ( I will be doing it for the component as that is where we got stuck a few days ago and most of the blogs we came across were implemented for page properties only).
Steps to be followed:
- Go to the cq:dialog of the component and add the granite:render condition node inside the dialog field for which you want the render condition to execute. Below is a sample code.
<templateBasedText
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Template Based Text"
name="./templateBasedText">
<granite:rendercondition
jcr:primaryType="nt:unstructured"
sling:resourceType="wknd/components/templateRenderCondition"
allowedTemplates="article-page-template" />
</templateBasedText>
- The sling:resourceType inside our granite:rendercondition is our custom render condition. So now we will need to create a component named templateRenderCondition inside apps/wknd/components/ and use the sling model in our templateRenderCondition.html so that our custom logic can execute.
This is the structure for the folder.
templateRenderCondition.html
<sly data-sly-use.templateRenderCondition="com.adobe.aem.guides.wknd.core.models.TemplateRenderCondition"/>
- Now we need to create the Java sling model in Core (Where all your existing sling models are) which we are using inside our templateRenderCondition.html above.
package com.adobe.aem.guides.wknd.core.models;
import java.util.Arrays;
import java.util.List;
import javax.annotation.PostConstruct;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adobe.granite.ui.components.rendercondition.RenderCondition;
import com.adobe.granite.ui.components.rendercondition.SimpleRenderCondition;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
@Model(adaptables = SlingHttpServletRequest.class)
public class TemplateRenderCondition {
private static final Logger logger = LoggerFactory.getLogger(TemplateRenderCondition.class);
@Self
private SlingHttpServletRequest request;
@SlingObject
private ResourceResolver resourceResolver;
@ValueMapValue
private String allowedTemplates;
@PostConstruct
public void init() {
boolean isAllowedTemplate = false;
try {
List<String> allowedTemplatesList = Arrays.asList(allowedTemplates.split(","));
String currentPagePath = extractPath(request.getPathInfo());
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
Page currentPage = pageManager.getPage(currentPagePath);
if (currentPage != null) {
String currentTemplate = currentPage.getTemplate().getName();
isAllowedTemplate = allowedTemplatesList.contains(currentTemplate);
logger.debug("Current template: {}, Allowed templates: {}, isAllowedTemplate: {}",
currentTemplate, allowedTemplatesList, isAllowedTemplate);
} else {
logger.warn("Current page is null for path: {}", currentPagePath);
}
} catch (Exception e) {
logger.error("Exception occurred in PostConstruct init: {}", e.getMessage());
}
request.setAttribute(RenderCondition.class.getName(), new SimpleRenderCondition(isAllowedTemplate));
logger.debug("Render condition set to: {}", isAllowedTemplate);
}
private static String extractPath(String fullPath) {
String startString = ".html";
String endString = "/jcr:content";
int startIndex = fullPath.indexOf(startString) + startString.length();
int endIndex = fullPath.indexOf(endString);
if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) {
return fullPath.substring(startIndex, endIndex);
} else {
return null;
}
}
}
request.setAttribute(RenderCondition.class.getName(), new SimpleRenderCondition(isAllowedTemplate)); => This is the line responsible for our render condition to actually work. The value of isAllowedTemplate will decide whether to show the fields or not. If it is true after the execution of your logic, you will see the fields, else you won’t.
According to the above-written code “Template Based Text” field will only appear on the pages created with article-page-template. You can tweak the logic if you wish to hide the field for “article-page-template ”.
Following the same steps as above you can also create a custom render condition that works based on the group of the logged in user. Below is the code :
Cq:dialog Node ⬇️
<adminOnlyText
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Admin Only Text"
name="./adminOnlyText">
<granite:rendercondition
jcr:primaryType="nt:unstructured"
sling:resourceType="wknd/components/groupRenderCondition"
allowedGroupsConfig="administrators"/>
</adminOnlyText>
groupRenderCondition.html ⬇️
<div data-sly-use.renderCondition="com.adobe.aem.guides.wknd.core.models.GroupRenderCondition"></div>
Sling Model GroupRenderCondition.java ⬇️
package com.adobe.aem.guides.wknd.core.models;
import java.util.Arrays;
import java.util.List;
import javax.annotation.PostConstruct;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.Self;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adobe.granite.ui.components.rendercondition.RenderCondition;
import com.adobe.granite.ui.components.rendercondition.SimpleRenderCondition;
import com.day.cq.wcm.api.Page;
import com.day.cq.wcm.api.PageManager;
@Model(adaptables = SlingHttpServletRequest.class)
public class TemplateRenderCondition {
private static final Logger logger = LoggerFactory.getLogger(TemplateRenderCondition.class);
@Self
private SlingHttpServletRequest request;
@SlingObject
private ResourceResolver resourceResolver;
@ValueMapValue
private String allowedTemplates;
@PostConstruct
public void init() {
boolean isAllowedTemplate = false;
try {
List<String> allowedTemplatesList = Arrays.asList(allowedTemplates.split(","));
String currentPagePath = extractPath(request.getPathInfo());
PageManager pageManager = resourceResolver.adaptTo(PageManager.class);
Page currentPage = pageManager.getPage(currentPagePath);
if (currentPage != null) {
String currentTemplate = currentPage.getTemplate().getName();
isAllowedTemplate = allowedTemplatesList.contains(currentTemplate);
logger.debug("Current template: {}, Allowed templates: {}, isAllowedTemplate: {}",
currentTemplate, allowedTemplatesList, isAllowedTemplate);
} else {
logger.warn("Current page is null for path: {}", currentPagePath);
}
} catch (Exception e) {
logger.error("Exception occurred in PostConstruct init: {}", e.getMessage());
}
request.setAttribute(RenderCondition.class.getName(), new SimpleRenderCondition(isAllowedTemplate));
logger.debug("Render condition set to: {}", isAllowedTemplate);
}
private static String extractPath(String fullPath) {
String startString = ".html";
String endString = "/jcr:content";
int startIndex = fullPath.indexOf(startString) + startString.length();
int endIndex = fullPath.indexOf(endString);
if (startIndex != -1 && endIndex != -1 && startIndex < endIndex) {
return fullPath.substring(startIndex, endIndex);
} else {
return null;
}
}
}
There is one more render condition that we get out of the box, which is privilege. This is used to render the field based on the logged-in user's access right on a certain path.
<privilegeText
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Privilege Text"
name="./privilegeText">
<granite:rendercondition
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/renderconditions/privilege"
path="${requestPathInfo.suffix}"
privileges="jcr:write" />
</privilegeText>
The path property defines the “path ”on which we check for the access right, “privileges” are the privileges that the current user needs to have on the given path to see the UI. This comes in handy for hiding certain buttons for certain resources based on the user’s access right.
That was almost all that I had for this blog. You can share your opinions with me in the comments. If you find any mistakes in this, inform me about them and give me a chance to learn and rectify them.
Also, you can reach out to me over here.
Thanks for reading!