Date Range Validation Extension

Exposing hidden features of the CFM editor in AEM - Part 1

Back to Insights
/de/en/insights
Share

Have you ever wondered how you can make sure that two date fields inside a Content Fragment (CF) can be validated together to make sure one is before/after the other?

Unfortunately, Content Fragment Models (CFMs) in AEM don't have any validation rules that span over multiple fields like in AEM Forms.
But there is a  hidden functionality  in the  datepicker  that can be exposed to make a  date range validation  possible e.g. to check if one date field value is before/after another date field value.

This hidden functionality is part of the underlying  granite datepicker  that is used for the  Content Fragment datepicker.

From the Documentation:

  • beforeSelector
    • Specifies a CSS selector targeting another datepickers that are before this datepicker. If those datepickers are not before this datepicker, it will be invalid.
  • afterSelector
    • Specifies a CSS selector targeting another datepickers that are after this datepicker. If those datepickers are not after this datepicker, it will be invalid.

First Step: Add the properties fields to the CFM editor

The first step we need to do is to add the input fields for the  beforeSelector  and  afterSelector values in the CFM editor. To do this we will create two JSP files under this path:

  • /apps/dam/cfm/models/editor/components/datatypeproperties/datebeforefield/datebeforefield.jsp
  • /apps/dam/cfm/models/editor/components/datatypeproperties/dateafterfield/dateafterfield.jsp

dateafterfield.jsp

<%@include file="/libs/granite/ui/global.jsp" %><%
%><%@ page session="false" contentType="text/html" pageEncoding="utf-8"
           import="com.adobe.granite.ui.components.formbuilder.FormResourceManager,
                 org.apache.sling.api.resource.Resource,
                 org.apache.sling.api.resource.ValueMap,
                 java.util.HashMap" %><%

    ValueMap fieldProperties = resource.adaptTo(ValueMap.class);

    HashMap values = new HashMap();
    values.put("granite:class",    "field-dateafter-descriptor");
    values.put("fieldLabel",       i18n.get("Date after field selector for validation"));
    values.put("name",             "./content/items/" + resource.getName() + "/afterSelector");
    values.put("value",            fieldProperties.get("afterSelector", String.class));
    values.put("fieldDescription", "The property name needs to be encapsulated inside this template: [name='PROPERTYNAME']");
    values.put("emptyText",        "[name='PROPERTYNAME']");

    FormResourceManager formResourceManager = sling.getService(FormResourceManager.class);
    Resource labelFieldResource = formResourceManager.getDefaultPropertyFieldResource(resource, values);

%>

datebeforefield.jsp

<%@include file="/libs/granite/ui/global.jsp" %><%
%><%@ page session="false" contentType="text/html" pageEncoding="utf-8"
           import="com.adobe.granite.ui.components.formbuilder.FormResourceManager,
                 org.apache.sling.api.resource.Resource,
                 org.apache.sling.api.resource.ValueMap,
                 java.util.HashMap" %><%

    ValueMap fieldProperties = resource.adaptTo(ValueMap.class);

    HashMap values = new HashMap();
    values.put("granite:class",    "field-datebefore-descriptor");
    values.put("fieldLabel",       i18n.get("Date before field selector for validation"));
    values.put("name",             "./content/items/" + resource.getName() + "/beforeSelector");
    values.put("value",            fieldProperties.get("beforeSelector", String.class));
    values.put("fieldDescription", "The property name needs to be encapsulated inside this template: [name='PROPERTYNAME']");
    values.put("emptyText",        "[name='PROPERTYNAME']");

    FormResourceManager formResourceManager = sling.getService(FormResourceManager.class);
    Resource labelFieldResource = formResourceManager.getDefaultPropertyFieldResource(resource, values);

%>

Keep in mind that  afterSelector  and  beforeSelector  need a  CSS selector. That's why the property value needs to be in a specific format.

See also the "fieldDescription": [name='PROPERTYNAME']

Now we need to assign these property fields to the actual datepicker for CFMs. We can do this by overwrite a file under  /libs  by adding this file to the  /apps  folder:

  • /apps/settings/dam/cfm/models/formbuilderconfig/datatypes/.content.xml

<?xml version="1.0" encoding="UTF-8"?>

<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"

jcr:primaryType="nt:unstructured">

<items jcr:primaryType="nt:unstructured">

<date

jcr:primaryType="nt:unstructured"

fieldProperties="[datepickerfields,labelfield,maptopropertyfield,datetimepickerfield,datevaluefield,requiredfield,datebeforefield,dateafterfield]"/>

</items>

</jcr:root>

How to add the validation to the Content Fragment Models

And that's basically it. Now the CFM author only needs to add the selector with the property to the Content Fragment Model like this:

Example of a date field with a before selector
medium
Example of a date field with a after selector
medium
And then the validation is active for the author of the Content Fragments
Author view with failed date validation
medium

I hope this little extension helped you and if you need a more in-depth explanation you can continue reading.

Technical Explanation

If we look inside the Content Fragment Model field for datepicker we will find this component:

  • /libs/dam/cfm/models/editor/components/datatypes/datepicker

And in the properties we find the resourceSuperType

  • /libs/granite/ui/components/coral/foundation/form/datepicker
resourceSuperType for cfm datepicker
medium

This helps us to see what functionalities we can use and which  "hidden" features  we can expose to the CFM author.

Disclaimer:

By  extending AEM out-of-the-box functionality  we always need to question ourselves  if it's worth the risk  to overwrite. Because a  new AEM release  can contain changes at our changed files and could lead to  unforeseen problems  as there is no way to know what changed if you are not looking into the jsp/html/.. files in folder  /libs  directly.

But in this case the risk is fairly low  because we are adding new files instead of overwriting existing ones. You only need to keep an eye on the  fieldProperties  as these could be extended/changed in the future.

Tested with AEM 6.5.17

If you want to know how to extend the Tag field to enable "forceSelection" you can click here:

Part 2

Let’s create something great.
We bring bold ideas to life. As your partner for digital transformation, we’re here to support shaping outstanding projects for leading brands. Reach out and discover how our expertise can drive your success.
/content/dam/ews/videos/alex_hands_contact_code_2K_recbitrate.mp4
Let’s talk
/de/en/about-eggs/contact
Your next big project starts here.
Looking for fresh ideas, innovative solutions, or a partner who takes your goals seriously? Let’s connect and start building success together.