Quantcast
Channel: Experiencing Adobe Experience Manager (AEM, CQ)
Viewing all articles
Browse latest Browse all 525

AEM Cloud Service - Digital OnBoarding Process, Post Lead Form Data to a AEM React SPA

$
0
0

Goal

Process detailed below explains a sample Digital OnBoarding Flow, where a potential customer enters basic information in a Lead Entry page setup on AEM Sites. Form data can be stored in AEM or Adobe Campaign or any external system (for email outreach incase user does not continue with the process) and the user is later POST-forwarded to a Digital OnBoarding AEM React Spa App which shows the basic information entered in lead entry page and more steps for converting the lead to a customer. This post does not discuss Analytics and Campaign pieces...

Demo | Package Install | Content Package | Github


The Flow



Lead Entry Form



Digital On-Boarding SPA Authoring


Digital On-Boarding SPA

Solution

1) Create the AEM React SPA project using archetype https://github.com/adobe/aem-project-archetype

mvn -B archetype:generate -D archetypeGroupId=com.adobe.aem -D archetypeArtifactId=aem-project-archetype 
-D archetypeVersion=24 -D aemVersion=cloud -D appTitle="Experience AEM SPA read Post data"
-D appId="eaem-cs-spa-read-post-data" -D groupId="apps.experienceaem.sites.spa"
-D frontendModule=react -D includeExamples=n -D includeDispatcherConfig=n


2) Create the Lead Entry Form Component /apps/eaem-cs-spa-read-post-data/components/steps extending the core/wcm/components/title/v2/title component. Add additional fields in dialog for enabling them in end user form...

<?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">
<content jcr:primaryType="nt:unstructured">
<items jcr:primaryType="nt:unstructured">
<tabs jcr:primaryType="nt:unstructured">
<items jcr:primaryType="nt:unstructured">
<eaem
jcr:primaryType="nt:unstructured"
jcr:title="Experience AEM"
sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns"
margin="{Boolean}true">
<items jcr:primaryType="nt:unstructured">
<column
jcr:primaryType="nt:unstructured"
jcr:title="Enable the fields for this step"
sling:resourceType="granite/ui/components/coral/foundation/form/fieldset">
<items jcr:primaryType="nt:unstructured">
<name
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/checkbox"
name="./showName"
text="Show Name"
value="true"/>
<email
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/checkbox"
name="./showEmail"
text="Show Email"
value="true"/>
<ssn
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/checkbox"
name="./showSSN"
text="Show SSN"
value="true"/>
<company
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/checkbox"
name="./showCompany"
text="Show Company"
value="true"/>
<previousLink
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldDescription="Select the Previous step"
fieldLabel="Previous step"
name="./previousLink"
rootPath="/content/eaem-cs-spa-read-post-data/us/en"/>
<nextLink
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
fieldDescription="Select the Next step"
fieldLabel="Next step"
name="./nextLink"
rootPath="/content/eaem-cs-spa-read-post-data/us/en"/>
</items>
</column>
</items>
</eaem>
</items>
</tabs>
</items>
</content>
</jcr:root>


3) Add the HTL script for rendering the lead entry form component /apps/eaem-cs-spa-read-post-data/components/steps/steps.html

&ltform method="post" action="/content/eaem-cs-spa-read-post-data/us/en/home.html"&gt
&ltdiv&gt
&lth1 style="text-align: center;"&gtLead Form&lt/h1&gt

&ltdiv class='eaem-info'&gt
&ltspan&gtEnter name&lt/span&gt
&ltinput name='eaemName'/&gt
&lt/div&gt

&ltdiv class='eaem-info'&gt
&ltspan&gtEnter email&lt/span&gt
&ltinput name='eaemEmail'/&gt
&lt/div&gt
&lt/div&gt

&ltdiv style="text-align: center; margin-top: 40px"&gt&ltinput type="submit"/&gt&lt/div&gt
&lt/form&gt


4) Since the data in Lead entry form is POSTed to SPA app (for security, so the user info is not visible in URL as with GET) hosted on url /content/eaem-cs-spa-read-post-data/us/en/home.html, add the path in CSRF filter configuration eaem-cs-spa-read-post-data\ui.config\src\main\content\jcr_root\apps\eaem-cs-spa-read-post-data\osgiconfig\config\com.adobe.granite.csrf.impl.CSRFFilter.config


5) Add a filter apps.experienceaem.sites.spa.core.filters.OnBoardingPostModifierFilter to convert the POST request to a GET request, so AEM does not strictly treat it as a POST and try to modify the node /content/eaem-cs-spa-read-post-data/us/en/home

package apps.experienceaem.sites.spa.core.filters;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.SlingHttpServletResponse;
import org.apache.sling.api.wrappers.SlingHttpServletRequestWrapper;
import org.osgi.framework.Constants;
import org.osgi.service.component.annotations.Component;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.*;
import java.io.IOException;

@Component(
service = Filter.class,
immediate = true,
name = "Experience AEM convert SPA home requests from POST to GET ",
property = {
Constants.SERVICE_RANKING + ":Integer=-99",
"sling.filter.scope=COMPONENT",
"sling.filter.pattern=(/content/eaem-cs-spa-read-post-data/us/en/home*)",
}
)
public class OnBoardingPostModifierFilter implements Filter {
private static final Logger log = LoggerFactory.getLogger(OnBoardingPostModifierFilter.class);

@Override
public void init(FilterConfig filterConfig) throws ServletException {
}

@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
throws IOException, ServletException {
SlingHttpServletRequest slingRequest = (SlingHttpServletRequest) request;
SlingHttpServletResponse slingResponse = (SlingHttpServletResponse) response;

try {
if(!slingRequest.getMethod().equals("POST")){
chain.doFilter(request, response);
return;
}

slingResponse.setHeader("Dispatcher", "no-cache");

RequestDispatcher dp = request.getRequestDispatcher(slingRequest.getRequestPathInfo().getResourcePath() + ".html");

dp.include(new GetSlingServletRequestWrapper(slingRequest), response);
} catch (Exception e) {
log.error("Error converting POST to GET of SPA home : " + slingRequest.getRequestURI());
}
}

@Override
public void destroy() {
}

private class GetSlingServletRequestWrapper extends SlingHttpServletRequestWrapper {
public GetSlingServletRequestWrapper(final SlingHttpServletRequest request) {
super(request);
}

public String getMethod() {
return "GET";
}
}
}


6) For the SPA App to read POSTed form data from a JS object added in window.eaemInitialData create a model apps.experienceaem.sites.spa.core.models.PostParamsModel

package apps.experienceaem.sites.spa.core.models;

import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.models.annotations.Model;
import org.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

@Model(
adaptables = {SlingHttpServletRequest.class}
)
public class PostParamsModel {
private static Logger log = LoggerFactory.getLogger(PostParamsModel.class);

@Inject
SlingHttpServletRequest request;

private String eaemInitialData;

@PostConstruct
protected void init() {
String eaemName = request.getParameter("eaemName");
String eaemEmail = request.getParameter("eaemEmail");

JSONObject jsonObject = new JSONObject();

try{
jsonObject.put("eaemName", eaemName);
jsonObject.put("eaemEmail", eaemEmail);
}catch (Exception e){
log.error("Error creating eaemInitialData from request",e);
}

eaemInitialData = jsonObject.toString();
}

/**
* @return brand
*/
public String getEaemInitialData() {
return eaemInitialData;
}
}


7) Initialize window.eaemInitialData object with POSTed form data in SPA App root page eaem-cs-spa-read-post-data\ui.apps\src\main\content\jcr_root\apps\eaem-cs-spa-read-post-data\components\page\body.html

<noscript>You need to enable JavaScript to run this app.</noscript>
<script data-sly-use.model="apps.experienceaem.sites.spa.core.models.PostParamsModel" data-sly-test="${model.eaemInitialData}">
window.eaemInitialData = ${model.eaemInitialData @ context='unsafe'};
</script>
<div id="spa-root"></div>


9) Crate a sling model exporter apps.experienceaem.sites.spa.core.models.EAEMGenericComponentSlingExporter for the Steps component...

package apps.experienceaem.sites.spa.core.models;

import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import com.adobe.cq.wcm.core.components.models.Image;
import org.apache.sling.api.SlingHttpServletRequest;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.models.annotations.Exporter;
import org.apache.sling.models.annotations.Model;
import org.apache.sling.models.annotations.injectorspecific.SlingObject;

import javax.annotation.PostConstruct;
import javax.inject.Inject;
import java.util.Map;

@Model(
adaptables = {SlingHttpServletRequest.class},
adapters = {ComponentExporter.class},
resourceType = {
"eaem-cs-spa-read-post-data/components/image",
"eaem-cs-spa-read-post-data/components/steps"
}
)
@Exporter(
name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
extensions = ExporterConstants.SLING_MODEL_EXTENSION
)
public class EAEMGenericComponentSlingExporter implements ComponentExporter {

@Inject
private Resource resource;

@PostConstruct
protected void initModel() {
}

public ValueMap getEaemData(){
return resource.getValueMap();
}

@Override
public String getExportedType() {
return resource.getResourceType();
}
}


8) Create the React render script eaem-cs-spa-read-post-data\ui.frontend\src\components\Steps\Steps.tsx for component /apps/eaem-cs-spa-read-post-data/components/steps. It reads the data entered in lead form entry from window.eaemInitialData

import { MapTo } from "@adobe/aem-react-editable-components";
import React, { FC, useState, useEffect } from "react";
import {Link} from "react-router-dom";
import "./StepsStyles.css";

type StepsProps = {
[x: string]: any
};

declare global {
interface Window {
eaemInitialData: any;
}
}

const StepsEditConfig = {
emptyLabel: "Steps - Experience AEM",

isEmpty: function (props: any) {
return !props || !props["jcr:title"];
}
};

const AEMSteps: FC<StepsProps> = props => {
const eaemInitialData = window.eaemInitialData;

return (
<div>
<h1 style={{ textAlign: "center", color: "maroon" }}>
{props["jcr:title"]}
</h1>

{
props.showName &&
<div className='eaem-info'>
<span>Enter name</span>
<input name='eaemName' value={eaemInitialData.eaemName}></input>
</div>
}
{
props.showEmail &&
<div className='eaem-info'>
<span>Enter email</span>
<input name='eaemEmail' value={eaemInitialData.eaemEmail}></input>
</div>
}
{
props.showSSN &&
<div className='eaem-info'>
<span>Enter SSN</span>
<input name='eaemSSN' value={eaemInitialData.eaemSSN}></input>
</div>
}
{
props.showCompany &&
<div className='eaem-info'>
<span>Enter company</span>
<input name='eaemCompany' value={eaemInitialData.eaemCompany}></input>
</div>
}

<div className='eaem-info'>
{
props.previousLink &&
<Link to={props.previousLink}>
<button type="button">Previous</button>
</Link>
}
{
props.nextLink &&
<Link to={props.nextLink}>
<button type="button">Next</button>
</Link>
}
</div>
</div>
);
};

export default MapTo("eaem-cs-spa-read-post-data/components/steps")(AEMSteps, StepsEditConfig);




Viewing all articles
Browse latest Browse all 525

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>