Builds ReactJS/Vue/Angular enabled AEM Touch UI components with dialogue.
Copyright 2019 by CQ-Factory GmbH - www.cq-factory.de
Authors: Siegfried-Thor Bolz, Yevgeniy Kravetsky
The Component-Generator is a tool for creating Adobe Experience Manager components with support for the UI rendering technologies HTL/Sightly, ReactJS, Vue and Angular (current version supports only HTL and ReactJS).
The user creates for each component a specific component-configuration file written in TypeScript, which the Component-Generator Builder uses
to create components with Touch UI dialogues and support for the selected UI rendering technology.
Using React, it uses our fantastic React-Loader for AEM to render the React component into a basic HTL-file and it also provides the CQ-dialog properties for
the React component .tsx file. All done without deploying special Java Sling Models (can be done to implement special Getters for properties) or AEM configurations. Works with AEM 6.4 SP2 or higher. For lower AEM versions we could provide special Sling Models.
The Component-Generator can easily integrated into existing AEM projects and helps you to create faster AEM components.
For the latest stable version:
``bash`
npm install -g @cqfactory/component-generator
Include as a dependency into existing package.json:
`bash`
"devDependencies": {
"@cqfactory/component-generator": "^0.3.11"
Include as a task into existing package.json
`bash`
"scripts": {
"build:components": "node node_modules/@cqfactory/component-generator/builder.js"
You can also add a specific folder. For default it would search the whole project.
`bash`
"scripts": {
"build:components": "node node_modules/@cqfactory/component-generator/builder.js --src ./myspecificfolder"
Example integration into a Webpack project.
`json`
{
"name": "npm-component-generator",
"version": "0.1.0",
"description": "npm project for the component-generator",
"main": "index.js",
"dependencies": {
"@types/react": "^16.8.17",
"@types/react-dom": "^16.8.4",
"jspath": "^0.4.0",
"npm": "^6.9.0",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-fast-compare": "^2.0.4",
"react-router": "^5.0.0",
"react-router-dom": "^5.0.0",
"react-scripts": "^2.1.8",
"react-scripts-ts": "3.1.0",
"react-styleguidist": "^9.0.6"
},
"devDependencies": {
"@cqfactory/component-generator": "^0.3.1",
"aem-clientlib-generator": "^1.4.1",
"ajv": "^6.10.0",
"awesome-typescript-loader": "^5.2.1",
"babel-core": "^6.26.3",
"babel-loader": "^8.0.5",
"babel-preset-env": "^1.7.0",
"babel-preset-react": "^6.24.1",
"clone": "^2.1.2",
"css-loader": "^2.1.1",
"html-webpack-plugin": "^3.2.0",
"node-sass": "^4.12.0",
"sass-loader": "^7.1.0",
"source-map-loader": "^0.2.4",
"style-loader": "^0.23.1",
"typescript": "^3.4.5",
"webpack": "^4.31.0",
"webpack-cli": "^3.3.1"
},
"scripts": {
"build:components": "node node_modules/@cqfactory/component-generator/builder.js",
"clean:dep": "rm -rf node_modules/",
"clean:cqfactory": "rm -rf node_modules/@cqfactory",
"webpack:prod": "webpack --mode production && clientlib --verbose"
},
"eslintConfig": {
"extends": "react-app"
},
"author": "CQ-Factory GmbH",
"license": "GNU General Public License v3.0"
}
Imagine your pure ReactJS developer has created an amazing stand-alone React component like this teaser.
!CQ-Factory Banner
All the developer has done was creating an index.tsx where the input fields for the CQ-dialog where pre-defined and the component initialized:
`typescript
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import CqfTeaserReactComponent from './CqfTeaserReactComponent';
import './index.css';
export interface CqfTeaserReactComponentProps {
cqComponent?: any;
}
// Pre-defined properties for the CQ-dialog.
export interface DialogProperties {
teaserHeadline?: string;
teaserText?: string;
imageFileReference?: string;
}
// Using some test values.
const teaserProps: DialogProperties = {
teaserHeadline: 'This is the teaser headline',
teaserText: "Sample text",
imageFileReference: "CQF-Mountains1.jpg"
};
ReactDOM.render(
document.getElementById('cqf-teaser-react-app') as HTMLElement
);
`
This is the React component:
`typescript
import * as React from 'react';
import './CqfTeaserReactComponent.scss';
import {CqfTeaserReactComponentProps} from "./index";
// Use an external library which provides atomic elements.
import Headline from '@cqfactory/react-elements/components/Headline/Headline';
export default class CqfTeaserReactComponent extends React.Component
get teaserHeadline() {
let teaserHeadline = this.props.cqComponent.teaserHeadline;
return teaserHeadline ? teaserHeadline :
Teaser Headline is empty, please configure it in the CQ-dialog!
; get teaserTextContent() {
let teaserTextContent = this.props.cqComponent.teaserText;
return teaserTextContent ?
Teaser text is empty, please configure it in the CQ-dialog!
; render() {
return (
{this.teaserTextContent}
`
Now, this great React application should be included into an AEM project. The pure React developer does not know anything about AEM and this is ok!
So this developer only has to create a component-configuration file, based on TypeScript and the CQ-Factory Component-Generator classes, to ensure correct value usage.
This example component-configuration file would now create a React enabled AEM component with a Touch UI dialog and the needed TSX files.
File: /aemproject/npm/src/cqfactory-teaser-react.component.ts
`typescript
import {
AEMTouchUIDialog,
TouchUIDialogTab,
TouchUIField,
ComponentGenerator
} from '@cqfactory/component-generator';
import { ReactConfiguration } from '@cqfactory/component-generator/uiconfigs/reactConfiguration';
/ Definition of dialog tabs. /
const tabs: TouchUIDialogTab[] = [
/ Headline und Text tab. /
{
title: 'Headline und Text',
fields: [
{
label: 'Headline',
type: TouchUIField.Text,
databaseName: 'teaserHeadline',
description: 'Teaser Headline.'
},
{
label: 'Text',
type: TouchUIField.RichText,
databaseName: 'teaserText',
description: 'Teaser Text.'
}
]
},
/ Image tab. /
{
title: 'Image Tab',
fields: [
{
label: 'Teaser Image',
type: TouchUIField.Imagefield,
databaseName: 'image',
description: 'Teaser Image.',
isRequired: false
},
{
label: 'Teaser Image Alternative Text',
type: TouchUIField.Text,
databaseName: 'image/alt',
description: 'Textual alternative of the meaning or function of the image, for visually impaired readers.'
},
{
label: 'Image is decorative',
type: TouchUIField.Checkbox,
databaseName: 'image/isDecorative',
uncheckedValue: 'false',
value: '{Boolean}true',
checked:
'${not empty cqDesign.isDecorative ? cqDesign.isDecorative : false}',
description: 'Check if the image should be ignored by assistive technology and therefore does not require an alternative text. This applies to decorative images only.'
},
{
label: 'Mein Hidden Field',
type: TouchUIField.HiddenField,
databaseName: 'image/sling:resourceType',
value: 'componentgenerator/components/content/image'
}
]
}
];
export const exampleTouchUIDialog: AEMTouchUIDialog = {
buildComponent: true / Create the component from scratch /,
buildConfigForAem: true / Create the file .content.xml /,
overwriteAllFilesOnBuild: true / Works only when buildComponent: true, later deactivate it to avoid recreation. /,
componentName: 'CQ-Factory Teaser React',
componentGroup: 'componentgenerator',
componentDescription: 'CQ-Factory Teaser React Component',
noDecoration: false,
isContainer: false,
componentPath: './../ui.apps/src/main/content/jcr_root/apps/componentgenerator/components/content/cqfteaserreact',
title: 'CQ-Factory Teaser React Component',
tag: 'div',
css: 'cqf-teaser-react',
tabs,
analytics: {
values: ['value1', 'value2'],
events: ['event1', 'event2']
},
fullyQualifiedClassName:
'de.cqfactory.componentgenerator.core.models.TeaserReactComponentModel', / Only needed for manipulating JCR values /
resourceSuperType: ''
};
let reactConfiguration = new ReactConfiguration(
'cqf-teaser-react-app',
'CqfTeaserReactComponent',
'./src/components/cqfteaserreactcomponent'
);
/**
* Special configuration for React only.
*/
new ComponentGenerator(
exampleTouchUIDialog,
reactConfiguration
).writeFilesToAEM();
`
Running node node_modules/@cqfactory/component-generator/builder.js would search for all files containing \.component.ts* in their filename:
!TypeScript REPL
When this task ended successfully, there will be a complete new folder /aemproject/ui.apps/src/main/content/jcr_root/apps/componentgenerator/components/content/cqfteaserreact created with all files for this AEM-component.
No changes are needed. Just leave it as it is (but you can!):
!TypeScript REPL
There is also a newly created React app folder /aemproject/npm/src/components/cqfteaserreactcomponent, containing the React component index.tsx with our provided magic AEM React-Loading mechanism:
!TypeScript REPL
The Component-Generator also created a basic react component (CqfTeaserReactComponent.tsx), which gives the developer access to all CQ-dialog properties via this.props.cqComponent:
!TypeScript REPL
After deploying this component to an AEM instance, it will work with its basic functionality:
!TypeScript REPL
Opening the CQ-dialog would show the JCR properties fields, which were previously defined in the file /aemproject/npm/src/cqfactory-teaser-react.component:
!TypeScript REPL
After saving the dialog, in this view it shows only what the JCR is containing - inside the React component:
!TypeScript REPL
This magic is done via our React-Loader which let React access the JCR properties (seen above in file CqfTeaserReactComponent.tsx):
!TypeScript REPL
Now that we have the basic component structure with a dialog, it is time to move the real React code inside the AEM project. All the developer has to do, is to copy & paste the
content of the React component TSX-file into the created file CqfTeaserReactComponent.tsx:
!TypeScript REPL
Build with WebPack and deploy to AEM to see the result:
!TypeScript REPL
`typescript``
buildComponent: false
Visit: www.cq-factory.de
Email: info@cq-factory.de