Abstract
Goal
Adobe Experience Manager 2021.9.5899.20210929T093525Z-210800 (Sep 29, 2021)
Create a AEM React SPA supporting Vanity Urls. Otb Vanity Urls are added in Page Properties, saved as jcr:content/sling:vanityPath
Solution
1) Add the function getVanityUrls() to get the list of vanity urls using querybuilder before loading the app. VanityURLModelClient extending ModelClient was added as a workaround to not request model.json using the vanity url (eg. this is invalid request http://localhost:4502/eaem-home.model.json). Code in eaem-spa-vanity-urls\ui.frontend\src\index.js facilitates loading of vanity urls into the app...
import 'react-app-polyfill/stable';
import 'react-app-polyfill/ie9';
import 'custom-event-polyfill';
import { Constants, ModelManager } from '@adobe/aem-spa-page-model-manager';
import { createBrowserHistory } from 'history';
import React from 'react';
import { render } from 'react-dom';
import { Router } from 'react-router-dom';
import {ModelClient} from '@adobe/aem-spa-page-model-manager';
import App from './App';
import LocalDevModelClient from './LocalDevModelClient';
import './components/import-components';
import './index.css';
const getVanityUrls = async () => {
const QUERY = "/bin/querybuilder.json?path=/content/eaem-spa-vanity-urls&property=jcr:content/sling:vanityPath&property.operation=exists" +
"&p.hits=selective&p.properties=jcr:content/sling:vanityPath%20jcr:path&type=cq:Page";
const response = process.env.REACT_APP_PROXY_ENABLED ? await fetch(QUERY, {
credentials: 'same-origin',
headers: {
'Authorization': process.env.REACT_APP_AEM_AUTHORIZATION_HEADER
}
}): await fetch(QUERY);
const data = (await response.json()).hits.reduce((current, next) => {
return { ...current, ...{ [next["jcr:path"]]: next["jcr:content"]?.["sling:vanityPath"] } }
}, {});
return data;
};
class VanityURLModelClient extends ModelClient{
fetch(modelPath) {
//if the path does not start with /content (page editing) or /conf (template editing) return empty model
if (modelPath && !/^\/content|^\/conf/.test(modelPath)) {
return Promise.resolve({});
}else{
return super.fetch(modelPath);
}
}
}
const modelManagerOptions = {};
if(process.env.REACT_APP_PROXY_ENABLED) {
modelManagerOptions.modelClient = new LocalDevModelClient(process.env.REACT_APP_API_HOST);
}else{
modelManagerOptions.modelClient = new VanityURLModelClient(process.env.REACT_APP_API_HOST);
}
const renderApp = (vanityUrls) => {
ModelManager.initialize(modelManagerOptions).then(pageModel => {
const history = createBrowserHistory();
render(
,
document.getElementById('spa-root')
);
});
};
document.addEventListener('DOMContentLoaded', () => {
getVanityUrls().then((vanityUrls) => {
renderApp(vanityUrls);
}, (err) => {
console.log("Error getting vanity urls", err);
renderApp({});
});
});
2) Pass the page full url to vanity url mapping object loaded in step above to child pages in eaem-spa-vanity-urls\ui.frontend\src\App.js
Read Full Blog
Q&A
Please use this thread to ask the related questions.
Kautuk Sahni