Skip to main content

Adobe

Updating the SPA Editor to Support Vue 3

Updating The Spa Editor To Support Vue 3

I enjoy building websites using the Adobe Experience Manager (AEM) content management system. I also enjoy using JavaScript frameworks like Vue, React, and Angular to create rich web applications. Occasionally I get to do both. AEM provides a SPA Editor to facilitate building components for single-page applications. The SPA Editor enables front-end developers to produce components in React or Angular. These components are used by the author to create rich, native-like experiences.

Some time ago, I was engaged with a customer who needed a single-page application written in Vue and not React or Angular. Sadly, the AEM SPA Editor does not support the Vue JavaScript framework. The customer employed Vue developers and wasn’t willing to migrate their tech stack to another framework. I was tasked to create a version of the SPA Editor that would support Vue.

The Challenge: Editor Functionality and Performance

Because this work would be custom and not have the confidence of Adobe support, I wanted to be sure I could deliver a SPA Editor with all the functionality and performance of the editors written for React and Angular. I visited my favorite search engine with the hopes of finding code repositories from developers who have accomplished this very thing. No such luck. I stumbled upon a few results that looked promising, but upon closer inspection didn’t quite match my use case. It was time to dig into the existing SPA code and see if I could create something similar.

Stanley: Our Vue SPA Editor Demo

At the end of this blog, you will find links to download an AEM project created to demonstrate a working Vue SPA Editor. When deployed, this project presents a cute site named “Stanley” containing a sampling of Core components.

Vue Spa Editor Stanley

Reverse Engineering the Existing SPA Editor

The front-end portion of the SPA Editor is composed of five NPM packages.

  1. AEM Component Mapping (@adobe/aem-SPA-component-mapping)
    A storage library for front-end components. Provides a way for the Single Page Application to map front-end components to Adobe Experience Manager resource types (AEM Components).
  2. Page Model Manager (@adobe/aem-SPA-page-model-manager)
    An interpreter between Adobe Experience Manager Editor and the Adobe Experience Manager Single Page Application (SPA) Editor.
  3. AEM SPA Editable Components (@adobe/aem-react-editable-components or @adobe/aem-angular-editable-components)
    This project provides the React/Angular components and integration layer to get you started with the Adobe Experience Manager SPA Editor.
  4. AEM WCM Components – SPA (@adobe/aem-core-components-react-SPA or @adobe/aem-core-components-angular-SPA)
    This module provides a React/Angular implementation for the containers in the AEM core components.
  5. AEM WCM Components – Base (@adobe/aem-core-components-react-base or @adobe/aem-core-components-angular-base)
    This module provides a React/Angular implementation for the AEM core components.

Of these five packages, three were specific to React or Angular. These are the three packages I would need to reverse engineer and replicate in Vue. I had a fair amount of React experience and decided to use the React packages as my starting point. The React code is based on the older Class Components and not the newer Hooks paradigm. This made digging through the code difficult. I was far more comfortable using the Options API in Vue, but I decided to challenge myself and create the components using the Composition API.

The AEM WCM Components Base package and the AEM WCM Components SPA package contained React components corresponding to AEM core components. I would need to migrate these packages in time, but the core functionality was to be found in the AEM SPA Editable Components package.

Note: As of late 2023, the 47th release of the AEM Project Archetype includes version 1.1.6 of @adobe/aem-react-editable-components, version 1.1.8 of @adobe/aem-core-components-react-base, and version 1.2.0 of @adobe/aem-core-components-react-SPA. I have based my Vue code upon these versions.

AEM SPA Editable Components Package Migration

The methods responsible for mapping AEM component paths to React components can be found in the ComponentMapping.tsx file. The file contains little React, and its migration was easy. Here are the five utility methods used.

MapTo

The primary method in this file, MapTo, takes in as its parameters: a path (or paths) to an AEM component, a corresponding React component, and an Edit configuration containing the readable name of the component and a method that determines if a component qualifies as “empty”.  The MapTo method returns the provided component wrapped with three other React components. These wrapping components would need to be migrated to Vue. I started with the “ContextProvider” component.

ContextProvider

ContextProvider provides a global context for all containing components. If the ContextProvider component is a child of another ContextProvider component, the context comes from the parent ContextProvider. The global context contains a Boolean value indicating whether the application is being accessed in “edit” mode. It also contains an instance of the JavaScript class ComponentMapping. This class manages the mapping between AEM component resource types and the corresponding JavaScript component class.

ModelProvider

ModelProvider adds an AEM listener to the provided component path. When the component is updated via authoring, the new values are passed to the child components. This component was tricky to migrate. Updates to properties can come from two paths. Authoring of the component directly or authoring of a parent component. I have added a reactive property named updatedModelProperties which is populated with the values from authoring or populated with fall-through attributes from the parent.

EditableProvider

EditableProvider wraps the component with a div containing HTML attributes that AEM requires to draw the editable overlays. The wrapping div is only added when in edit mode.

With these three components migrated to Vue, I was able to complete the ComponentMapping code and migrate the test cases to ensure consistent behavior with the code from the React package. With this done, I turned my attention to the foundation components: Container, Page, ResponsiveGrid, and AllowedComponentsContainer.

Foundation Components

The AEM Editable Components package is written in an older flavor of React making use of Class Components. This made it easy for the original authors to extend the existing Container component and override the functionality. The Container component is the base for all the other foundation components.

React now discourages using Class Components and recommends using Functional Components. Vue has moved away from that pattern as well and provides other ways of reusing functionality across components. I chose to make the Container component more extensible.

I created composable functions that encapsulate reusable logic and added them to a utility file. When migrating the AllowedComponentsContainer, I determined that much of the Container component would be reused. It wasn’t worth encapsulating the remaining Container logic. I decided to wrap the Container component and pass any new functionality as props to the component.

I was able to replicate the behavior of the React components and keep my codebase clean and readable. After ensuring the test cases passed for the Foundation components, I was able to conclude my work on the AEM SPA Editable Components package.

AEM WCM Components – SPA Package Migration

The next package to be migrated would be the AEM WCM Components – SPA package. The AEM WCM Components – SPA package contains container components like Container, Accordion, Carousel, Tabs, and Experience Fragments.

These components allow you to nest other components inside of them. Since most of these components extended the foundation Container component, I was able to use the same tricks (and utility methods) I used in the AEM Editable Components package to migrate these components.

The Accordion, Carousel, and Tabs components do not display all nested components at once. The SPAUtils file contains methods to subscribe to the AEM Message Channel and listen for “navigation” events from authoring and display the correct nested components. I upgraded the React rendering code to include the accessibility features from the default AEM components.

The AEM WCM Components – SPA package also contains the Content Fragment component and the ContentFragmentRegistry.ts file. The primary method in this file, MapToContentFragmentModel, takes in as its parameters a path to a content fragment model and a corresponding Vue component. The MapToContentFragmentModel returns the provided component rendered using the properties from the Content Fragment model. If a Content Fragment model is not mapped using the MapToContentFragmentModel method, the component “DefaultContentFragment” will render the component. The method MapToContentFragmentModel is potent. It allows you to create one-off components using a Content Fragment model and a Vue component to display the data.

AEM WCM Components – Base Package Migration

The last package to be migrated is the AEM WCM Components – Base package. It contains the following components: Breadcrumb, Button, Download, Image, Language Navigation, List, Navigation, Separator, Teaser, Text, and Title. It also contains the component CoreLink. This component is used to generate anchor tags for external links or routed links (using Vue Router) for links internal to the application. These are generally simple components. The migration was straightforward.

With the three packages specific to a JavaScript framework migrated to Vue, I was ready to generate a project using my new framework. I ran the following command to generate a new project:

New Project Command Vue Migration

UI Frontend Changes

The next thing I did was to copy the pom.xml file out of the ui.frontend maven project and delete the ui.frontend directory. I then created a new ui.frontend directory and re added the pom.xml file. I edited the pom.xml file and removed the following from the plugins section:

Ui Frontend Changes Plugin

 I also updated my Node and NPM version. The react ui.frontend maven project would use react-scripts to build the project. I decided to use Webpack instead. I initialized a package.json file and added my dev dependencies. I would use:

  • Webpack and Babel to bundle my CSS and JavaScript
  • TypeScript to improve my developer experience and increase my productivity
  • Jest to test my code
  • ESLint and Prettier to lint my TypeScript
  • StyleLint to lint my CSS
  • PostCSS to transform my CSS
  • The AEM Client Library Generator to create a Client Library from my bundled CSS and JavaScript

My dependencies consisted of Vue, Vue Router, my three SPA Editor packages I migrated from React, and the two Adobe packages not dependent upon a frontend framework. I rewrote the client lib config in TypeScript and added a src directory.

Mapping Components

Within the src directory, I added the all-important “import-components” file. The file contains a mapping for every component within ui.apps that has a matching Vue component. For example, mapping for a button was added because the package AEM WCM Components – Base contains a button component. There is no mapping for the progress bar component because there are no corresponding Vue components in any of the packages I migrated from React.

The mapping of the Page AEM Component is not to the Page Vue component. It is to the Composite Route Vue Component. This component adds a Vue Route for each page encountered. The very last command to execute in the App Vue component is a call to the Vue Router to load the route corresponding to the current page path. If a route is found for the current page path, Vue Router will load the Page component for that path and all its child components.

Vue-Specific Changes

You can see the other Vue-specific changes I made here: https://github.com/PRFTAdobe/stanley/tree/main/ui.frontend

The Page and App policies also had to be updated to point to the new Vue client library instead of React. In the project I created above, the file in question can be found here: stanley/ui.content/src/main/content/jcr_root/conf/stanley/settings/wcm/policies/.content.xml. Any reference to “stanley.react” needed to be changed to “stanley.vue”.

The generated “customheaderlibs.html” and “customfooterlibs.html” needed to be updated and references to “clientlib-react” were changed to “clientlib-vue”.

I’ve also added some application-specific components and a custom form with validation. Feel free to check out the repo and try it out: https://github.com/PRFTAdobe/stanley.

I have created a Components page to demo the Core Vue components.

Core Vue Components

You can find the source code for my migrated packages here:

https://github.com/PRFTAdobe/aem-vue-3-editable-components

https://github.com/PRFTAdobe/aem-vue-3-core-wcm-components-SPA

https://github.com/PRFTAdobe/aem-vue-3-core-wcm-components-base

 Fixing Layout Mode

Currently, Layout Mode is not working. I have not had a chance to investigate the issue. This repo is open source. If someone has a fix, feel free to create a PR and I will look.

Get More Out of Adobe Experience Manager

Check out more of our AEM-related blogs for more help making informed decisions for your own implementations.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Shannon Sumner, Senior Technical Consultant

More from this Author

Categories
Follow Us