Date Range Validation Erweiterung

Freilegen von versteckten Features des CFM Editors von AEM - Teil 1

Haben Sie sich jemals gefragt, wie Sie sicherstellen können, dass zwei Datumsfelder innerhalb eines Content Fragments (CF) zusammen validiert werden können, um sicherzustellen, dass eines vor/nach dem anderen liegt?

Leider verfügen Content Fragment Models (CFMs) in AEM nicht über Validierungsregeln, die sich über mehrere Felder erstrecken, wie in AEM Forms.
Es gibt jedoch eine versteckte Funktionalität im Datepicker, die offengelegt werden kann, um eine Date-Range-Validierung zu ermöglichen.

Diese versteckte Funktionalität ist Teil des zugrundeliegenden Granite Datepicker, der für den Content Fragment Datepicker verwendet wird.

Aus der Dokumentation:

  • 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.

Erster Schritt: Hinzufügen des Property Felder zum CFM editor

Der erste Schritt ist das Hinzufügen der Eingabefelder für die beforeSelector und afterSelector Werte im CFM-Editor. Dazu werden wir zwei JSP-Dateien unter diesem Pfad erstellen:

 

  • /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);

%>

Beachten Sie, dass afterSelector und beforeSelector einen CSS-Selektor benötigen. Deshalb muss der Property value in einem bestimmten Format vorliegen.

Siehe auch "fieldDescription": [name='PROPERTYNAME']

 

Nun müssen wir diese Eigenschaftsfelder dem eigentlichen Datepicker für CFMs zuweisen. Wir können dies tun, indem wir eine Datei unter /libs überschreiben/überlagern (overwrite), indem wir diese Datei zum Ordner /apps hinzufügen:

  • /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>

Zweiter Schritt: Validierung zum Content Fragment Model hinzufügen

Und das war's im Grunde schon. Jetzt muss der CFM-Autor nur noch den Selektor mit der Eigenschaft zum Inhaltsfragmentmodell hinzufügen, etwa so:

Example of a date field with a before selector
Example of a date field with a after selector

Und dann ist die Validierung für den Autor des Inhaltsfragments aktiv

Author view with failed date validation

Ich hoffe, diese kleine Erweiterung hat Ihnen geholfen, und wenn Sie eine ausführlichere Erklärung benötigen, können Sie weiter lesen.

Technische Details

Wenn wir in das Feld Content Fragment Model für Datepicker schauen, finden wir diese Komponente:

  • /libs/dam/cfm/models/editor/komponenten/datatentypen/datepicker

Und in den Properties finden wir den resourceSuperType

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

Dies hilft uns zu sehen, welche Funktionalitäten wir nutzen können und welche "versteckten" Funktionen wir dem CFM-Autor zur Verfügung stellen können.

 

Disclaimer:

Bei der Erweiterung von AEM Out-of-the-Box-Funktionen müssen wir uns immer fragen, ob es das Risiko wert ist, Dateien zu überlagern (overwrite). Denn ein neues AEM-Release kann Änderungen an unseren geänderten Dateien enthalten und zu unvorhergesehenen Problemen führen, da es keine Möglichkeit gibt, herauszufinden, was sich geändert hat, wenn man nicht direkt in die jsp/html/...-Dateien im Ordner /libs schaut.

Aber in diesem Fall ist das Risiko ziemlich gering, da wir neue Dateien hinzufügen, anstatt bestehende zu überlagern. Sie müssen nur die fieldProperties im Auge behalten, da diese in der Zukunft erweitert/geändert werden könnten. 

Getestet mit AEM 6.5.17

 

Wenn Sie wissen möchten, wie Sie das Tag-Feld erweitern können, um "forceSelection" zu aktivieren, klicken Sie bitte hier:

Teil 2