Recently, I went through an article about integrating React JS and Angular JS with AEM. In this blog, I am going to show you how to create a custom component that includes a cq:dialog and one that does not include a cq:dialog. Before building the components, clone the repository, which is a sample project based on React JS.

First, we will deploy this project in AEM 6.5. Then, we will create one sample component called custom-heading.

The component structure should look like this.

1. Component node (<project root>/apps/my-aem-project/components/content/custom-heading)

<?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" jcr:primaryType="cq:Component" jcr:title="Custom Heading" componentGroup="My AEM Project"/>

2. cq:dialog

<?xml version="1.0" encoding="UTF-8"?> <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/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="Custom Heading" sling:resourceType="cq/gui/components/authoring/dialog"> <content jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container"> <items jcr:primaryType="nt:unstructured"> <tabs jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/tabs" maximized="{Boolean}true"> <items jcr:primaryType="nt:unstructured"> <general jcr:primaryType="nt:unstructured" jcr:title="General" sling:resourceType="granite/ui/components/coral/foundation/container" margin="{Boolean}true"> <items jcr:primaryType="nt:unstructured"> <columns jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns" margin="{Boolean}true"> <items jcr:primaryType="nt:unstructured"> <column jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/container"> <items jcr:primaryType="nt:unstructured"> <heading jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/textfield" fieldLabel="Heading" name="./heading" required="{Boolean}true"/> <heading-type jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/select" fieldLabel="Heading Type" name="./headingType"> <items jcr:primaryType="nt:unstructured"> <h1 jcr:primaryType="nt:unstructured" text="H1" value="h1"/> <h2 jcr:primaryType="nt:unstructured" text="H2" value="h2"/> <h3 jcr:primaryType="nt:unstructured" text="H3" value="h3"/> </items> </heading-type> <heading-color jcr:primaryType="nt:unstructured" sling:resourceType="granite/ui/components/coral/foundation/form/select" fieldLabel="Heading Color" name="./headingColor"> <items jcr:primaryType="nt:unstructured"> <red jcr:primaryType="nt:unstructured" text="Red" value="red-color"/> <green jcr:primaryType="nt:unstructured" text="Green" value="green-color"/> <blue jcr:primaryType="nt:unstructured" text="Blue" value="blue-color"/> </items> </heading-color> </items> </column> </items> </columns> </items> </general> </items> </tabs> </items> </content> </jcr:root>

Now the question is why have I not created custom-heading.html? For a React-based component, we are not going to create any custom-heading.html. Also, we are not going to use HTL/Sightly to render logic. Surprising, right? We will render the component HTML using React. Therefore, I have created a React component called CustomHeading.

In the project you’ve cloned, create the CustomHeading (<project root>/react-app/src/components/CustomHeading) folder and create the following two files.

The react component structure should look like this.

1. CustomHeading.js

import React, {Component} from 'react'; import {MapTo} from '@adobe/cq-react-editable-components'; require('./CustomHeading.scss'); const CustomHeadingEditConfig = { emptyLabel: 'Custom Heading', isEmpty: function(props) { return !props || props.heading.trim().length < 1; } }; export default class CustomHeading extends Component { render() { return (<div className="heading"> First Component </div>); } } MapTo('my-aem-project/components/content/custom-heading')(CustomHeading, CustomHeadingEditConfig);

2. CustomHeading.scss

.red-color{ color: #fc0b03; } .green-color{ color : #39fc03; } .blue-color{ color : #2403fc; }

Content for Everyone Companies that can quickly and consistently meet the demands of consumers are thriving in an era of infinite content. Learn about how to build fluid experiences for your omnichannel customers. Get the Guide

Now, the question is still how do you read the authored value in a React component? For this, we need a Sling Model exporter. I have created one Sling Model exporter class.

The Sling Model will look like this.

CustomHeadingModel.java

package com.surajkamdi.core.models; import com.adobe.cq.export.json.ComponentExporter; import com.adobe.cq.export.json.ExporterConstants; import javax.annotation.Nonnull; import org.apache.sling.api.SlingHttpServletRequest; import org.apache.sling.models.annotations.DefaultInjectionStrategy; import org.apache.sling.models.annotations.Exporter; import org.apache.sling.models.annotations.Model; import org.apache.sling.models.annotations.injectorspecific.ValueMapValue; @Model(adaptables = SlingHttpServletRequest.class, resourceType = CustomHeadingModel.RESOURCE_TYPE, adapters = {CustomHeadingModel.class, ComponentExporter.class}, defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL) @Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION) public class CustomHeadingModel implements ComponentExporter { protected static final String RESOURCE_TYPE = "my-aem-project/components/content/custom-heading"; @ValueMapValue(name = "heading") private String heading; @ValueMapValue(name = "headingType") private String headingType; @ValueMapValue(name = "headingColor") private String headingColor; public String getHeading() { return heading; } public String getHeadingType() { return headingType; } public String getHeadingColor() { return headingColor; } @Nonnull @Override public String getExportedType() { return RESOURCE_TYPE; } }

Run the build and deploy using the following Maven commands:

mvn clean install -PautoInstallPackage -Padobe-public

Now let’s add this component into the page and do some authoring, then get the resource path of component. Open a new tab in the browser window and paste the following URL constructed using resource path in the following format:

http://localhost:4502/<content_root_path>/custom_heading.model.json

You will notice authored values are also present here in the above structure. This is the output rendered by the above Sling Model exporter class.

In order to add this component into the page created in AEM, we need to import the component inside file called <project root>/react-app/src/ Index.js. If you are using the above-mentioned repository structure, then you need to include a component path inside MappedComponents.js. Otherwise, you will not able to add a component into the page.

MappedComponets.js

require('./CustomHeading/CustomHeading');

Finally, the last step is to read the above JSON in the React component and render the logic to achieve component functionality.

CustomHeading.js

import React, {Component} from 'react'; import {MapTo} from '@adobe/cq-react-editable-components'; require('./CustomHeading.scss'); const CustomHeadingEditConfig = { emptyLabel: 'Custom Heading', isEmpty: function(props) { return !props.heading || props.heading.trim().length < 1; } }; export default class CustomHeading extends Component { render() { let headingElement = this.props.headingType ? React.createElement(this.props.headingType, {className: this.props.headingColor},this.props.heading) : ''; return (<div className="heading"> {headingElement} </div>); } } MapTo('my-aem-project/components/content/custom-heading')(CustomHeading, CustomHeadingEditConfig);

Now, deploy your code using Maven commands.

mvn clean install -PautoInstallPackage -Padobe-public



I hope this blog post was helpful. In the next blog, I will create a component without using cq:dialog.

If you are still confused about working with the React component, visit here or comment below.