Accessing Component Policies in AEM via ResourceType-Based Servlet

How can I leverage component policies chosen at the template level to manage the dropdown-based selection?

AEM has integrated component policies as a pivotal element of the editable template feature. This functionality empowers both authors and developers to provide options for configuring the comprehensive behavior of fully-featured components, including deciding which fields to display, hide, or customize. This configuration is carried out at the template level, facilitating reuse across various templates or template-specific customizations.

Furthermore, AEM introduces a robust styling feature that empowers frontend developers to manage the visual appearance and user interface of components. This grants authors the capability to tailor the look and feel of the component.

Requirement:

In a previous discussion, we outlined a step-by-step process for Accessing Component Policies in AEM at the Component Dialog Level, Sightly code, and Sling model.

Now, consider a scenario where a company is selling various electronic devices in different countries. For example, a company like Brisket sells phones, tablets, and laptops in India, and only phones and tablets in the USA. However, if they plan to introduce laptops in the USA in the future, they’d like to make changes to the template policy. How can this be achieved at the dialog level?

Solution:

Step 1: L Create an “ElectronicsDS” component and add the design dialog with specific attributes:

ElectronicDS Design dialog

Step 2:  Create the dialog and add an extra granite node with a specific property:

ElectronicDS dialog

Step 3: When you open the component dialog and inspect the field, you will see extra attributes, including “component-path” and “field-path”.

Shows componet and feild paths

Step 4: Create a dialog-level listener that makes a resourceType based call to a Servlet using “field-path” and passes the attributes as shown.

(function($, $document, Granite, Coral) {

    var flag = false;
    var errorMessage = 'Error occurred during processing';

    $(document).on("foundation-contentloaded", function(e) {
        var value = $("coral-select[name='./country']").val();
        var fieldPath = $("coral-select[name='./devices']").attr("data-field-path");
        var compPath = $("coral-select[name='./devices']").attr("data-component-path");
        populateItems(fieldPath, compPath, value);
    });

    var REGEX_SELECTOR = "dropdown.selector",
        foundationReg = $(window).adaptTo("foundation-registry");

    foundationReg.register("foundation.validation.validator", {
        selector: "[data-foundation-validation='" + REGEX_SELECTOR + "']",
        validate: function(el) {
            if ($(el).is(":visible")) {
                var value = $(el).val();
                var fieldPath = $("coral-select[name='./devices']").attr("data-field-path");
                var compPath = $("coral-select[name='./devices']").attr("data-component-path");
                populateItems(fieldPath, compPath, value);
            }
        }
    });

    function populateItems(fieldPath, compPath, value) {
        dsUrl = Granite.HTTP.externalize(fieldPath) + ".html?componentPath=" + compPath + "&dropdownValue=" + value;
        $.ajax({
            type: "GET",
            async: false,
            url: dsUrl,
            success: function(result) {
                if (result) {
                        var select = document.querySelector("coral-select[name='./devices']");
                        var newHtml = document.createElement("div");
                        newHtml.innerHTML = result;
                        var newResult = newHtml.querySelector("coral-select[name='./devices']").children;
                        Coral.commons.ready(select, function(component) {
                            component.items.clear();
                            [...newResult].forEach((e) => {
                              component.items.add(e);
                            })
                        });
                    } else {
                        flag = true;
                    }
                }
        });
        if (flag) {
            return errorMessage;
        }
    }

}(jQuery, $(document), Granite, Coral));

Step 5: Create a registered path-based Servlet that accepts the component path as a parameter, retrieves the component policy, and returns dropdown options.

package com.aem.operations.core.servlets;

import com.adobe.granite.ui.components.ExpressionHelper;
import com.day.cq.wcm.api.policies.ContentPolicy;
import com.day.cq.wcm.api.policies.ContentPolicyManager;
import com.day.crx.JcrConstants;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.*;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.api.wrappers.ValueMapDecorator;
import org.jetbrains.annotations.NotNull;
import org.osgi.service.component.annotations.Component;
import com.adobe.granite.ui.components.Config;
import com.adobe.granite.ui.components.Value;
import com.adobe.granite.ui.components.ds.DataSource;
import com.adobe.granite.ui.components.ds.SimpleDataSource;
import com.adobe.granite.ui.components.ds.ValueMapResource;
import com.adobe.granite.ui.components.ExpressionResolver;
import org.osgi.service.component.annotations.Reference;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.IOException;
import java.util.*;

@Component(
        service = {Servlet.class},
        property = {
                "sling.servlet.resourceTypes=" + ElectronicsDSServlet.RESOURCE_TYPE,
                "sling.servlet.methods=GET",
                "sling.servlet.extensions=html"
        }
)
public class ElectronicsDSServlet extends SlingSafeMethodsServlet {
    private static final long serialVersionUID = 1L;

    static final String RESOURCE_TYPE = "aemoperations/components/electronics/alloweddevices";


    @Reference
    protected ExpressionResolver expressionResolver;

    @Override
    protected void doGet(@NotNull SlingHttpServletRequest request, @NotNull SlingHttpServletResponse response)
            throws ServletException, IOException {
        SimpleDataSource allowedDevicesDataSource = new SimpleDataSource(getAllowedDevices(request).iterator());
        request.setAttribute(DataSource.class.getName(), allowedDevicesDataSource);
    }

    protected List<Resource> getAllowedDevices(@NotNull SlingHttpServletRequest request) {
        List<Resource> colors = Collections.emptyList();
        String contentPath = (String) request.getAttribute(Value.CONTENTPATH_ATTRIBUTE);

        if(StringUtils.isEmpty(contentPath)){
            contentPath = request.getParameter("componentPath");
        }

        String dropdownValue = request.getParameter("dropdownValue");
        if(StringUtils.isEmpty(dropdownValue)){
            Config config = getConfig(request);
            ValueMap map = getComponentValueMap(config, request);
            dropdownValue = getParameter(config, "dropdownValue", request);
            if (StringUtils.isEmpty(dropdownValue)) {
                dropdownValue = map != null ? map.get("country", String.class) : StringUtils.EMPTY;
            }
        }
        ResourceResolver resolver = request.getResourceResolver();
        ContentPolicy policy = null;
        if (StringUtils.isNotEmpty(contentPath)) {
            policy = getContentPolicy(contentPath, resolver);
        }
        if (StringUtils.isEmpty(contentPath) || policy == null) {
            contentPath = request.getRequestPathInfo().getSuffix();
            if (StringUtils.isNotEmpty(contentPath)) {
                policy = getContentPolicy(contentPath, resolver);
            }
        }
        if (policy != null) {
            colors = populateDropdown(policy, resolver, dropdownValue);
        }
        return colors;
    }

    private ContentPolicy getContentPolicy(@NotNull String path, @NotNull ResourceResolver resolver) {
        ContentPolicy policy = null;
        ContentPolicyManager policyMgr = resolver.adaptTo(ContentPolicyManager.class);
        Resource contentResource = resolver.getResource(path);
        if (contentResource != null && policyMgr != null) {
            policy = policyMgr.getPolicy(contentResource);
        }
        return policy;
    }

    private List<Resource> populateDropdown(@NotNull ContentPolicy policy, @NotNull ResourceResolver resolver, String dropdownValue) {
        List<Resource> devices = new ArrayList<>();
        ValueMap device = null;
        Resource policyRes = resolver.resolve(policy.getPath());
        Iterator<Resource> children = policyRes.listChildren();
        while (children.hasNext()) {
            final Resource child = children.next();
            if (StringUtils.equalsIgnoreCase(child.getName(), "multifield")) {
                Iterator < Resource > multiChild = child.listChildren();
                while (multiChild.hasNext()) {
                    ValueMap valueMap = multiChild.next().adaptTo(ModifiableValueMap.class);
                    String[] type = valueMap.get("country",String[].class);
                    if(ArrayUtils.contains(type, dropdownValue)){
                        String[] allowedDevices = valueMap.get("devices",String[].class);
                        if (allowedDevices != null && allowedDevices.length > 0) {
                            for (String allowedDevice : allowedDevices) {
                                device = new ValueMapDecorator(new HashMap<>());
                                device.put("text", allowedDevice.toUpperCase());
                                device.put("value", allowedDevice);
                                devices.add(new ValueMapResource(resolver, new ResourceMetadata(), JcrConstants.NT_UNSTRUCTURED,
                                        device));
                            }
                        }
                    }
                }
            }
        }
        return devices;
    }

    Config getConfig(SlingHttpServletRequest request) {
        // get datasource configuration
        Resource datasource = request.getResource().getChild(Config.DATASOURCE);
        if (datasource == null) {
            return null;
        }
        return new Config(datasource);
    }

    protected String getParameter(@NotNull Config config, @NotNull String name,
                                  @NotNull SlingHttpServletRequest request) {
        String value = config.get(name, String.class);
        if (value == null) {
            return null;
        }
        ExpressionHelper expressionHelper = new ExpressionHelper(expressionResolver, request);
        return expressionHelper.getString(value);
    }

    ValueMap getComponentValueMap(Config config, SlingHttpServletRequest request) {
        if (config == null) {
            return null;
        }
        String componentPath = getParameter(config, "componentPath", request);
        if (componentPath == null) {
            return null;
        }

        // get component resource
        Resource component = request.getResourceResolver().getResource(componentPath);
        if (component == null) {
            return null;
        }
        return component.getValueMap();
    }
}

By accessing the component policy and selecting country and electronics, you can dynamically control the available dropdown options. For example, selecting “Country A” displays phone, tablet, and laptop options, while choosing “Country B” shows only phone and tablet options.

Working code for this can be found in the repository: Link to Repository

Design Dialog Configuration
Policy in CRXDE
Working output

Accessing Component Policies in AEM via Path-Based Servlet

How can I leverage component policies chosen at the template level to manage the dropdown-based selection?

AEM has integrated component policies as a pivotal element of the editable template feature. This functionality empowers both authors and developers to provide options for configuring the comprehensive behavior of fully-featured components, including deciding which fields to display, hide, or customize. This configuration is carried out at the template level, facilitating reuse across various templates or template-specific customizations.

Furthermore, AEM introduces a robust styling feature that empowers front-end developers to manage the visual appearance and user interface of components. This grants authors the capability to tailor the look and feel of the component.

Requirement:

In a previous discussion, we outlined a step-by-step process for Accessing Component Policies in AEM at the Component Dialog Level, Sightly code, and Sling model.

Now, consider a scenario where a company is selling various electronic devices in different countries. For example, a company like Brisket sells phones, tablets, and laptops in India, and only phones and tablets in the USA. However, if they plan to introduce laptops in the USA in the future, they’d like to make changes to the template policy. How can this be achieved at the dialog level?

Solution:

Step 1: Create an “Electronics” component and add the design dialog with specific attributes:

Design Dialog

Step 2:  Create the dialog and add an extra granite node with a specific property:

component-path="${requestPathInfo.suffix}" - get component path
Dialog

Step 3: When you open the component dialog and inspect the field, you will see extra attributes, including “component-path.”

Step 4: Create a dialog-level listener that makes a path-based call to a Servlet and passes the attributes as shown.

(function($, $document) {

    $(document).on("foundation-contentloaded", function(e) {
        var value = $("coral-select[name='./country']").val();
        var compPath = $("coral-select[name='./country']").attr("data-component-path");
        populateItems(compPath, value);
    });

    var REGEX_SELECTOR = "dropdown.selector",
        foundationReg = $(window).adaptTo("foundation-registry");

    foundationReg.register("foundation.validation.validator", {
        selector: "[data-foundation-validation='" + REGEX_SELECTOR + "']",
        validate: function(el) {
            if ($(el).is(":visible")) {
                var value = $(el).val();
                var flag = false;
                var compPath = $(el).attr("data-component-path");
                var errorMessage = 'Error occurred during processing';
                populateItems(compPath, value);
            }
        }
    });

    function populateItems(compPath, value) {
        $.ajax({
            type: "GET",
            async: false,
            url: "/bin/electronicsServlet?componentPath=" + compPath + "&dropdownValue=" + value,
            success: function(result) {
                if (result) {
                    var select = document.querySelector("coral-select[name='./devices']");
                    Coral.commons.ready(select, function(component) {
                        component.items.clear();
                        for (var i = 0; i < result.length; ++i) {
                            var option = document.createElement('coral-select-item');
                            option.textContent = result[i].text;
                            option.value = result[i].value;
                            component.items.add(option);
                        }
                    });
                } else {
                    flag = true;
                }
            }
        });
        if (flag) {
            return errorMessage;
        }
    }
}(jQuery, $(document)));

Step 5: Create a registered path-based Servlet that accepts the component path as a parameter, retrieves the component policy, and returns dropdown options.

package com.aem.operations.core.servlets;

import com.adobe.granite.rest.Constants;
import com.day.cq.wcm.api.policies.ContentPolicy;
import com.day.cq.wcm.api.policies.ContentPolicyManager;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.resource.ModifiableValueMap;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.api.servlets.SlingSafeMethodsServlet;
import org.apache.sling.servlets.post.JSONResponse;
import org.jetbrains.annotations.NotNull;
import org.osgi.service.component.annotations.Component;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.*;

@Component(service = { Servlet.class }, property = {
        "sling.servlet.paths=" + ElectronicsServlet.RESOURCE_PATH, "sling.servlet.methods=GET" })
public class ElectronicsServlet extends SlingSafeMethodsServlet {

    private static final long serialVersionUID = 1L;

    static final String RESOURCE_PATH = "/bin/electronicsServlet";

    @Override
    protected void doGet(@NotNull SlingHttpServletRequest request, @NotNull SlingHttpServletResponse response)
            throws ServletException, IOException {
        JsonArray jsonResponse = new JsonArray();
        response.setContentType(JSONResponse.RESPONSE_CONTENT_TYPE);
        response.setCharacterEncoding(Constants.DEFAULT_CHARSET);
        String componentPath = request.getParameter("componentPath");
        String dropdownValue = request.getParameter("dropdownValue");
        if(StringUtils.isNotEmpty(componentPath)){
            ContentPolicy policy = getContentPolicy(componentPath, request.getResourceResolver());
            if(null != policy){
                jsonResponse = populateDropdown(policy, request.getResourceResolver(), dropdownValue);
            }
        }
        try (PrintWriter out = response.getWriter()) {
            out.print(new Gson().toJson(jsonResponse));
        }
    }

    private ContentPolicy getContentPolicy(@NotNull String path, @NotNull ResourceResolver resolver) {
        ContentPolicy policy = null;
        ContentPolicyManager policyMgr = resolver.adaptTo(ContentPolicyManager.class);
        Resource contentResource = resolver.getResource(path);
        if (contentResource != null && policyMgr != null) {
            policy = policyMgr.getPolicy(contentResource);
        }
        return policy;
    }

    private JsonArray populateDropdown(ContentPolicy policy, ResourceResolver resolver, String dropdownValue) {
        JsonArray jsonResponse = new JsonArray();
        Resource policyRes = resolver.resolve(policy.getPath());
        Iterator<Resource> children = policyRes.listChildren();
        while (children.hasNext()) {
            final Resource child = children.next();
            if (StringUtils.equalsIgnoreCase(child.getName(), "multifield")) {
                Iterator < Resource > multiChild = child.listChildren();
                while (multiChild.hasNext()) {
                    ValueMap valueMap = multiChild.next().adaptTo(ModifiableValueMap.class);
                    String[] type = valueMap.get("country",String[].class);
                    if(ArrayUtils.contains(type, dropdownValue)){
                        String[] allowedDevices = valueMap.get("devices",String[].class);
                        if (allowedDevices != null && allowedDevices.length > 0) {
                            for (String allowedDevice : allowedDevices) {
                                JsonObject jsonObj = new JsonObject();
                                jsonObj.addProperty("text", allowedDevice.toUpperCase());
                                jsonObj.addProperty("value", allowedDevice);
                                jsonResponse.add(jsonObj);
                            }
                        }
                    }
                }
            }
        }
        return jsonResponse;
    }
}

By accessing the component policy and selecting country and electronics, you can dynamically control the available dropdown options. For example, selecting “Country A” displays phone, tablet, and laptop options, while choosing “Country B” shows only phone and tablet options.

The working code for this can be found in the repository: Link to Repository

Access template policy
Configure Policy
Saved under policies node as a multifield
Finally dropdown changes

Accessing Component Policies in AEM through the Component Sling Model

Problem Statement:

How can I utilize component policies chosen at the template level to manage the visibility of specific field content within the component’s Sling model code?

Introduction:

AEM has integrated component policies as a pivotal element of the editable template feature. This functionality empowers both authors and developers to provide options for configuring the comprehensive behavior of fully-featured components, including deciding which fields to display, hide, or customize. This configuration is carried out at the template level, facilitating reuse across various templates or template-specific customizations.

Furthermore, AEM introduces a robust styling feature that empowers frontend developers to manage the visual appearance and user interface of components. This grants authors the capability to tailor the look and feel of the component.

Let’s illustrate this with an AEM core component as an example:

Requirement:

In a previous discussion, we outlined a step-by-step process for Accessing Component Policies in AEM at the Component Dialog Level and Sightly code. However, a situation has arisen a question where the field has already been authored on multiple pages, and we now need to avoid displaying this field on pages where it has already been authored. How can this be achieved using the Sling model?

Solution:

This can be achieved at the Sling model level in two ways:

  1. By adapting to Out of the Box (OOTB) “ContentPolicyManager” as shown below:
package com.adobe.cq.wcm.core.components.internal.models.v1;

import com.adobe.cq.export.json.ExporterConstants;
import com.adobe.cq.wcm.core.components.models.Teaser;
import com.day.cq.wcm.api.designer.Style;
import com.day.cq.wcm.api.policies.ContentPolicy;
import com.day.cq.wcm.api.policies.ContentPolicyManager;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.*;

@Model(adaptables = SlingHttpServletRequest.class, resourceType = TeaserModel.RESOURCE_TYPE)
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME , extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class TeaserModel {
    public final static String RESOURCE_TYPE = "core/wcm/components/teaser/v1/teaser";

    @Self
    private SlingHttpServletRequest request;

    @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
    private String pretitle;

    @SlingObject
    protected Resource resource;

    private boolean pretitleHidden = false;

    public String getPretitle() {
        ContentPolicyManager policyManager = request.getResourceResolver().adaptTo(ContentPolicyManager.class);
        if (policyManager != null) {
            ContentPolicy currentPolicy = policyManager.getPolicy(resource, request);
            if (currentPolicy != null) {
                pretitleHidden = currentPolicy.getProperties().get(Teaser.PN_PRETITLE_HIDDEN, pretitleHidden);
            }
        }
        if(!pretitleHidden){
            return pretitle;
        }
        return StringUtils.EMPTY;
    }
}

2. By Injecting Out of the Box (OOTB) “currentStyle” using “ScriptVariable” as shown below:

package com.adobe.cq.wcm.core.components.internal.models.v1;

import com.adobe.cq.export.json.ExporterConstants;
import com.adobe.cq.wcm.core.components.models.Teaser;
import com.day.cq.wcm.api.designer.Style;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.InjectionStrategy;
import org.apache.sling.models.annotations.injectorspecific.ScriptVariable;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;

@Model(adaptables = SlingHttpServletRequest.class, resourceType = TeaserModel.RESOURCE_TYPE)
@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME , extensions = ExporterConstants.SLING_MODEL_EXTENSION)
public class TeaserModel {
    public final static String RESOURCE_TYPE = "core/wcm/components/teaser/v1/teaser";
    
    @ValueMapValue(injectionStrategy = InjectionStrategy.OPTIONAL)
    private String pretitle;

    @ScriptVariable
    protected Style currentStyle;

    private boolean pretitleHidden = false;

    public String getPretitle() {
        pretitleHidden = currentStyle.get(Teaser.PN_PRETITLE_HIDDEN, pretitleHidden);
        if(!pretitleHidden){
            return pretitle; 
        }
        return StringUtils.EMPTY;
    }
}

One significant advantage of employing the Sling model for this requirement is the flexibility it offers in implementing and manipulating business logic before delivering results. As demonstrated in the example above, injecting ScriptVariable allows for a more streamlined approach, eliminating the need for extra lines of code such as adaptation, resource resolution, and null checks. The Sling model code dynamically manages the visibility of the “pretitle” field based on the “pretitleHidden” property in the “currentStyle.”

Accessing Component Policies in AEM at the Component Sightly Level

How can I access component policies selected at the template level to control the visibility of specific field content in the component’s Sightly code?

AEM has integrated component policies as a pivotal element of the editable template feature. This functionality empowers both authors and developers to provide options for configuring the comprehensive behavior of fully-featured components, including deciding which fields to display, hide, or customize. This configuration is carried out at the template level, facilitating reuse across various templates or template-specific customizations.

Furthermore, AEM introduces a robust styling feature that empowers frontend developers to manage the visual appearance and user interface of components. This grants authors the capability to tailor the look and feel of the component.

Let’s illustrate this with an AEM core component as an example:

Requirement:

In a previous discussion, we outlined a step-by-step process for Accessing Component Policies in AEM at the Component Dialog Level. However, a situation has arisen a question where the field has already been authored on multiple pages, and we now need to avoid displaying this field on pages where it has already been authored. How can we achieve this?

If the “hidePretitle” checkbox is unchecked at the policy level, the Sightly code should display the content.

Policy unchecked
Sightly Display content


If the “hidePretitle” checkbox is checked at the policy level, the Sightly code should hide the content.

Policy checked
Sightly hide content

Solution:

This can be accomplished at the Sightly level by utilizing the Out of the Box (OOTB) implicit object called “currentStyle,” which implements “com.day.cq.wcm.api.designer.Style” as demonstrated below:

${!currentStyle.pretitleHidden}
<sly data-sly-template.pretitle="${@ teaser}">

    <p class="cmp-teaser__pretitle" data-sly-test.pretitle="${!currentStyle.pretitleHidden}">${teaser.pretitle}</p>

</sly>

This Sightly code dynamically controls the visibility of the “pretitle” based on the “pretitleHidden” property in the “currentStyle.”

Sightly code

Accessing Component Policies in AEM at the Component Dialog Level

How can I access component policies that were chosen at the template level to determine the visibility of specific fields within a component’s dialog?

AEM (Adobe Experience Manager) has integrated component policies as a central feature in the editable template system. This feature empowers both authors and developers to provide options for configuring the comprehensive behavior of fully featured components, including deciding which fields should be shown, hidden, or customized. This configuration can occur at the template level, allowing for reuse across various templates or template-specific customization.

Furthermore, AEM introduces a robust styling feature that empowers frontend developers to manage the visual appearance and user interface of components. This provides authors with the ability to configure the look and feel of components.

Let’s illustrate this with an example using one of AEM’s core components:

Requirement:

We want to prevent authors from editing the Pretitle field in the Teaser component.

Steps to Access the Policy:

Step 1: Navigate to a page in the WKND site via the Site Navigation. In our case, let’s open a magazine page.

WKND site

Step 2: Access the Teaser component, where you will see the Pretitle field enabled.

Teaser Component

Step 3: Now, go to the template editor to access the template structure.

Access Tempalte

Step 4: Click on the Teaser component’s policy.

Access Teaser Policy

Step 5: Select the checkbox to hide the Pretitle field.

Configure Policy

Step 6: Return to the page, refresh it, and open the Teaser component. You’ll notice that you can enable or disable fields using the powerful policy feature.

Teaser component without Pretitle

Now, let’s explore how this works from a backend perspective by accessing CRXDE:

Step 1: In CRXDE, access your project structure to locate your Teaser component.

Path: /apps/wknd/components/teaser
CRXDE: Teaser component

You’ll notice that the component doesn’t have any content, as it overlays the core Teaser component.

Step 2: Navigate to the core Teaser component to access the design dialog (Policy-level fields).

Path: /apps/core/wcm/components/teaser/v2/teaser/cq:design_dialog/content/items/tabs/items/main/items/elements/items/pretitle
CRXDE: Design dialog

You can see that Policy-level fields are located under the design dialog, which can be used to modify the component dialog or behavior. The powerful aspect of policies is that you can have different policies for different templates.

Step 3: Go to the component dialog and examine the Pretitle fields.

Path: /apps/core/wcm/components/teaser/v2/teaser/cq:dialog/content/items/tabs/items/text/items/columns/items/column/items/pretitle
CRXDE: Dialog

You’ll see that a property has been added to the component level using the OOTB (Out of the Box) Granite UI. This can be used for handling:

FilteringResourceWrapper
Render Condition

For more details, you can access these links for Granite UI and documentation.

However, to access any policy field at the component level, you can use the cqDesign implicit variable provided by ExpressionCustomizer by calling the property name as follows:

${cqDesign.[propertyname]} – general field name
For example: ${cqDesign.pretitleHidden} – pretitle field name