Custom functions in Adaptive Forms Core Components
This article describes creating Custom Functions with the latest Adaptive Form Core Component, which have the latest features such as:
- Caching feature for Custom Functions
- Global scope object and field objects support for Custom Functions
- Support for modern JavaScript features like let and arrow functions (ES10 support)
Ensure to set the latest form version on your AEM Forms Core Component environment to use the latest features in Custom Functions.
Introduction
AEM Forms 6.5 includes JavaScript functions that allow you to define complex business rules by using the rule editor. While AEM Forms offers a variety of out-of-the-box custom functions, many use cases require defining your own custom functions to use across multiple forms. These custom functions enhance the capabilities of forms by enabling the manipulation and processing of entered data to meet specific requirements. Additionally, they allow for dynamic alteration of form behavior based on the predefined criteria.
Uses of custom functions uses-of-custom-function
Advantages of using Custom Functions in Adaptive Forms Core Components are:
- Managing data: Custom functions manage and process data entered into the forms fields.
- Processing of data: Custom functions help process data entered into the forms fields.
- Validation of data: Custom functions enable you to perform custom checks on form inputs and provide specified error messages.
- Dynamic behavior: Custom functions allow you to control the dynamic behavior of your forms based on specific conditions. For example, you can show/hide fields, modify field values, or adjust form logic dynamically.
- Integration: You can use custom functions to integrate with external APIs or services. It helps in fetching data from external sources, sending data to external Rest endpoints, or performing custom actions based on external events.
Custom functions are essentially client libraries that are added in the JavaScript file. Once you create a Custom Function, it becomes available in the rule editor for selection by the user in an Adaptive Form. The custom functions are identified by the JavaScript annotations in the rule editor.
Supported JavaScript annotations for custom function js-annotations
JavaScript annotations provides metadata for JavaScript code. It includes comments that start with specific symbols for example, /**
and @
. The annotations provide important information about functions, variables, and other elements in the code. Adaptive Form supports the following JavaScript annotations for custom functions:
Name
The Name is used to identify the custom function in the rule editor of an Adaptive form. Following syntaxes are used to name a Custom Function:
@name [functionName] <Function Name>
@function [functionName] <Function Name>
@func [functionName] <Function Name>
[functionName]
is the name of the function. Spaces are not allowed.<Function Name>
is the display name of the function in the rule editor of Adaptive Forms.If the function name is identical to the name of the function itself, you can omit
[functionName]
from the syntax.Parameter
The Parameter is a list of arguments used by custom functions. A function can support multiple parameters. The following syntaxes are used to define a parameter in a custom function:
-
@param {type} name <Parameter Description>
-
@argument
{type} name <Parameter Description>
-
@arg
{type}
name <Parameter Description>
{type}
represents the parameter type. The allowed parameter types are:- string: Represents a single string value.
- number: Represents a single numeric value.
- boolean: Represents a single boolean value (true or false).
- string[]: Represents an array of string values.
- number[]: Represents an array of numeric values.
- boolean[]: Represents an array of boolean values.
- date: Represents a single date value.
- date[]: Represents an array of date values.
- array: Represents a generic array containing values of various types.
- object: Represents form object passed to a custom function instead of passing its value directly.
- scope: Represents the globals object, which contains read-only variables such as form instances, target field instances, and methods for performing form modifications within the custom functions. It is declared as the last parameter in the JavaScript annotations and is not visible to the rule editor of an Adaptive Form. The scope parameter accesses the object of the form or component to trigger the rule or event required for form processing. For further information on the Globals object and how to use it, click here
The Parameter type is not case-sensitive and spaces are not allowed in the parameter name.
<Parameter Description>
contains details about the purpose of the parameter. It can have multiple words.
Return Type
The return type specifies the type of value that the custom function returns after execution. The following syntaxes are used to define a return type in a custom function:
@return {type}
@returns {type}
{type}
represents the return type of the function. The allowed return types are:- string: Represents a single string value.
- number: Represents a single numeric value.
- boolean: Represents a single boolean value (true or false).
- string[]: Represents an array of string values.
- number[]: Represents an array of numeric values.
- boolean[]: Represents an array of boolean values.
- date: Represents a single date value.
- date[]: Represents an array of date values.
- array: Represents a generic array containing values of various types.
- object: Represents form object instead of its value directly.
The return type is not case-sensitive.
Private
The custom function, declared as private, does not appear in the list of custom functions in the rule editor of an Adaptive form. By default, custom functions are public. The syntax to declare custom function as private is @private
.
Guidelines while creating custom functions considerations
To list the custom functions in the rule editor, you can use any one of the following formats:
Function statement with or without jsdoc comments
You can create a custom function with or without jsdoc comments.
function functionName(parameters)
{
// code to be executed
}
If the user does not add any JavaScript annotations to the custom function, it is listed in the rule editor by its function name. However, it is recommended to include JavaScript annotations for improved readability of the custom functions.
Arrow function with mandatory JavaScript annotations or comment
You can create a custom function with an arrow function syntax:
/**
* test function
* @name testFunction
* @param {string} a parameter description
* @param {string=} b parameter description
* @return {string}
*/
testFunction = (a, b) => {
return a + b;
};
/** */
testFunction1=(a) => (return a)
/** */
testFunction2 = a => a + 100;
If the user does not add any JavaScript annotations to the custom function, the custom function is not listed in the rule editor of an Adaptive Form.
Function expression with mandatory JavaScript annotations or comment
To list custom functions in the rule editor of an Adaptive Form, create custom functions in the following format:
/**
* test function
* @name testFunction
* @param {string} input1 parameter description
* @param {string=} input2 parameter description
* @return {string}
*/
testFunction = function(input1,input2)
{
// code to be executed
}
If the user does not add any JavaScript annotations to the custom function, the custom function is not listed in the rule editor of an Adaptive Form.
Prerequisites to create a custom function
Before you begin adding a custom function to your Adaptive Forms, ensure you have the following Software installed on your machine:
-
Plain Text Editor (IDE): While any plain text editor can work, an Integrated Development Environment (IDE) like Microsoft Visual Studio Code offers advanced features for easier editing.
-
Git: This version control system is required for managing code changes. If you do not have it installed, download it from https://git-scm.com.
Create a custom function create-custom-function
Steps to create custom functions are:
Create a client library using the AEM Project Archetype create-client-library-archetype
You can add custom functions by adding a client library to the project created using the AEM Project Archetype.
If you have an existing project you can directly add custom functions to your local project.
After you create an Archetype Project or use an existing project, create a client library. To create a client library perform the following steps:
Add a Client Library Folder
To add new client library folder to your [AEM project directory], follow the steps:
-
Open the [AEM project directory] in an editor.
-
Locate
ui.apps
. -
Add new folder. For example, add a folder named as
experience-league
. -
Navigate to
/experience-league/
folder and add aClientLibraryFolder
. For example, create a client library folder named ascustomclientlibs
.Location is:
[AEM project directory]/ui.apps/src/main/content/jcr_root/apps/
Add files and folders to the Client Library folder
Add the following to the added client library folder:
.content.xml
filejs.txt
filejs
folder
Location is: [AEMaaCS project directory]/ui.apps/src/main/content/jcr_root/apps/experience-league/customclientlibs/
-
In the
.content.xml
add the following lines of code:code language-javascript <?xml version="1.0" encoding="UTF-8"?> <jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" jcr:primaryType="cq:ClientLibraryFolder" categories="[customfunctionscategory]"/>
note note NOTE You can choose any name for client library folder
andcategories
property. -
In the
js.txt
add the following lines of code:code language-javascript #base=js function.js
-
In the
js
folder, add the javascript file asfunction.js
which includes the custom functions:code language-javascript /** * Calculates Age * @name calculateAge * @param {object} field * @return {string} */ function calculateAge(field) { var dob = new Date(field); var now = new Date(); var age = now.getFullYear() - dob.getFullYear(); var monthDiff = now.getMonth() - dob.getMonth(); if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < dob.getDate())) { age--; } return age; }
-
Save the files.
Include the new folder in filter.xml:
-
Navigate to the
/ui.apps/src/main/content/META-INF/vault/filter.xml
file in your [AEMaaCS project directory]. -
Open the file and add the following line at the end:
<filter root="/apps/experience-league" />
-
Save the file.
-
Build the newly created client library folder to your AEM environment by following the steps given in How to Build section.
Create and deploy custom functions through CRXDE create-add-custom-function
If you are using the latest AEM Forms and Forms add-on, you can create a custom function through CRXDE to use the latest updates of custom functions. To do so, perform the following steps:
-
Log into
http://server:port/crx/de/index.jsp#
. -
Create a folder under the
/apps
folder. For example, create a folder named asexperience-league
. -
Save your changes.
-
Navigate to the created folder and create a node of type
cq:ClientLibraryFolder
asclientlibs
. -
Navigate to the newly created
clientlibs
folder and add theallowProxy
andcategories
properties:note note NOTE You can provide any name in place of customfunctionsdemo
. -
Save your changes.
-
Create a folder called
js
under theclientlibs
folder. -
Create a JavaScript file called
functions.js
under thejs
folder. -
Create a file called
js.txt
under theclientlibs
folder. -
Save your changes.
The created folder structure looks like: -
Double-click the
functions.js
file to open the editor. The file comprises the code for custom function.
Let’s add the following code to the JavaScript file to calculate age based on the Date of Birth(YYYY-MM-DD).code language-javascript /** * Calculates Age * @name calculateAge * @return {string} */ function calculateAge(dateOfBirthString) { var dob = new Date(dateOfBirthString); var now = new Date(); var age = now.getFullYear() - dob.getFullYear(); var monthDiff = now.getMonth() - dob.getMonth(); if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < dob.getDate())) { age--; } return age; }
-
Save
function.js
. -
Navigate to
js.txt
and add the following code:code language-javascript #base=js functions.js
-
Save the
js.txt
file.
You can refer to the following custom function folder. Download and install this folder on your AEM instance.
Now, you can use the custom function in your Adaptive Form by adding the client library.
Add client library in an Adaptive Form add-client-library
Once you have deployed your client library to your AEM Forms environment, use its capabilities in your Adaptive Form. To add the client library in your Adaptive Form
-
Open your form in the edit mode. To open a form in the edit mode, select a form and select Edit.
-
Open the Content browser, and select the Guide Container component of your Adaptive Form.
-
Click the Guide Container properties icon. The Adaptive Form Container dialog box opens.
-
Open the Basic tab and select the name of the client library category from the drop-down list (in this case, select
customfunctionscategory
). -
Click Done.
Now, you can create a rule to use custom functions in the rule editor:
Now, let’s understand how to configure and use a custom function using the Rule Editor’s Invoke service in AEM Forms 6.5
Using Custom Function in an Adaptive Form use-custom-functions
In an Adaptive Form, you can use Custom Functions within the rule editor.
Let us add the following code to the JavaScript file (Function.js
file) to calculate age based on the Date of Birth (YYYY-MM-DD). Create a custom function as calculateAge()
which takes the date of birth as input and returns age:
/**
* Calculates Age
* @name calculateAge
* @param {object} field
* @return {string}
*/
function calculateAge(field) {
var dob = new Date(field);
var now = new Date();
var age = now.getFullYear() - dob.getFullYear();
var monthDiff = now.getMonth() - dob.getMonth();
if (monthDiff < 0 || (monthDiff === 0 && now.getDate() < dob.getDate())) {
age--;
}
return age;
}
In the above example, when the user enters the date of birth in the format (YYYY-MM-DD), the custom function calculateAge
is invoked and returns the age.
Let’s preview the form to observe how the custom functions are implemented through the rule editor:
Support for asynchronous functions in custom functions support-of-async-functions
Asynchronous custom functions do not appear in the rule editor list. However, it is possible to invoke asynchronous functions within custom functions created using synchronous function expressions.
Look at the code below to see how we can invoke asynchronous functions using custom functions:
async function asyncFunction() {
const response = await fetch('https://petstore.swagger.io/v2/store/inventory');
const data = await response.json();
return data;
}
/**
* callAsyncFunction
* @name callAsyncFunction callAsyncFunction
*/
function callAsyncFunction() {
asyncFunction()
.then(responseData => {
console.log('Response data:', responseData);
})
.catch(error => {
console.error('Error:', error);
});
}
In the above example, the asyncFunction function is an asynchronous function
. It performs an asynchronous operation by making a GET
request to https://petstore.swagger.io/v2/store/inventory
. It waits for the response using await
, parses the response body as JSON using the response.json()
, and then returns the data. The callAsyncFunction
function is a synchronous custom function that invokes the asyncFunction
function and displays the response data in the console. Although the callAsyncFunction
function is synchronous, it calls the asynchronous asyncFunction function and handles its result with then
and catch
statements.
To see its working, let us add a button and create a rule for the button that invokes the asynchronous function upon a button click.
Refer to the illustration of the console window below to demonstrate that when the user clicks the Fetch
button, the custom function callAsyncFunction
is invoked, which in turn calls an asynchronous function asyncFunction
. Inspect the console window to view the response upon the button click:
Let’s dive into the features of custom functions.
Various features for Custom Functions
You can use custom functions to add personalized features to forms. These functions support various abilities such as working with specific fields, using global fields, or caching. This flexibility allows you to customize forms according to your organization’s requirements.
Field and Global scope objects in custom functions support-field-and-global-objects
Field objects refers to the individual components or elements within a form, such as text fields, checkboxes. The Globals object contains read-only variables such as form instance, target field instance and methods to do form modifications within custom functions.
param {scope} globals
has to be the last parameter and it is not displayed in the rule editor of an Adaptive Form.Let’s learn how custom functions use field and global objects with the help of a Contact Us
form using different usecases.
Use Case: Show a panel using the SetProperty
rule
Add the following code in the custom function as explained in the create-custom-functionsection, to set the form field as Required
.
/**
* enablePanel
* @name enablePanel
* @param {object} field1
* @param {object} field2
* @param {scope} globals
*/
function enablePanel(field1,field2, globals)
{
if(globals.functions.validate(field1).length === 0)
{
globals.functions.setProperty(field2, {visible: true});
}
}
- You can configure the field properties using the available properties located in
[form-path]/jcr:content/guideContainer.model.json
. - Modifications made to the form using the
setProperty
method of the Globals object are asynchronous in nature and are not reflected during the execution of the custom function.
In this example, validation of the personaldetails
panel occurs upon clicking the button. If no errors are detected in the panel, another panel, the feedback
panel, becomes visible upon button click.
Let’s create a rule for the Next
button, which validates the personaldetails
panel and makes the feedback
panel visible when the user clicks the Next
button.
Refer to the illustration below to demonstrate where the personaldetails
panel is validated upon clicking the Next
button. In case all the fields within the personaldetails
are validated, the feedback
panel becomes visible.
If errors are present in the fields of the personaldetails
panel, they are displayed at the field level upon clicking the Next
button, and the feedback
panel remains invisible.
Use Case: Validate the field.
Add the following code in the custom function as explained in the create-custom-functionsection, to validate the field.
/**
* validateField
* @name validateField
* @param {object} field
* @param {scope} globals
*/
function validateField(field,globals)
{
globals.functions.validate(field);
}
validate()
function, it validates the form.In this example, a custom validation pattern is applied to the contact
field. Users are required to input a phone number starting with 10
followed by 8
digits. If the user enters a phone number that does not start with 10
or contains more or less than 8
digits, a validation error message appears upon the button click:
Now, next step is to create a rule for the Next
button that validates the contact
field on the button click.
Refer to the illustration below to demonstrate that if the user enters a phone number that does not start with 10
, an error message appears at the field level:
If the user enters a valid phone number and all fields in the personaldetails
panel are validated, the feedback
panel appears on the screen:
Use Case: Reset a panel
Add the following code in the custom function as explained in the create-custom-functionsection, to reset the panel.
/**
* resetField
* @name resetField
* @param {string} input1
* @param {object} field
* @param {scope} globals
*/
function resetField(field,globals)
{
globals.functions.reset(field);
}
reset()
function, it validates the form.In this example, the personaldetails
panel resets upon clicking the Clear
button. Next step is to create a rule for the Clear
button that resets the panel on the button click.
See the illustration below to display that if the user clicks the clear
button, the personaldetails
panel resets:
Use Case: To display custom message at the field level and marking the field as invalid
You can use the markFieldAsInvalid()
function to define a field as invalid and set custom error message at a field level. The fieldIdentifier
value can be fieldId
, or field qualifiedName
, or field dataRef
. The value of the object named option
can be {useId: true}
, {useQualifiedName: true}
, or {useDataRef: true}
.
The syntaxes used to mark field as invalid and set custom message are:
globals.functions.markFieldAsInvalid(field.$id,"[custom message]",{useId: true});
globals.functions.markFieldAsInvalid(field.$qualifiedName, "[custom message]", {useQualifiedName: true});
globals.functions.markFieldAsInvalid(field.$dataRef, "[custom message]", {useDataRef: true});
Add the following code in the custom function as explained in the create-custom-functionsection, to enable custom message at the field level.
/**
* customMessage
* @name customMessage
* @param {object} field
* @param {scope} globals
*/
function customMessage(field, globals) {
const minLength = 15;
const comments = field.$value.trim();
if (comments.length < minLength) {
globals.functions.markFieldAsInvalid(field.$id, "Comments must be at least 15 characters long.", { useId: true });
}
}
In this example, if the user enters less than 15 characters in the comments textbox, a custom message appears at the field level.
Next step is to create a rule for the comments
field:
See the demonstration below to display that entering negative feedback in the comments
field triggers the display of a custom message at the field level:
If the user enters more than 15 characters in commments textbox, the field gets validated and form is submitted:
Use Case: Submit altered data to the server
The following line of code:globals.functions.submitForm(globals.functions.exportData(), false);
is used to submit the form data after manipulation.
- The first argument is the data to be submitted.
- The second argument represents whether the form is to be validated before submission. It is
optional
and set astrue
by default. - The third argument is the
contentType
of the submission, which is also optional with the default value asmultipart/form-data
. The other values can beapplication/json
andapplication/x-www-form-urlencoded
.
Add the following code in the custom function as explained in the create-custom-functionsection, to submit the manipulated data at the server:
/**
* submitData
* @name submitData
* @param {object} field
* @param {scope} globals
*/
function submitData(globals)
{
var data = globals.functions.exportData();
if(!data.comments) {
data.comments = 'NA';
}
console.log('After update:{}',data);
globals.functions.submitForm(data, false);
}
In this example, if the user leaves the comments
textbox empty, the NA
is submitted to the server at form submission.
Now create a rule for the Submit
button which submits data:
Refer to the illustration of the console window
below to demonstrate that if the user leaves the comments
textbox empty, then the value as NA
is submitted at the server:
You can also inspect the console window to view the data submitted to the server:
Caching support for custom function
Adaptive Forms implement caching for custom functions to enhance response time while retrieving the custom function list in the rule editor. A message as Fetched following custom functions list from cache
appears in the error.log
file.
In case the custom functions are modified, the caching becomes invalidated, and it is parsed.
Troubleshooting troubleshooting
-
The user needs to ensure that the core component and specification version is set to the latest version. However, for existing AEM projects and forms, there are additional steps to follow:
-
For the AEM project, the user should replace all instances of
submitForm('custom:submitSuccess', 'custom:submitError')
withsubmitForm()
and deploy the project. -
For existing forms, if the custom submission handlers are not functioning correctly, the user needs to open and save the
submitForm
rule on the Submit button using the Rule Editor. This action replaces the existing rule fromsubmitForm('custom:submitSuccess', 'custom:submitError')
withsubmitForm()
in the form.
-
-
If the JavaScript file containing code for custom functions has an error, the custom functions are not listed in the rule editor of an Adaptive Form. To check the custom function list, you can navigate to the
error.log
file for the error. In case of an error, the custom function list appears empty:In case of there is no error, the custom function are fetched and appear in the
error.log
file. A message asFetched following custom functions list
appears in theerror.log
file:
Considerations
-
The
parameter type
andreturn type
do not supportNone
. -
The functions that are not supported in the custom function list are:
- Generator functions
- Async/Await functions
- Method definitions
- Class methods
- Default parameters
- Rest parameters