Dynamic form set based on user input and external data

hariaem 16-02-2018

Working in a implementation where the we need to collect lot of information in multiple forms from the user. The subsequent forms need to be displayed based on some conditions defined in a rule engine (exposed as REST service, which can tell what is the next form that is eligible for that user). I looked at Form set (Adobe Experience Manager Help | Form set in AEM Forms ) and the eligibility expression, but it seems a simple javascript based implementation. The condition not just changes based on user inputs but also other events out of the forms, so it is important to invoke the rule engine to determine the next form. Does AEM Forms support this dynamic navigation to different forms?

Accepted Solutions (1)

Accepted Solutions (1)

James_R_Green 24-02-2018

Hi hariaem

There are a few requirements here:

1) to hide show - you can use the rules editor to set the visible property of an entire panel based on the value of other fields.

e.g.

Screen Shot 2018-02-24 at 16.57.13.png

Screen Shot 2018-02-24 at 16.55.38.png

Screen Shot 2018-02-24 at 16.58.20.png

Screen Shot 2018-02-24 at 16.58.43.png

Hide by default on form load:

Screen Shot 2018-02-24 at 16.59.01.png

Yes

Screen Shot 2018-02-24 at 16.59.15.png

No

Screen Shot 2018-02-24 at 16.59.20.png

2) User flagged for fraudulent activity (in which case shown a error page and dont take to next form in sequence). This needs to checked with every form submission, before proceeding to next form.

If you are posting your results to a REST endpoint on submit, you can check the user details for fraud and redirect accordingly. Example java servlet:

@Override
protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException, ServletException {

  // Business logic to set redirect URL to next form or error page

  String redirectURL = "http://google.com";

// Redirect the calling application to that page

  response.sendRedirect(redirectURL);

}

3) another loan form is already processed (take to the submitted loan and discard current one)

Presumably to determine whether a form has been submitted previously you need to capture the username/ID or equivalent? Security aside,

if that is the case, the forms logic (in rules or client library) could make an ajax call to some java logic on change of the username field, to check if the person has submitted previously. If they have, you could pass back the guidestate from the previous submission and use the guidebridge restoreGuideState function Adaptive Forms Class: GuideBridge to display the previously submitted data. If they haven't previously submitted, they carry on filling out the form.

Example

Add 3 textboxes username, firstName, lastName

Screen Shot 2018-02-24 at 18.05.57.png

Rules logic on username valueCommit:

Screen Shot 2018-02-24 at 17.58.34.png

var usernameValue = this.value;

$.ajax({

        type: "POST",

        url: "http://localhost:4502/bin/fromDataSubmit",

        dataType:"json",

        data: usernameValue,

        success: function (response) {

guideBridge.restoreGuideState({

                guideState : response.guideState,

                error : function (guideResultObject) {

                    console.log('failed', guideResultObject);

                }

            });

        }

});

In the servlet:

protected void doPost(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException, ServletException {

  // logic to take the username, lookup the previously submitted data, simulated below with guideState string

     String guideState = "{\n" +

   \"guideState\": {\n" +

   \"guideDom\": {\n" +

   \"templateId\": \"guideContainer__\",\n" +

   \"id\": \"\",\n" +

   \"jcr:path\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer\",\n" +

   \"thankYouOption\": \"page\",\n" +

   \"disableSwipeGesture\": false,\n" +

   \"thankYouMessage\": \"Thank you for submitting the form.\",\n" +

   \"guideCss\": \"guideContainer\",\n" +

   \"useExistingAF\": \"false\",\n" +

   \"autoSaveStrategyType\": \"fd/fp/components/actions/autosave/timebased\",\n" +

   \"enableRestEndpointPost\": \"false\",\n" +

   \"fd:version\": \"1.1\",\n" +

   \"name\": \"guide1\",\n" +

   \"guideNodeClass\": \"guideContainerNode\",\n" +

   \"restEndpointPostUrl\": \"/bin/CMAT/fromDataSubmit\",\n" +

   \"themeRef\": \"/content/dam/formsanddocuments-themes/themeLibrary/basicTheme\",\n" +

   \"sling:resourceType\": \"fd/af/components/guideContainerWrapper\",\n" +

   \"textIsRich\": \"true\",\n" +

   \"dorType\": \"none\",\n" +

   \"actionType\": \"fd/af/components/guidesubmittype/restendpoint\",\n" +

   \"layout\": {\n" +

   \"templateId\": \"guideContainer-layout__\",\n" +

   \"id\": \"\",\n" +

   \"jcr:path\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer/layout\",\n" +

   \"toolbarPosition\": \"Bottom\",\n" +

   \"sling:resourceType\": \"fd/af/layouts/defaultGuideLayout\",\n" +

   \"mobileLayout\": \"fd/af/layouts/mobile/step\"\n" +

   " },\n" +

   \"rootPanel\": {\n" +

   \"templateId\": \"guideContainer-rootPanel__\",\n" +

   \"id\": \"\",\n" +

   \"jcr:path\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer/rootPanel\",\n" +

   \"jcr:title\": \"Root Panel\",\n" +

   \"panelSetType\": \"Navigable\",\n" +

   \"dorExcludeTitle\": \"true\",\n" +

   \"name\": \"guideRootPanel\",\n" +

   \"guideNodeClass\": \"rootPanelNode\",\n" +

   \"sling:resourceType\": \"fd/af/components/rootPanel\",\n" +

   \"dorExcludeDescription\": \"true\",\n" +

   \"nonLocalizedTitle\": \"Root Panel\",\n" +

   \"layout\": {\n" +

   \"templateId\": \"guideContainer-rootPanel-layout__\",\n" +

   \"id\": \"\",\n" +

   \"jcr:path\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer/rootPanel/layout\",\n" +

   \"toolbarPosition\": \"Bottom\",\n" +

   \"sling:resourceType\": \"fd/af/layouts/gridFluidLayout\",\n" +

   \"nonNavigable\": true\n" +

   " },\n" +

   \"items\": {\n" +

   \"guidetextbox\": {\n" +

   \"templateId\": \"guideContainer-rootPanel-guidetextbox__\",\n" +

   \"id\": \"\",\n" +

   \"jcr:path\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer/rootPanel/items/guidetextbox\",\n" +

   \"jcr:createdBy\": \"admin\",\n" +

   \"jcr:title\": \"Username\",\n" +

   \"jcr:created\": \"Sat Feb 24 2018 17:19:51 GMT+0000\",\n" +

   \"name\": \"username\",\n" +

   \"guideNodeClass\": \"guideTextBox\",\n" +

   \"sling:resourceType\": \"fd/af/components/guidetextbox\",\n" +

   \"autofillFieldKeyword\": \"name\",\n" +

   \"textIsRich\": [\n" +

   \"true\",\n" +

   \"true\",\n" +

   \"true\"\n" +

   " ],\n" +

   \"nonLocalizedTitle\": \"Username\",\n" +

   \"_value\": \"\"\n" +

   " },\n" +

   \"guidetextbox_356533011\": {\n" +

   \"templateId\": \"guideContainer-rootPanel-guidetextbox_356533011__\",\n" +

   \"id\": \"\",\n" +

   \"jcr:path\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer/rootPanel/items/guidetextbox_356533011\",\n" +

   \"jcr:createdBy\": \"admin\",\n" +

   \"jcr:title\": \"First Name\",\n" +

   \"jcr:created\": \"Sat Feb 24 2018 17:39:49 GMT+0000\",\n" +

   \"name\": \"firstName\",\n" +

   \"guideNodeClass\": \"guideTextBox\",\n" +

   \"sling:resourceType\": \"fd/af/components/guidetextbox\",\n" +

   \"autofillFieldKeyword\": \"name\",\n" +

   \"textIsRich\": [\n" +

   \"true\",\n" +

   \"true\",\n" +

   \"true\"\n" +

   " ],\n" +

   \"nonLocalizedTitle\": \"First Name\",\n" +

   \"_value\": \"James\"\n" +

   " },\n" +

   \"guidetextbox_1815031605\": {\n" +

   \"templateId\": \"guideContainer-rootPanel-guidetextbox_1815031605__\",\n" +

   \"id\": \"\",\n" +

   \"jcr:path\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer/rootPanel/items/guidetextbox_1815031605\",\n" +

   \"jcr:createdBy\": \"admin\",\n" +

   \"jcr:title\": \"Last Name\",\n" +

   \"jcr:created\": \"Sat Feb 24 2018 17:40:25 GMT+0000\",\n" +

   \"name\": \"lastName\",\n" +

   \"guideNodeClass\": \"guideTextBox\",\n" +

   \"sling:resourceType\": \"fd/af/components/guidetextbox\",\n" +

   \"autofillFieldKeyword\": \"name\",\n" +

   \"textIsRich\": [\n" +

   \"true\",\n" +

   \"true\",\n" +

   \"true\"\n" +

   " ],\n" +

   \"nonLocalizedTitle\": \"Last Name\",\n" +

   \"_value\": \"Green\"\n" +

   " },\n" +

   \"submit\": {\n" +

   \"templateId\": \"guideContainer-rootPanel-submit__\",\n" +

   \"id\": \"\",\n" +

   \"jcr:path\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer/rootPanel/items/submit\",\n" +

   \"jcr:createdBy\": \"admin\",\n" +

   \"jcr:title\": \"Submit\",\n" +

   \"jcr:created\": \"Sat Feb 24 2018 17:20:11 GMT+0000\",\n" +

   \"name\": \"submit1519492811863\",\n" +

   \"type\": \"submit\",\n" +

   \"guideNodeClass\": \"guideButton\",\n" +

   \"dorExclusion\": \"true\",\n" +

   \"sling:resourceType\": \"fd/af/components/actions/submit\",\n" +

   \"clickExp\": \"\\ntry {\\n guideBridge.submit();\\\\\\n} catch (e) {\\n window.guideBridge._guide.logger().error(\\\"AF\\\",\\\"Failed to execute rule: \\\" + e);\\n}\\n\",\n" +

   \"nonLocalizedTitle\": \"Submit\",\n" +

   \"fd:rules\": {\n" +

   \"templateId\": \"guideContainer-rootPanel-submit-fd:rules__\",\n" +

   \"id\": \"\",\n" +

   \"jcr:path\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer/rootPanel/items/submit/fd:rules\",\n" +

   \"fd:click\": [\n" +

   \"{\\\"nodeName\\\":\\\"ROOT\\\",\\\"items\\\":[{\\\"nodeName\\\":\\\"STATEMENT\\\",\\\"choice\\\":{\\\"nodeName\\\":\\\"EVENT_SCRIPTS\\\",\\\"items\\\":[{\\\"nodeName\\\":\\\"EVENT_CONDITION\\\",\\\"choice\\\":{\\\"nodeName\\\":\\\"EVENT_AND_COMPARISON\\\",\\\"items\\\":[{\\\"nodeName\\\":\\\"COMPONENT\\\",\\\"value\\\":{\\\"id\\\":\\\"XX\\\",\\\"type\\\":\\\"BUTTON\\\",\\\"name\\\":\\\"XX\\\"}},{\\\"nodeName\\\":\\\"EVENT_AND_COMPARISON_OPERATOR\\\",\\\"choice\\\":{\\\"nodeName\\\":\\\"is clicked\\\",\\\"value\\\":null}},{\\\"nodeName\\\":\\\"PRIMITIVE_EXPRESSION\\\",\\\"choice\\\":null}]},\\\"nested\\\":false},{\\\"nodeName\\\":\\\"Then\\\",\\\"value\\\":null},{\\\"nodeName\\\":\\\"BLOCK_STATEMENTS\\\",\\\"items\\\":[{\\\"nodeName\\\":\\\"BLOCK_STATEMENT\\\",\\\"choice\\\":{\\\"nodeName\\\":\\\"SUBMIT_FORM\\\",\\\"items\\\":[]}}]}]}}],\\\"isValid\\\":true,\\\"enabled\\\":true,\\\"version\\\":1,\\\"script\\\":\\\"/**\\\\n\\\\n * This is a machine-generated code for the rule.\\\\n * If you modify it in the code editor, you will not be able to view and edit the rule in the visual editor.\\\\n */\\\\n\\\\nguideBridge.submit();\\\\n\\\\n\\\",\\\"eventName\\\":\\\"Click\\\"}\"\n" +

   " ]\n" +

   " }\n" +

   " }\n" +

   " }\n" +

   " },\n" +

   \"autoSaveInfo\": {\n" +

   \"templateId\": \"guideContainer-autoSaveInfo__\",\n" +

   \"id\": \"\",\n" +

   \"jcr:path\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer/autoSaveInfo\",\n" +

   \"metadataselector\": \"global\"\n" +

   " },\n" +

   \"signerInfo\": {\n" +

   \"templateId\": \"guideContainer-signerInfo__\",\n" +

   \"id\": \"\",\n" +

   \"jcr:path\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer/signerInfo\",\n" +

   \"firstSignerFormFiller\": \"false\",\n" +

   \"workflowType\": \"SEQUENTIAL\",\n" +

   \"signer0\": {\n" +

   \"templateId\": \"guideContainer-signerInfo-signer0__\",\n" +

   \"id\": \"\",\n" +

   \"jcr:path\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer/signerInfo/signer0\",\n" +

   \"countryCode\": \"undefined\",\n" +

   \"phoneSource\": \"form\",\n" +

   \"securityOption\": \"NONE\",\n" +

   \"signerTitle\": \"Signer One\",\n" +

   \"email\": \"undefined\",\n" +

   \"phone\": \"undefined\",\n" +

   \"countryCodeSource\": \"form\",\n" +

   \"emailSource\": \"form\"\n" +

   " }\n" +

   " },\n" +

   \"repeatBindRef\": [],\n" +

   \"guideGlobalLazyField\": [],\n" +

   \"allLazyChildren\": [],\n" +

   \"assetRefs\": [],\n" +

   \"submitSuccess\": \"window.guideBridge._defaultSubmitSuccessHandler($event.data)\",\n" +

   \"submitError\": \"window.guideBridge._handleServerValidationError($event.data)\",\n" +

   \"autoSaveStart\": false\n" +

   " },\n" +

   \"guideContext\": {\n" +

   \"customPropertyMap\": {\n" +

   \"runtimeLocale\": \"en\",\n" +

   \"lastFocusItem\": \"guide[0].guide1[0].guideRootPanel[0].firstName[0]\"\n" +

   " },\n" +

   \"guidePath\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer\",\n" +

   \"guideName\": \"guide1\",\n" +

   \"disablePreview\": false,\n" +

   \"makeFileNameUnique\": false,\n" +

   \"scriptingBehaviourVersion\": \"None\",\n" +

   \"afSubmissionInfo\": {\n" +

   \"lastFocusItem\": \"guide[0].guide1[0].guideRootPanel[0].firstName[0]\",\n" +

   \"computedMetaInfo\": {},\n" +

   \"signers\": {}\n" +

   " }\n" +

   " },\n" +

   \"additionalSubmitInfo\": {\n" +

   \":redirect\": \"/content/forms/af/test/test-redirect/jcr:content/guideContainer.guideThankYouPage.html\",\n" +

   \":selfUrl\": \"/content/forms/af/test/test-redirect\",\n" +

   \"_guideValueMap\": \"yes\",\n" +

   \"_guideValuesMap\": \"{\\\"username\\\":null,\\\"firstName\\\":\\\"James\\\",\\\"lastName\\\":\\\"Green\\\",\\\"submit1519492811863\\\":null}\"\n" +

   " }\n" +

   " }\n" +

   "}";

  response.getWriter().write(guideState);

  response.setStatus(SlingHttpServletResponse.SC_OK);

}

Thanks,

James

Answers (2)

Answers (2)

hariaem 23-02-2018

Hi, to give an example, lets say the goal is to build loan processing system, where details related to the user is being collected. If the user declares married, then additional form needs to be shown to collect spouse details. However there may be external (to aem forms) events like 

- User flagged for fraudulent activity (in which case shown a error page and dont take to next form in sequence). This needs to checked with every form submission, before proceeding to next form.

- another loan form is already processed (take to the submitted loan and discard current one)

James_R_Green 17-02-2018

Hi Do you have an example of the kind of variables/rules that effect the users journey? Putting these things into context help alot.

From what i understand you are looking at setting the redirect manually via Javascript