Integrate the SPA developer-workflow
Understand how the source code for a Single Page Application (SPA) written in React can be integrated with an Adobe Experience Manager (AEM) Project. Learn to use modern front-end tools, like a webpack dev server, to rapidly develop the SPA against the AEM JSON model API.
Objective
- Understand how the SPA project is integrated with AEM with client-side libraries.
- Learn how to use a webpack development server for dedicated front-end development.
- Explore the use of a proxy and static mock file for developing against the AEM JSON model API.
What you will build
In this chapter you will make several small changes to the SPA in order to understand how it is integrated with AEM.
This chapter will add a simple Header
component to the SPA. In the process of building out this static Header
component several approaches to AEM SPA development are used.
The SPA is extended to add a static Header
component
Prerequisites
Review the required tooling and instructions for setting up a local development environment. This chapter is a continuation of the Create Project chapter, however to follow along all you need is a working SPA-enabled AEM project.
Integration approach integration-approach
Two modules were created as part of the AEM project: ui.apps
and ui.frontend
.
The ui.frontend
module is a webpack project that contains all of the SPA source code. A majority of the SPA development and testing is done in the webpack project. When a production build is triggered, the SPA is built and compiled using webpack. The compiled artifacts (CSS and Javascript) are copied into the ui.apps
module which is then deployed to the AEM runtime.
A high-level depiction of the SPA integration.
Additional information about the Front-end build can be found here.
Inspect the SPA integration inspect-spa-integration
Next, inspect the ui.frontend
module to understand the SPA that has been auto-generated by the AEM Project archetype.
-
In the IDE of your choice open your AEM Project. This tutorial will use the Visual Studio Code IDE.
-
Expand and inspect the
ui.frontend
folder. Open the fileui.frontend/package.json
-
Under the
dependencies
you should see several related toreact
includingreact-scripts
The
ui.frontend
is a React application based on the Create React App or CRA for short. Thereact-scripts
version indicates which version of CRA is used. -
There are also several dependencies prefixed with
@adobe
:code language-json "@adobe/aem-react-editable-components": "~1.1.2", "@adobe/aem-spa-component-mapping": "~1.1.0", "@adobe/aem-spa-page-model-manager": "~1.3.3", "@adobe/aem-core-components-react-base": "1.1.8", "@adobe/aem-core-components-react-spa": "1.1.7",
The above modules make up the AEM SPA Editor JS SDK and provide the functionality to make it possible to map SPA Components to AEM Components.
Also included are AEM WCM Components - React Core implementation and AEM WCM Components - Spa editor - React Core implementation. These are a set of re-usable UI components that map to out of the box AEM components. These are designed to be used as is and styled to meet your project’s needs.
-
In the
package.json
file there are severalscripts
defined:code language-json "scripts": { "start": "react-scripts start", "build": "react-scripts build && clientlib", "test": "react-scripts test", "eject": "react-scripts eject", }
These are standard build scripts made available by the Create React App.
The only difference is the addition of
&& clientlib
to thebuild
script. This extra instruction is responsible for copying the compiled SPA into theui.apps
module as a client-side library during a build.The npm module aem-clientlib-generator is used to facilitate this.
-
Inspect the file
ui.frontend/clientlib.config.js
. This configuration file is used by aem-clientlib-generator to determine how to generate the client library. -
Inspect the file
ui.frontend/pom.xml
. This file transforms theui.frontend
folder into a Maven module. Thepom.xml
file has been updated to use the frontend-maven-plugin to test and build the SPA during a Maven build. -
Inspect the file
index.js
atui.frontend/src/index.js
:code language-js //ui.frontend/src/index.js ... document.addEventListener('DOMContentLoaded', () => { ModelManager.initialize().then(pageModel => { const history = createBrowserHistory(); render( <Router history={history}> <App history={history} cqChildren={pageModel[Constants.CHILDREN_PROP]} cqItems={pageModel[Constants.ITEMS_PROP]} cqItemsOrder={pageModel[Constants.ITEMS_ORDER_PROP]} cqPath={pageModel[Constants.PATH_PROP]} locationPathname={window.location.pathname} /> </Router>, document.getElementById('spa-root') ); }); });
index.js
is the entrypoint of the SPA.ModelManager
is provided by the AEM SPA Editor JS SDK. It is responsible for calling and injecting thepageModel
(the JSON content) into the application. -
Inspect the file
import-components.js
atui.frontend/src/components/import-components.js
. This file imports the out of the box React Core Components and makes them available to the project. We will inspect the mapping of AEM content to SPA components in the next chapter.
Add a static SPA component static-spa-component
Next, add a new component to the SPA and deploy the changes to a local AEM instance. This is a simple change, just to illustrate how the SPA is updated.
-
In the
ui.frontend
module, beneathui.frontend/src/components
create a new folder namedHeader
. -
Create a file named
Header.js
beneath theHeader
folder. -
Populate
Header.js
with the following:code language-js //Header.js import React, {Component} from 'react'; export default class Header extends Component { render() { return ( <header className="Header"> <div className="Header-container"> <h1>WKND</h1> </div> </header> ); } }
Above is a standard React component that will output a static text string.
-
Open the file
ui.frontend/src/App.js
. This is the application entry-point. -
Make the following updates to
App.js
to include the staticHeader
:code language-diff import { Page, withModel } from '@adobe/aem-react-editable-components'; import React from 'react'; + import Header from './components/Header/Header'; // This component is the application entry point class App extends Page { render() { return ( <div> + <Header /> {this.childComponents} {this.childPages} </div>
-
Open a new terminal and navigate into the
ui.frontend
folder and run thenpm run build
command:code language-shell $ cd aem-guides-wknd-spa $ cd ui.frontend $ npm run build ... Compiled successfully. File sizes after gzip: 118.95 KB (-33 B) build/static/js/2.489f399a.chunk.js 1.11 KB (+48 B) build/static/js/main.6cfa5095.chunk.js 806 B build/static/js/runtime-main.42b998df.js 451 B build/static/css/main.e57bbe8a.chunk.css
-
Navigate to the
ui.apps
folder. Beneathui.apps/src/main/content/jcr_root/apps/wknd-spa-react/clientlibs/clientlib-react
you should see the compiled SPA files have been copied from theui.frontend/build
folder. -
Return to the terminal and navigate into the
ui.apps
folder. Execute the following Maven command:code language-shell $ cd ../ui.apps $ mvn clean install -PautoInstallPackage ... [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 9.629 s [INFO] Finished at: 2020-05-04T17:48:07-07:00 [INFO] ------------------------------------------------------------------------
This will deploy the
ui.apps
package to a local running instance of AEM. -
Open a browser tab and navigate to http://localhost:4502/editor.html/content/wknd-spa-react/us/en/home.html. You should now see the contents of the
Header
component being displayed in the SPA.The above steps are executed automatically when triggering a Maven build from the root of the project (i.e
mvn clean install -PautoInstallSinglePackage
). You should now understand the basics of the integration between the SPA and AEM client-side libraries. Notice that you can still edit and addText
components in AEM beneath the staticHeader
component.
Webpack Dev Server - Proxy the JSON API proxy-json
As seen in the previous exercises, performing a build and syncing the client library to a local instance of AEM takes a few minutes. This is acceptable for final testing, but is not ideal for the majority of the SPA development.
A webpack-dev-server can be used to rapidly develop the SPA. The SPA is driven by a JSON model generated by AEM. In this exercise the JSON content from a running instance of AEM is proxied into the development server.
-
Return to the IDE and open the file
ui.frontend/package.json
.Look for a line like the following:
code language-json "proxy": "http://localhost:4502",
The Create React App provides an easy mechanism to proxy API requests. All unknown requests are proxied through
localhost:4502
, the local AEM quickstart. -
Open a terminal window and navigate to the
ui.frontend
folder. Run the commandnpm start
:code language-shell $ cd ui.frontend $ npm start ... Compiled successfully! You can now view wknd-spa-react in the browser. Local: http://localhost:3000 On Your Network: http://192.168.86.136:3000 Note that the development build is not optimized. To create a production build, use npm run build.
-
Open a new browser tab (if not already opened) and navigate to http://localhost:3000/content/wknd-spa-react/us/en/home.html.
You should see the same content as in AEM, but without any of the authoring capabilities enabled.
note note NOTE Due to the security requirements of AEM, you will need to be logged into the local AEM instance (http://localhost:4502) in the same browser but in a different tab. -
Return to the IDE and create a file named
Header.css
in thesrc/components/Header
folder. -
Populate the
Header.css
with the following:code language-css .Header { background-color: #FFEA00; width: 100%; position: fixed; top: 0; left: 0; z-index: 99; box-shadow: 0px 0px 10px 0px rgba(0, 0, 0, 0.24); } .Header-container { display: flex; max-width: 1024px; margin: 0 auto; padding: 12px; } .Header-container h1 { letter-spacing: 0; font-size: 48px; }
-
Re-open
Header.js
and add the following line to referenceHeader.css
:code language-diff //Header.js import React, {Component} from 'react'; + require('./Header.css');
Save the changes.
-
Navigate to http://localhost:3000/content/wknd-spa-react/us/en/home.html to see the style changes automatically reflected.
-
Open the file
Page.css
atui.frontend/src/components/Page
. Make the following changes to fix the padding:code language-css .page { max-width: 1024px; margin: 0 auto; padding: 12px; padding-top: 50px; }
-
Return to the browser at http://localhost:3000/content/wknd-spa-react/us/en/home.html. You should immediately see the changes to the app reflected.
You can continue to make content updates in AEM and see them reflected in webpack-dev-server, since we are proxying the content.
-
Stop the webpack dev server with
ctrl+c
in the terminal.
Deploy SPA updates to AEM
The changes made to the Header
are currently only visible through the webpack-dev-server. Deploy the updated SPA to AEM to see the changes.
-
Navigate to the root of the project (
aem-guides-wknd-spa
) and deploy the project to AEM using Maven:code language-shell $ cd .. $ mvn clean install -PautoInstallSinglePackage
-
Navigate to http://localhost:4502/editor.html/content/wknd-spa-react/us/en/home.html. You should see the updated
Header
and styles applied.Now that the updated SPA is in AEM, authoring can continue.
Congratulations! congratulations
Congratulations, you have updated the SPA and explored the integration with AEM! You now know how to develop the SPA against the AEM JSON model API using a webpack-dev-server.
Next Steps next-steps
Map SPA components to AEM components - Learn how to map React components to Adobe Experience Manager (AEM) components with the AEM SPA Editor JS SDK. Component mapping enables users to make dynamic updates to SPA components within the AEM SPA Editor, similar to traditional AEM authoring.
(Bonus) Webpack Dev Server - Mock JSON API mock-json
Another approach to rapid development is to use a static JSON file to act as the JSON model. By “mocking” the JSON, we remove the dependency on a local AEM instance. It also allows a front-end developer to update the JSON model in order to test functionality and drive changes to the JSON API that would then be later implemented by a back-end developer.
The initial set up of the mock JSON does require a local AEM instance.
-
Return to the IDE and navigate to
ui.frontend/public
and add a new folder namedmock-content
. -
Create a new file named
mock.model.json
beneathui.frontend/public/mock-content
. -
In the browser navigate to http://localhost:4502/content/wknd-spa-react/us/en.model.json.
This is the JSON exported by AEM that is driving the application. Copy the JSON output.
-
Paste the JSON output from the previous step in the file
mock.model.json
. -
Open the file
index.html
atui.frontend/public/index.html
. Update the metadata property for the AEM page model to point to a variable%REACT_APP_PAGE_MODEL_PATH%
:code language-html <!-- AEM page model --> <meta property="cq:pagemodel_root_url" content="%REACT_APP_PAGE_MODEL_PATH%" />
Using a variable for the value of the
cq:pagemodel_root_url
will make it easier to toggle between the proxy and mock json model. -
Open the file
ui.frontend/.env.development
and make the following updates to comment out the previous value forREACT_APP_PAGE_MODEL_PATH
andREACT_APP_API_HOST
:code language-diff + PUBLIC_URL=/ - PUBLIC_URL=/etc.clientlibs/wknd-spa-react/clientlibs/clientlib-react/resources - REACT_APP_PAGE_MODEL_PATH=/content/wknd-spa-react/us/en.model.json + REACT_APP_PAGE_MODEL_PATH=/mock-content/mock.model.json - REACT_APP_API_HOST=http://localhost:4502 + #REACT_APP_API_HOST=http://localhost:4502 REACT_APP_ROOT=/content/wknd-spa-react/us/en/home.html
-
If currently running, stop the webpack-dev-server. Start the webpack-dev-server from the terminal:
code language-shell $ cd ui.frontend $ npm start
Navigate to http://localhost:3000/content/wknd-spa-react/us/en/home.html and you should see the SPA with the same content used in the proxy json.
-
Make a small change to the
mock.model.json
file created earlier. You should see the updated content immediately reflected in the webpack-dev-server.
Being able to manipulate the JSON model and see the effects on a live SPA can help a developer understand the JSON model API. It also allows both front-end and back-end development happen in parallel.
You can now toggle where to consume the JSON content by toggling the entries in the env.development
file:
# JSON API via proxy to AEM
#REACT_APP_PAGE_MODEL_PATH=/content/wknd-spa-react/us/en.model.json
#REACT_APP_API_HOST=http://localhost:4502
# JSON API via static mock file
REACT_APP_PAGE_MODEL_PATH=/mock-content/mock.model.json