May 15, 2020
Estimated Post Reading Time ~

Creating Your First Custom AEM Component Using React – Part 1

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;
}

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



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

Source: https://blogs.perficient.com/2019/09/17/creating-your-first-custom-aem-component-using-react-part-1/


By aem4beginner

No comments:

Post a Comment

If you have any doubts or questions, please let us know.