Colonne griglia personalizzate
{align="center"}
È possibile aggiungere colonne griglia personalizzate alla console Frammenti di contenuto utilizzando il punto di estensione contentFragmentGrid
. Questo esempio mostra come aggiungere una colonna personalizzata che visualizzi la pagina Frammenti di contenuto, in base alla data dell’ultima modifica, in formato leggibile.
Punto di estensione
Questo esempio si estende al punto di estensione contentFragmentGrid
per aggiungere una colonna personalizzata alla console Frammenti di contenuto.
Estensione di esempio
Nell'esempio seguente viene creata una colonna personalizzata, Age
, che visualizza la pagina del frammento di contenuto in formato leggibile. La pagina viene calcolata a partire dall’ultima data modificata del frammento di contenuto.
Il codice mostra come è possibile ottenere i metadati del frammento di contenuto nel file di registrazione dell’estensione e come è possibile esportare il contenuto JSON del frammento di contenuto.
In questo esempio viene utilizzata la libreria Luxon per calcolare la durata del frammento di contenuto, installato tramite npm i luxon
.
Registrazione dell’estensione
ExtensionRegistration.js
, mappato alla route index.html, è il punto di ingresso per l'estensione AEM e definisce:
- La posizione dell'estensione si inietta (
contentFragmentGrid
) nell'esperienza di creazione AEM - Definizione della colonna personalizzata nella funzione
getColumns()
- I valori di ogni colonna personalizzata, per riga
import React from "react";
import { generatePath } from "react-router";
import { Text } from "@adobe/react-spectrum";
import { register } from "@adobe/uix-guest";
import { extensionId } from "./Constants";
import { Duration } from "luxon";
/**
* Set up a in-memory cache of the custom column data.
* This is important if the work to contain column data is expensive, such as making HTTP requests to obtain the value.
*
* The caching of computed value is optional, but recommended if the work to compute the column data is expensive.
*/
const COLUMN_AGE_ID = "age";
const cache = {
[COLUMN_AGE_ID]: {},
};
function ExtensionRegistration() {
const init = async () => {
const guestConnection = await register({
id: extensionId,
methods: {
contentFragmentGrid: {
getColumns() {
return [
{
id: COLUMN_AGE_ID, // Give the column a unique ID.
label: "Age", // The label to display
render: async function (fragments) {
// This function is run for each row in the grid.
// The fragments parameter is an array of all the fragments (aka each row) in the grid.
const context = await guestConnection.hostConnection.getRemoteApi().getSharedContext();
// Iterate over each fragment in the grid
for (const fragment of fragments) {
// Check if a previous pass has computed the value for this fragment.id. If it has, we can skip it.
if (!cache[COLUMN_AGE_ID][fragment.id]) {
// If the fragment.id has not been computed and cached, then compute the value and cache it.
cache[COLUMN_AGE_ID][fragment.id] = await computeAgeColumnValue(fragment, context);
}
}
// Return the populated cache of the custom column data.
return cache[COLUMN_AGE_ID];
}
},
// Add other custom columns here...
];
},
},
},
});
};
init().catch(console.error);
return <Text>IFrame for integration with Host (AEM)...</Text>;
}
async function computeAgeColumnValue(fragment, context) {
// Various data is available in the sharedContext, such as the AEM host, the IMS token, and the fragment itself.
// Accessing AEM APIs requires the IMS token, which is available in the sharedContext, and also supporting CORS configurations deployed to AEM Author.
//const aemHost = context.aemHost;
//const accessToken = context.auth.imsToken;
// Get the user's locale to format the value of the column.
const locale = context.locale;
// Compute the value of the column, in this case we are computing the age of the fragment from its last modified date.
const duration = Duration.fromMillis(Date.now() - fragment.modifiedDate).rescale();
let largestUnit = {};
if (duration.years > 0) {
largestUnit = {years: duration.years};
} else if (duration.months > 0) {
largestUnit = {months: duration.months};
} else if (duration.days > 0) {
largestUnit = {days: duration.days};
} else if (duration.hours > 0) {
largestUnit = {hours: duration.hours};
} else if (duration.minutes > 0) {
largestUnit = {minutes: duration.minutes};
} else if (duration.seconds > 0) {
largestUnit = {seconds: duration.seconds};
}
// Convert the largest unit of the age to human readable format.
const columnValue = Duration.fromObject(largestUnit, {locale: locale}).toHuman();
// Return the value of the column.
return columnValue;
}
export default ExtensionRegistration;
Dati dei frammenti di contenuto
Il metodo render(..)
in getColumns()
ha passato una matrice di frammenti. Ogni oggetto nell’array rappresenta una riga nella griglia e contiene i seguenti metadati sul frammento di contenuto. Questi metadati possono essere utilizzati per le colonne personalizzate più comuni nella griglia.
render: async function (fragments) {
for (const fragment of fragments) {
// An example value from this console log is displayed below.
console.log(fragment);
}
}
Esempio di JSON per frammenti di contenuto disponibile come elemento del parametro fragments
nel metodo render(..)
.
{
"id": "/content/dam/wknd-shared/en/magazine/alaska-adventure/alaskan-adventures",
"name": "alaskan-adventures",
"title": "Alaskan Adventures",
"status": "draft",
"statusPreview": null,
"model": {
"name": "Article",
"id": "/conf/wknd-shared/settings/dam/cfm/models/article"
},
"folderId": "/content/dam/wknd-shared/en/magazine/alaska-adventure",
"folderName": "Alaska Adventure",
"createdBy": "admin",
"createdDate": 1684875665786,
"modifiedBy": "admin",
"modifiedDate": 1654104774889,
"publishedBy": "",
"publishedDate": 0,
"locale": "",
"main": true,
"translations": {
"locale": "en",
"languageCopies": [
{
"path": "/content/dam/wknd-shared/en/magazine/alaska-adventure/alaskan-adventures",
"title": "Alaskan Adventures",
"locale": "en",
"model": "Article",
"status": "DRAFT",
"id": "/content/dam/wknd-shared/en/magazine/alaska-adventure/alaskan-adventures"
}
]
},
"transientStatus": {},
"extensions": {
"age": "1 year old"
}
}
Se sono necessari altri dati per compilare la colonna personalizzata, è possibile effettuare richieste HTTP all’autore AEM per recuperare i dati.
https://localhost:9080
, l'origine dello stage di AppBuilder e l'origine della produzione di AppBuilder.const context = await guestConnection.hostConnection.getRemoteApi().getSharedContext();
...
// Fetch the Content Fragment JSON from AEM Author using the AEM Assets HTTP API
const response = await fetch(`${context.aemHost}${fragment.id.slice('/content/dam'.length)}.json`, {
headers: {
'Authorization': `Bearer ${context.auth.imsToken}`
}
})
...
Definizione colonna
Il risultato del metodo di rendering è un oggetto JavaScript le cui chiavi sono il percorso del frammento di contenuto (o fragment.id
) e il cui valore è da visualizzare nella colonna.
Ad esempio, i risultati di questa estensione per la colonna age
sono:
{
"/content/dam/wknd-shared/en/magazine/la-skateparks/ultimate-guide-to-la-skateparks": "22 minutes",
"/content/dam/wknd-shared/en/adventures/bali-surf-camp/bali-surf-camp": "1 hour",
"/content/dam/wknd-shared/en/magazine/western-australia/western-australia-by-camper-van": "1 hour",
"/content/dam/wknd-shared/en/adventures/climbing-new-zealand/climbing-new-zealand": "10 months",
"/content/dam/wknd-shared/en/magazine/skitouring/skitouring": "1 year",
"/content/dam/wknd-shared/en/adventures/beervana-portland/beervana-in-portland": "1 year",
"/content/dam/wknd-shared/en/magazine/alaska-adventure/alaskan-adventures": "1 year",
"/content/dam/wknd-shared/en/magazine/arctic-surfing/aloha-spirits-in-northern-norway": "1 year",
"/content/dam/wknd-shared/en/magazine/san-diego-surf-spots/san-diego-surfspots": "1 year",
"/content/dam/wknd-shared/en/magazine/fly-fishing-amazon/fly-fishing": "1 year",
"/content/dam/wknd-shared/en/adventures/napa-wine-tasting/napa-wine-tasting": "1 year",
"/content/dam/wknd-shared/en/adventures/cycling-southern-utah/cycling-southern-utah": "1 year",
"/content/dam/wknd-shared/en/adventures/gastronomic-marais-tour/gastronomic-marais-tour": "1 year",
"/content/dam/wknd-shared/en/adventures/tahoe-skiing/tahoe-skiing": "1 year",
"/content/dam/wknd-shared/en/adventures/surf-camp-in-costa-rica/surf-camp-costa-rica": "1 year",
"/content/dam/wknd-shared/en/adventures/yosemite-backpacking/yosemite-backpacking": "1 year",
"/content/dam/wknd-shared/en/adventures/whistler-mountain-biking/whistler-mountain-biking": "1 year",
"/content/dam/wknd-shared/en/adventures/colorado-rock-climbing/colorado-rock-climbing": "1 year",
"/content/dam/wknd-shared/en/adventures/ski-touring-mont-blanc/ski-touring-mont-blanc": "1 year",
"/content/dam/wknd-shared/en/adventures/cycling-tuscany/cycling-tuscany": "1 year",
"/content/dam/wknd-shared/en/adventures/west-coast-cycling/west-coast-cycling": "1 year",
"/content/dam/wknd-shared/en/adventures/downhill-skiing-wyoming/downhill-skiing-wyoming": "1 year",
"/content/dam/wknd-shared/en/adventures/riverside-camping-australia/riverside-camping-australia": "1 year",
"/content/dam/wknd-shared/en/contributors/ian-provo": "1 year",
"/content/dam/wknd-shared/en/contributors/sofia-sj-berg": "1 year",
"/content/dam/wknd-shared/en/contributors/justin-barr": "1 year",
"/content/dam/wknd-shared/en/contributors/jake-hammer": "1 year",
"/content/dam/wknd-shared/en/contributors/jacob-wester": "1 year",
"/content/dam/wknd-shared/en/contributors/stacey-roswells": "1 year",
"/content/dam/wknd-shared/en/contributors/kumar-selveraj": "1 year"
}