AEM 6550 - Create a sample SPA React App using AEM SPA Editor


This tutorial on official adobe docs is an excellent guide for creating your first AEM SPA React App, however if you are looking for some quick steps, go through the following... (lets not terms like react, es6, babel, web pack intimidate you... just creating an authorable spa component using some front end language called React )




1) Create the project structure (for both React SPA and traditional authoring) with the following command using maven archetype - https://github.com/adobe/aem-project-archetype

mvn -B archetype:generate -D archetypeGroupId=com.adobe.granite.archetypes -D archetypeArtifactId=aem-project-archetype 
-D archetypeVersion=23  -D aemVersion=6.5.0  -D appTitle="Experience AEM SPA React"  -D appId="eaem-sites-spa-how-to-react"  -D groupId="com.eaem"  
-D frontendModule=react  -D includeExamples=n  -D includeErrorHandler=n -D includeDispatcherConfig=n

2) The argument -D frontendModule=react in above command creates a ui.frontend folder with a react app (and sample components) adding the following AEM SPA editor npm dependencies in ui.frontend/package.json

        "scripts": {
...
"build": "react-scripts build && clientlib",
...
"sync": "aemsync -d -w ../ui.apps/src/main/content"
},

        "dependencies": {
"@adobe/cq-react-editable-components": "^1.2.0",
"@adobe/cq-spa-component-mapping": "^1.0.3",
"@adobe/cq-spa-page-model-manager": "^1.1.0",
...
},
        "devDependencies": {
...
"aem-clientlib-generator": "^1.5.0",
"aemsync": "^4.0.0",
...
}

3) If you are not using maven archetype or a prebuilt package.json, but creating the structure on your own, you'd probably be executing the following commands....

                       a. Install latest version of Node and NPM from https://nodejs.org/en/ 
                       b. Install create-react-app  npm install --g create-react-app
                       c. Create the react app create-react-app eaem-sites-spa-how-to-react
                       d. Quick build check npm run build
                       e. Install the AEM client lib generator npm install aem-clientlib-generator --save-dev
                       f. Check the clientlib configuration in eaem-sites-spa-how-to-react\clientlib.config.js
                       g. Edit package.json in eaem-sites-spa-how-to-react\ui.frontend to update the build command. "build": "react-scripts build && clientlib"
                       g. Install AEM JS SDK for SPA editor 
                                                  npm install @adobe/cq-spa-component-mapping
                                                  npm install @adobe/cq-spa-page-model-manager
                                                  npm install @adobe/cq-react-editable-components
                       i. Install react router dependencies
                                                  npm install --save react-router 
                                                  npm install --save react-router-dom
                       i. Install aem sync npm install -g aemsync
                                                  
4) After creating the project structure, removed all unnecessary modules and sample code generated to keep only the following components required to build our simple SPA site...

                       /apps/eaem-sites-spa-how-to-react/components/image
                       /apps/eaem-sites-spa-how-to-react/components/navigation
                       /apps/eaem-sites-spa-how-to-react/components/text
                       /apps/eaem-sites-spa-how-to-react/components/title                       

5) The following spa editable templates are also created by archetype for the app root and app pages...

                       /conf/eaem-sites-spa-how-to-react/settings/wcm/templates/spa-app-template
                       /conf/eaem-sites-spa-how-to-react/settings/wcm/templates/spa-page-template

6) Added the following template type in source code for creating new spa editable templates...

                       /conf/eaem-sites-spa-how-to-react/settings/wcm/template-types/spa-page

7) Let's create a new component Image for using in the SPA editor (sample package in Github has other components like Navigation, Text...)

8) Create the component folder ui.apps\src\main\content\jcr_root\apps\eaem-sites-spa-how-to-react\components\image and the cq:dialog with following code

<?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"
    jcr:title="Image"
    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">
                    <image
                        jcr:primaryType="nt:unstructured"
                        jcr:title="Image"
                        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">
                                            <image
                                                jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
                                                fieldLabel="Image"
                                                name="./imageURL"
                                                renderReadOnly="{Boolean}false"
                                                required="{Boolean}true"
                                                rootPath="/content/dam"/>
                                            <imagelink
                                                jcr:primaryType="nt:unstructured"
                                                sling:resourceType="granite/ui/components/coral/foundation/form/pathfield"
                                                fieldDescription="Enter the Link URL"
                                                fieldLabel="Image Link"
                                                name="./imageLink"
                                                rootPath="/content/eaem-sites-spa-how-to-react/us/en"/>
                                        </items>
                                    </column>
                                </items>
                            </columns>
                        </items>
                    </image>
                </items>
            </tabs>
        </items>
    </content>
</jcr:root>

9) Add the following code in ui.frontend\src\components\Image\Image.js. React uses ES6 based language and transpiles the code to javascript. No HTL code is required in AEM, because the display will be handled by script in React

import { MapTo } from '@adobe/cq-react-editable-components';
import DOMPurify from 'dompurify';
import React, { Component } from 'react';
import {Link} from "react-router-dom";

const ImageEditConfig = {
    emptyLabel: 'Image - Experience AEM',

    isEmpty: function (props) {
        return (!props || !props.imageURL || (props.imageURL.trim().length < 1));
    }
};

class Image extends Component {
    get imageHTML() {
        const imgStyles = {
            "display": 'block',
            "margin-left": 'auto',
            "margin-right": 'auto'
        };

        return (
            <div>
                <Link to={this.props.imageLink}>
                    <img src={this.props.imageURL} style={imgStyles}/>
                </Link>
            </div>
        );
    }

    render() {
        return this.imageHTML;
    }
}

export default MapTo('eaem-sites-spa-how-to-react/components/image')(Image,ImageEditConfig);

10) #36 in above code states this react component is mapped to AEM component eaem-sites-spa-how-to-react/components/image

11) The page model in json format for above component is provided by Sling Model Jackson Exporter (selector .model.json) available at url http://localhost:4502/content/eaem-sites-spa-how-to-react/us/en.model.json



12) For testing any quick edits to the render script in react app, run aem sync to listen to any code changes in the app and sync them to AEM...



13) After making code edits in render script, run npm run build to build the react app JS which is copied to \ui.apps\src\main\content\jcr_root\apps\eaem-sites-spa-how-to-react\clientlibs\clientlib-react by aem sync (configuration provided in ui.frontend\clientlib.config.js)



14) Also removed the npm run test from frontend-maven-plugin in ui.frontend\pom.xml ( if there are no unit tests for react components )

No comments:

Post a Comment