Usage
Alokai integration for SmartEdit ships with a framework-agnostic Typescript SDK. It allows you to:
- ri:checkbox-circle-fillfetch your pages from the SAP Commerce Cloud Pages API
Fetching pagesri:link
The getPageByIdAndUser()
method allows you to fetch the page data and CMS content slots using the pageLabel
/pageId
or the code
parameter
import { getSdk } from '@/sdk';
const sdk = await getSdk();
const page = await sdk.smartedit.getPageByIdAndUser({
pageLabel: 'homepage',
});
getPageByIdAndUser
doesn't return the nested components of the page. You can fetch them using the getComponentsByIdsAndUser()
method
import { getSdk } from '@/sdk';
const sdk = await getSdk();
const page = await sdk.smartedit.getComponentsByIdsAndUser({
componentIds: ['Component1', 'Component2'],
});
Initializing Live Previewri:link
Use the initLivePreview()
method to allow SmartEdit to edit your application pages in real time by:
- ri:checkbox-circle-fillinjecting the webApplicationInjector script into your application's head,
- ri:checkbox-circle-filladding the SmartEdit classes to the
body
element as specified in the SmartEdit HTML markup contract, - ri:checkbox-circle-fillhandling component updates.
To allow the method to work correctly:
- ri:checkbox-circle-fillcreate a /cx-preview route which SmartEdit can load initially to get redirected to a proper route in your app,
- ri:checkbox-circle-fillfulfill the SmartEdit HTML markup contract by adding SmartEdit properties to slots and components,
- ri:checkbox-circle-fillcall the
initLivePreview()
method client-side and pass the raw, non-unified page object to it, - ri:checkbox-circle-fillinclude a copy of the
webApplicationInjector
script in your project,
We suggest keeping the webApplicationInjector
script it in the /public
directory of your Next/Nuxt project. If you want to keep it somewhere else, provide the correct path to the file via the scriptSrc
prop,
Creating the preview routeri:link
Whenever you open up Live Preview window for a page in SmartEdit, it will first try to load your application's /cx-preview route. Make sure it does exist and handle redirect to a desired Storefront route there.
import { prepareConfig } from '@vue-storefront/sdk';
import type { SearchParams } from 'nuqs/server';
import { redirect } from '@/config/navigation';
import { getSdk } from '@/sdk';
/**
* CX Preview Page Props
*/
interface CxPreviewPageProps {
/**
* Raw parameters passed by Next.js to the top-level page component.
*/
params: {
/**
* The locale of the page.
*/
locale: string;
/**
* The slug of the page.
*/
slug?: string[];
};
/**
* Search parameters.
*/
searchParams: SearchParams;
}
/**
* Change the path to your example product path.
*
* Path is used in case when you want to create a shared CMS content for all products.
* In this case, as the `/product` path doesn't exist, we need to redirect to one of the product page,
* to be able to preview the CMS content in SmartEdit.
*/
const EXAMPLE_PRODUCT_PATH = '/product/45574-snowboard-ski-tool-toko-plexiklinge-3-mm-flexibel/45574';
export default async function CxPreviewPage({ searchParams }: CxPreviewPageProps) {
const sdk = await getSdk();
const { cmsTicketId: cmsTicketIdParam } = searchParams;
if (!cmsTicketIdParam) {
redirect({ pathname: '/' });
return null;
}
const cmsTicketId = cmsTicketIdParam.toString();
const requestConfig = prepareConfig({ headers: { 'x-cms-ticket-id': cmsTicketId } });
const { data: page } = await sdk.smartedit.getPageWithUser({}, requestConfig);
let redirectPath = (page as any).label;
if (redirectPath === '/product') {
redirectPath = EXAMPLE_PRODUCT_PATH;
}
redirect({
pathname: `/${redirectPath}`,
query: { cmsTicketId },
});
return null;
}
Fulfilling HTML markup contractri:link
When the /cx-preview
route performs a redirect, the destination route needs to be handled by a page component which fetches and renders SmartEdit data (including properties required by the HTML markup contract).
import { prepareConfig } from '@vue-storefront/sdk';
import { SearchParams } from 'nuqs/parsers';
import { getSdk } from '@/sdk';
import LivePreview from '@/components/live-preview';
/**
* CMS Dynamic Page props.
*/
interface DynamicPageProps {
/**
* Raw parameters passed by Next.js to the top-level page component.
*/
params: {
/**
* The locale of the page.
*/
locale: string;
/**
* The slug of the page.
*/
slug?: string[];
};
/**
* The search parameters of the page.
*/
searchParams: SearchParams;
}
export default async function DynamicPage(props: DynamicPageProps) {
const sdk = await getSdk();
const { searchParams } = props;
const { cmsTicketId: cmsTicketIdParam } = searchParams;
const cmsTicketId = cmsTicketIdParam?.toString();
const requestConfig = prepareConfig({});
if (cmsTicketId) {
Object.assign(requestConfig, { headers: { 'x-cms-ticket-id': cmsTicketId } });
}
const { data: page } = await sdk.smartedit.getPageWithUser({ pageLabelOrId: '/your-page-path' }, requestConfig);
return (
<>
{/* The LivePreview component will be added in the next step */}
<LivePreview page={page} />
{
page.contentSlots?.contentSlot?.map((contentSlot) => (
<>
<div {...getSmartEditProps(contentSlot)}>
{
contentSlot.components?.component?.map((component) => (
<div {...getSmartEditProps(component)} key={component.uid}>
<pre>{JSON.stringify(component, null, 2)}</pre>
</div>
))
}
</div>
</>
))
}
</>
)
}
function getSmartEditProps(item: Record<string, any>) {
const { properties = {} } = item;
const { smartedit } = properties;
if (!smartedit) {
return {};
}
const { catalogVersionUuid, classes, componentId, componentType, componentUuid } = smartedit;
return {
className: classes,
'data-smartedit-catalog-version-uuid': catalogVersionUuid,
'data-smartedit-component-id': componentId,
'data-smartedit-component-type': componentType,
'data-smartedit-component-uuid': componentUuid,
};
}
Calling initLivePreview()
methodri:link
Connection with SmartEdit's Live Preview service is a purely client-side operation. Therefore, make sure you're calling the initLivePreview()
method via appropriate lifecycle methods / hooks. Executed on the server-side, it will not throw but will not yield the desired result either.
Create a client component which calls initLivePreview()
. Make sure it is imported and rendered in the page component from the previous step.
'use client';
import { useEffect } from 'react';
import { useSdk } from '@/sdk/alokai-context';
interface LivePreviewProps {
/**
* A SmartEdit page object.
*/
page: Record<string, any>;
}
/**
* Change these to match your SAP environment
*/
const ALLOWED_ORIGINS = ['backoffice.c1jvi8hu9a-vsfspzooa1-d1-public.model-t.cc.commerce.ondemand.com:443'];
export default function LivePreview(props: LivePreviewProps) {
const { page } = props;
const sdk = useSdk();
useEffect(() => {
sdk.smartedit.utils.initLivePreview({
allowedOrigins: ALLOWED_ORIGINS,
page,
});
}, []);
return null;
}
If you're experiencing CORS-related issues after calling the initLivePreview()
method, you're probably missing a proper ALLOWED_ORIGINS
configuration. Read the official SmartEdit documentation which explains the issue in more detail.