Building Single Page Application (SPA) with Adobe Experience Manager (AEM)

Adobe Experience Manager (AEM) is one of the most popular enterprise web content management systems (WCMS). Traditionally, it’s made for building content heavy websites. Credits to its beautiful java stack, enterprises push its sphere building dynamic web applications on it. Modern trend of interactive internet web applications and the hybrid (cross-platform) mobile apps asks for the feasibility of doing Single Page Application (SPA) with AEM. Million dollar challenge swiftly comes to mind is how authoring can be done if the application is SPA. Yes, there are challenges but building SPA is possible with AEM. Success depends on how AEM templates and components are coded to match the browser side SPA (JavaScript) framework’s needs.

Recently, I’ve been involved in building a SPA with AEM using AngularJS(1.x). I intent to make this article address the typical architectural concerns when integrating AEM with SPA frameworks.

Before you proceed – Hang on! – Read about SPA and round up on SPA frameworks.

Authoring

Why think again. Serve the authors full page (no SPA) in author instance. Authoring is AEM’s strength. We cannot afford to make any behavioural change there. Leverage the WCMMODE information to compose the pages into SPA experience only in publish instance. Achieve this making conditional inclusion in templates.

Templates

In the eyes of SPA (JS) frameworks, there are 2 types of templates –

  • Layout template – One index page that gets loaded first and usually contains the invariable content such as header and footer with the placeholder for the partial template.
  • Partial template – Rest all pages are partial, usually contains body content, the variable portion that gets sandwiched into the layout templates’s placeholder.

Don’t get confused. With AEM, we’ve AEM templates. We would’ve to match AEM templates to the needs of SPA framework templates. Achieve this leveraging sling selectors in templates.

/content/example/en.html - Provides the layout template (full html page with placeholder for the variable content) for SPA framework.
/content/example/en.partial.html - Provides the partial template(just the body content) for SPA framework.
<!--author-->
<sly data-sly-test="${!wcmmode.disabled}"><sly data-sly-include="partial.html" data-sly-unwrap/></sly>
<!--publish-->
<sly data-sly-test="${wcmmode.disabled}"><div ng-view></div></sly>

Code page component backing the template to support the above. For SPA framework’s following component tree model, the layout template is the root component which app gets bootstrapped with .

Components

In AEM, component represents a visually cohesive portion of page. Find its counterpart in SPA framework. Depending on the SPA framework being chosen , it could be –

  • a view, meaning a html markup backed by view model or controller. Example – AngularJS, Vue, etc. This translates to AEM component having a htl (sightly) file and a clientlib with controller (JS).
  • a component in SPA framework which is essentially an enhanced HTML vocabulary. Example – React, Angular, Vue, etc. This translates to AEM component having a clientlib with component file (js) and often with a htl expressed with SPA specific and other component tags.

URL and Routes

SPA’s usually follow hashbang url style.


http://www.example.com/#content/example/en

Often HTML5 history APIs could be leveraged to make it look like usual URL. The hash section in the url is called route. And yes, the SPA framework is in charge of routing. Hence, there has to be a match making between the site content hierarchy and the routes. An example route using AngularJS is shown below.

$routeProvider.when('/content/example/:name*', {
            templateUrl: function (attr) {
                var url = '/content/example/' + attr + '.partial.html';
                }
                return url;
            }
        }).when('/home', {
            //For home page
            templateUrl: '/content/example/en.partials.html'
        }).when('/',{
            redirectTo: '/home'
        }).otherwise({
            //If not matching with above criteria, redirect to 404 page.
            redirectTo: '/content/example/en/errors/404'
        });

Links

Every time an author choose a link, we’ve to make it to DOM as-is in author mode and translate to hashbang style in publish mode. This asks for changes in RTE link plugin too.

              if (link.startsWith('/content')) {
                if(link.indexOf('/content/dam') === -1){
                    if(wcmMode === 'wcm-enabled'){
                        // Author mode - Append .html if absent.
                        if(link.indexOf('.html') === -1){
                            url = link + '.html';
                        }
                    }else {
                        // Publish mode - Hashbang style url.
                        url =  '#' + link;
                    }
                }

Access control

If there are protected (CUG) pages, you have to be concerned about this. SPA framework’s cannot deal the 302 redirects usually done by sever when accessing protected page in an unauthenticated session as the browser natively redirect the page to the given url. So, we’ve to make SPA framework routing play a role here too. If certain route is protected, check the authentication state of the user and show the login page if not authenticated.

Caching

It boils down to the design. Better way handle this is to make all the htmls 100% cacheable and any dynamic information required to be exposed as REST style APIs. This API could come from AEM or other 3rd party systems, even CORS too.

Application state

Since the SPA framework being used is in charge of the application, it has to maintain the state. Application-wide state could be anything such as locale information, logged-in state, etc. State of individual business logic are usually encapsulated into services. It often gets easier as your code has to be concerned about just one user.

Browser side integrations

Usual style with SPA is to achieve integrations at the browser side, mostly with data APIs. Rich set of libraries with SPAs make integrations easier. Often sessions too are managed with data APIs.

Code and skill set

Typically AEM projects for SPA involves good amount of JavaScript code and hence onboard full stack developers. JSLint becomes inevitable. We cannot let unit JS unit testing go too.

Building Single Page Application (SPA) with Adobe Experience Manager (AEM)