Sunday 15 April 2018

Touch UI Validation in AEM - using jQuery and Granite UI (Part 2)

In this post, you'll know how you can validate the fields in the touch ui dialog of AEM using jQuery logic for validation and Granite UI to prompt error.
To use foundation-validation validator for touch ui validation, refer this post.

The entire logic for validation can be written in JS code as shown below which you can put in clientlibs and add the categories of clientlibs as cq.authoring.dialog

For ex. I am doing the validation for a phone number field which accepts only 10 digit numbers in the dialog.
(function (document, $, ns) {
    "use strict";
    $(document).on("click", ".cq-dialog-submit", function (e) {
        e.stopPropagation();
        e.preventDefault();
 
        var $form = $(this).closest("form.foundation-form"),
            symbolid = $form.find("[name='./symbol']").val(),
               message, clazz = "coral-Button ",
         patterns = {
             symboladd: /^([0-9]{10})\.?$/i
        };

        if(symbolid != "" && !patterns.symboladd.test(symbolid) && (symbolid != null)) {
                ns.ui.helpers.prompt({
                title: Granite.I18n.get("Invalid Number"),
                message: "Please Enter a valid 10 Digit Phone Number",
                actions: [{
                    id: "CANCEL",
                    text: "CANCEL",
                    className: "coral-Button"
                }],
            callback: function (actionId) {
                if (actionId === "CANCEL") {
                }
            }
        });
        }else{
                 $form.submit();
        }
    });
})(document, Granite.$, Granite.author);
As seen from the above code snippet, you can get the dialog element value using name and apply the regex pattern test to validate the field and then with the Granite ui, you can prompt the validation fail message as shown below:


Saturday 14 April 2018

Touch UI Validation in AEM - using Foundation Validation (Part 1)

In this post, you'll know how you can validate the fields in the touch ui dialog of AEM.
Now that  jQuery based validator, i.e. $.validator.register() is deprecated, we'll be using foundation-validation.

To select the node/field in the dialog for validation, just add any validation attribute along with your value in the properties of that node. Then in the component field markup you'll be able to see that data-attribute added.

For ex. I have added the below validation attribute to my summary textfield-



and the field markup of the dialog now looks like:


Now, this data-attribute will act as the selector for validating that particular field.

Use the below JS code snippet which you can put in clientlibs and add the categories as cq.authoring.dialog
(function ($window) {

 $window.adaptTo("foundation-registry").register("foundation.validation.validator", {
  selector: "[data-myapp-maxlength]",
  validate: function(el) {
    var maxlength = parseInt(el.getAttribute("data-myapp-maxlength"), 20);

    if (isNaN(maxlength)) {return;}

    var length = el.value.length;
    if (length > maxlength) {
      return "The field needs to be maximum " + maxlength + " characters. 
              It currently has a length of " + length + ".";
    }
  }, 
 });

})($(window));
The dialog validation looks like this:


Here, based on the validation attribute added (data-myapp-maxlength=20) and the logic written in js, it will validate the field length and show the error if validation fails.

You can also add show() and clear() method for this foundation-validation by referring the below interface:
interface FoundationValidationValidator {
  /**
   * Only the element satisfying the selector will be validated.
   */
  selector: string;
  /**
   * The actual validation function. It must return a string of error message
   * if the element fails.
   */
  validate: (element: Element) => string;
  /**
   * The function to show the error.
   */
  show: (element: Element, message: string, ctx: FoundationValidationValidatorContext) => void;
  /**
   * The function to clear the error.
   */
  clear: (element: Element, ctx: FoundationValidationValidatorContext) => void;
}
Referencehttps://helpx.adobe.com/experience-manager/6-3/sites/developing/using/reference-materials/granite-ui/api/jcr_root/libs/granite/ui/components/coral/foundation/clientlibs/foundation/js/validation/index.html

URL Mapping and Removing .html extension from pages in AEM

There's an OSGI configuration provided by AEM - Apache Sling Resource Resolver Factory for Resource mapping in AEM.
Resource mapping is used to define redirects, vanity URLs and virtual hosts for AEM.

Like when you want to shorten the url (don't want to expose the entire path starting with /content), you can write your entries in URL Mapping property of the above configuration (http://localhost:4502/system/console/configMgr).

For ex. I have my page at http://localhost:4502/content/slingModels63/en.html and I want to slice the middle path of the url, I can have the below mapping and I will be able to access the page by hitting



The URL Mapping also accepts the regex pattern which you can enter for your resource mapping.
But there's a separate mapping location in crxde where you can define mappings for the HTTP protocol - it's /etc/map/http folder as highlighted above in the config.

Here you can create a node of type sling:Mapping and provide your mappings and avoid doing the changes in the OSGI Configuration through Web console.

Use-Case:

If you want to open your pages in aem without .html extension, then create a node of type sling:Mapping and add the two properties with url pattern values to that node - sling:internalRedirect and sling:match as shown below:



Below is the xml snippet of the above node with properties:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" 
    xmlns:jcr="http://www.jcp.org/jcr/1.0"
    jcr:primaryType="sling:Mapping"
    sling:internalRedirect="/content/$1.html"
    sling:match="localhost.4502/content/(.*)$"/>
The above mapping will allow you to access all pages inside content folder to open without .html extension in author instance.

If you want to achieve the same in publish mode, then create /etc/map.publish folder to hold the configurations for the publish environment. These must then be replicated, and the new location (/etc/map.publish) should be configured for the Mapping Location of the Apache Sling Resource Resolver Factory of the publish environment.

Thus, you can define your own regex pattern and mappings upon your requirement and also you can view all your mappings under the JCR ResourceResolver option of the Felix console at http://localhost:4502/system/console/jcrresolver .

Sunday 8 April 2018

Retrieve Composite Multifield child values in AEM through Java (WCMUsePojo)

This post is a part 2 of my previous blog Composite Collapsible Multifield in AEM 6.3 Touch UI in which we retrieved the authored multifield values in our sightly code by using JS Use-API.

Here, we'll use the Java WCMUsePojo API to retrieve the multifield child values in our sightly code.

Since the multifield values stored in the page is stored as child nodes as shown below:





First, we'll create the MultiList bean having same properties as in our dialog with getters and setters as shown below:

package com.foo.community.core;

public class MultiList {
 
 private String multitext;
 private String multinum;
 private String multicheck;

 public String getMultitext() {
  return multitext;
 }

 public void setMultitext(String multitext) {
  this.multitext = multitext;
 }

 public String getMultinum() {
  return multinum;
 }

 public void setMultinum(String multinum) {
  this.multinum = multinum;
 }

 public String getMulticheck() {
  return multicheck;
 }

 public void setMulticheck(String multicheck) {
  this.multicheck = multicheck;
 }
}

And then, we'll create a class extending WCMUsePojo where we'll get the current node and then get the products node (where child values of multifield are stored) and iterate over that and set the retrieved property values in List of MultiList bean created above. Below is the implementation of this process:

package com.foo.community.core;

import java.util.ArrayList;
import java.util.List;   
import com.adobe.cq.sightly.WCMUsePojo;
import javax.jcr.Node;
import javax.jcr.NodeIterator;    

public class MultiListComponent extends WCMUsePojo{

  private List multiItems = new ArrayList();

  @Override
  public void activate() throws Exception {

     Node currentNode = getResource().adaptTo(Node.class);
     if(currentNode.hasNode("products")){
     Node productsNode = currentNode.getNode("products");
     NodeIterator ni =  productsNode.getNodes();
     
     String multitext;
     String multinum;
     String multicheck;

     while (ni.hasNext()) {

      MultiList multiItem = new MultiList();          
      Node child = (Node)ni.nextNode();

         multitext= child.hasProperty("multitext") ? 
           child.getProperty("multitext").getString(): ""; 
         multinum = child.hasProperty("multinum") ? 
           child.getProperty("multinum").getString(): ""; 
         multicheck = child.hasProperty("multicheck") ? 
           child.getProperty("multicheck").getString(): "";    
     
         multiItem.setMultitext(multitext);
         multiItem.setMultinum(multinum);
         multiItem.setMulticheck(multicheck);
         multiItems.add(multiItem);
     }  
    }
 } 

 public List getMultiItems() {
  return multiItems;
 }
}

And use below sightly code (blog.html) to render values in the html:

<div data-sly-use.blog="com.foo.community.core.MultiListComponent">
  <h4>Composite Multifield</h4>
  <ol data-sly-list.product="${blog.multiItems}">
     <li>
        ${product.multitext} has ${product.multinum} with boolean 
        ${product.multicheck}
     </li>
  </ol>
</div>

Saturday 7 April 2018

Composite Collapsible Multifield in AEM 6.3 Touch UI

This post discusses how to create a composite collapsible multifield touch ui dialog which uses Granite UI Coral3 multifield in AEM 6.3 .

We'll be using OOTB coral multifield provided by AEM 6.3 for storing multifield values as child nodes (NODE STORE)granite/ui/components/coral/foundation/form/multifield, and we'll retrieve the authored multifield values in our sightly code by using JS Use-API (To retrieve values via WCMUsePojo API, refer this post here).

The advantage of using JS Use-API here is you can get the node of multifield values stored in pages and pass it to the sightly code in few lines of code as compared to more lines of code when using JAVA for this.

Also you won't need to use acs-commons package or any other custom multifield here for storing composite multifield values.

To make the multi-fields collapsible we'll be using the foundation layout type inside container as: granite/ui/components/foundation/layouts/collapsible, thus we would not need to write customization code in clientlibs for this.

The touch ui dialog structure is shown below:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
    xmlns:cq="http://www.day.com/jcr/cq/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="Blog Component"
    sling:resourceType="cq/gui/components/authoring/dialog"
    helpPath="https://www.adobe.com/go/aem6_3_docs_component_en#Image - HTL">
    <content
      jcr:primaryType="nt:unstructured"        
      sling:resourceType="granite/ui/components/foundation/container">
      <layout
         jcr:primaryType="nt:unstructured"
         sling:resourceType="granite/ui/components/foundation/layouts/tabs"
         type="nav"/>
        <items jcr:primaryType="nt:unstructured">
            <multi
                jcr:primaryType="nt:unstructured"
                jcr:title="Multi"
                sling:resourceType="granite/ui/components/foundation/section">
                <layout
                    jcr:primaryType="nt:unstructured"
       sling:resourceType="granite/ui/components/foundation/layouts/fixedcolumns"
                    margin="{Boolean}false"/>
                <items jcr:primaryType="nt:unstructured">
                    <column
                        jcr:primaryType="nt:unstructured"                       
       sling:resourceType="granite/ui/components/foundation/container">
                        <items jcr:primaryType="nt:unstructured">
                            <fieldenter
                                jcr:primaryType="nt:unstructured"
       sling:resourceType="granite/ui/components/coral/foundation/form/multifield"
                                composite="{Boolean}true"
                                fieldLabel="Composite Multifield">
                                <field
                                    jcr:primaryType="nt:unstructured"
                                    jcr:title="Click to Expand"                              
       sling:resourceType="granite/ui/components/foundation/container"
                                    fieldLabel="Products Container"
                                    name="./products">
                                    <layout
                                     jcr:primaryType="nt:unstructured"                                 
       sling:resourceType="granite/ui/components/foundation/layouts/collapsible"/>
                                    <items 
                                     jcr:primaryType="nt:unstructured">
                                        <multitext
                                            jcr:primaryType="nt:unstructured"
       sling:resourceType="granite/ui/components/foundation/form/textfield"
                                            fieldLabel="Multi Text"
                                            name="multitext"/>
                                        <multinum
                                            jcr:primaryType="nt:unstructured"
       sling:resourceType="granite/ui/components/foundation/form/numberfield"
                                            fieldLabel="Multi Num"
                                            name="multinum"/>
                                        <multicheck
                                            jcr:primaryType="nt:unstructured"               
       sling:resourceType="granite/ui/components/foundation/form/checkbox"
                                            name="multicheck"
                                            text="Multi Check"
                                            value="{Boolean}true"/>
                                    </items>
                                </field>
                            </fieldenter>
                        </items>
                    </column>
                </items>
            </multi>
        </items>
    </content>
</jcr:root>

Touch UI Dialog authoring interface is as shown below:


The multifield values stored in the page is stored as child nodes as shown below:





You can use below JS Use code (blog.js) to fetch the node and pass to sightly:

"use strict";
use(function() {
    var resourceResolver = resource.getResourceResolver();
    return {
      node : resourceResolver.getResource(currentNode.getPath() + "/products"),
    };
});

And use below sightly code (blog.html) to render values in the html:

<div data-sly-use.blog="blog.js">
    <h4>Composite Multifield Values: </h4>
    <ol data-sly-list.product="${blog.node.listChildren}">
      <li>
          ${product.multitext} has ${product.multinum} with boolean 
          ${product.multicheck}
      </li>
    </ol>
</div>