Adobe Experience Manager (AEM) Maven Project Archetype 23

Maven AEM Project Archetype 23 was recently released. To use, setup your local development environment for AEM as a Cloud Service SDK or use one of the following AEM versions: 6.5.0+, 6.4.4.0+ or 6.3.3.4+. Additionally, Java 8 or 11 and Maven 3.3.9+ is also required. Here is an overview of a few of the changes.

Project Generation

In a terminal, create a directory for the source code. e.g.,

cd repo_base_foldername

mkdir src

In the source code directory src, use the mvn command to init the project with the desired archetype version.

The number of prompts for property values when generating the project has been reduced. By default, artifactId will use the appId value and package will use the groupId value. You can change these to use different values if you prefer by adding them to your generate command line.

This is a minimal example for batch mode project generation:

cd src

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="My Project" \
 -D appId="myproject" \
 -D groupId="com.myproject" \
 -D frontendModule=general \
 -D includeExamples=n

Available Properties

Name Default Description
appTitle For website title and component groups
appId For app, conf and content folder names; clientlib names
artifactId ${appId} Base Maven artifact ID
groupId Base Maven group ID
package ${groupId} Java Source Package
version 1.0-SNAPSHOT Project version
aemVersion 6.5.0 Target AEM version (can be cloud for AEM as a Cloud Service; or 6.5.0, 6.4.4, or 6.3.3 for Adobe Managed Services or on-premise)
sdkVersion latest When aemVersion=cloud an SDK version can be specified
includeDispatcherConfig y Includes a dispatcher configuration either for cloud or for AMS/on-premise, depending of the value of aemVersion (y/n)
frontendModule none Includes a Webpack frontend build module that generates the clientlibs (general or none; angular or react for SPA)
languageCountry en_us Language and country code to create the content structure from (e.g. en_us)
singleCountry y Includes a language-master content structure (y/n)
includeExamples y Includes a Component Library example site (y/n)
includeErrorHandler n Includes a custom 404 response page that will be global to the entire instance (y/n)

React and Angular Support

The AEM SPA Project Archetype has been merged into this version to facilitate a React or Angular Single Page App that implements the SPA Editor.

Example Content

  • Styling updated for example content
  • Responsivegrid replaced with core container component
  • Experience fragment simplified for the header and footer
  • Custom helloworld component updated

AEM Archetype 23 Example Content Page
Example Content Page

Other Changes Include

  • Support added for AEM as a Cloud Service SDK
  • Mapping / rewrite rules to support the image core component. For example, when using the dispatcher module with the associated Resource Resolver Factory rewrite rule, images referenced in /content/wknd should resolve.
  • org.apache.sling.servlets.annotations dependency management added
  • use Sling’s org.apache.sling.bnd.models.ModelsScannerPlugin which automatically identifies and registers SlingModels in the project.
  • repository-structure project renamed to ui.apps.structure

Component Client-side JavaScript

This is the ui.frontend JS for the helloworld component (general module configuration).

5/18/2020 - minor bugfix merged into master

helloworld.js
// Example of how a component should be initialized via JavaScript
// This script logs the value of the component's text property model message to the console

(function() {
    "use strict";

    // Best practice:
    // For a good separation of concerns, don't rely on the DOM structure or CSS selectors,
    // but use dedicated data attributes to identify all elements that the script needs to
    // interact with.
    var selectors = {
        self:      '[data-cmp-is="helloworld"]',
        property:  '[data-cmp-hook-helloworld="property"]',
        message:   '[data-cmp-hook-helloworld="model"]'
    };

    function HelloWorld(config) {

        function init(config) {
            // Best practice:
            // To prevents multiple initialization, remove the main data attribute that
            // identified the component.
            config.element.removeAttribute("data-cmp-is");

            var property = config.element.querySelectorAll(selectors.property);
            property = property.length == 1 ? property[0].textContent : null;

            var model = config.element.querySelectorAll(selectors.message);
            model = model.length == 1 ? model[0].textContent : null;

            if (console && console.log) {
                console.log(
                    "HelloWorld component JavaScript example",
                    "\nText property:\n", property,
                    "\nModel message:\n", model
                );
            }
        }

        if (config && config.element) {
            init(config);
        }
    }

    // Best practice:
    // Use a method like this mutation obeserver to also properly initialize the component
    // when an author drops it onto the page or modified it with the dialog.
    function onDocumentReady() {
        var elements = document.querySelectorAll(selectors.self);
        for (var i = 0; i < elements.length; i++) {
            new HelloWorld({ element: elements[i] });
        }

        var MutationObserver = window.MutationObserver || window.WebKitMutationObserver || window.MozMutationObserver;
        var body             = document.querySelector("body");
        var observer         = new MutationObserver(function(mutations) {
            mutations.forEach(function(mutation) {
                // needed for IE
                var nodesArray = [].slice.call(mutation.addedNodes);
                if (nodesArray.length > 0) {
                    nodesArray.forEach(function(addedNode) {
                        if (addedNode.querySelectorAll) {
                            var elementsArray = [].slice.call(addedNode.querySelectorAll(selectors.self));
                            elementsArray.forEach(function(element) {
                                new HelloWorld({ element: element });
                            });
                        }
                    });
                }
            });
        });

        observer.observe(body, {
            subtree: true,
            childList: true,
            characterData: true
        });
    }

    if (document.readyState !== "loading") {
        onDocumentReady();
    } else {
        document.addEventListener("DOMContentLoaded", onDocumentReady);
    }

}());

Part 3 of 4 in the AEM Maven Project series.

Part 1 | Adobe Experience Manager (AEM) Maven Project Part II | Adobe Experience Manager (AEM) Maven Project Archetype 24

comments powered by Disqus