Dynamische routing met Kentico Kontent en NextJS
Tech4 februari 2021

Dynamische routing met Kentico Kontent en NextJS

De meeste headless Content Management Systemen hebben geen geïntegreerde manier om de navigatie van een site te structureren. Kentico Kontent heeft onlangs Spotlight uitgebracht, dat is ontworpen om deze leemte op te vullen. In dit artikel kijken we naar Spotlight en wat het voor je kan doen. Gevolgd door een technisch diepgaand voorbeeld van hoe Spotlight kan worden geïntegreerd met NextJS, een statische sitegenerator.

Spotlight

De meeste headless content management systemen zijn gebouwd om te worden gebruikt als een omnichannel contenthub. Het is meestal mogelijk om de navigatiestructuur van een site te implementeren door gekoppelde items op de content items te gebruiken, maar dit is niet intuïtief en kan lastig te beheren zijn, in termen van UX. Dit is waar de Spotlight van Kentico Kontent zich onderscheidt. Kentico Kontent is ook gebouwd als een content hub, maar door de Spotlight add-on te installeren voegt het een view toe die gebruikt kan worden om de navigatiestructuur te beheren. Het biedt ook de mogelijkheid om in-context-bewerkingen uit te voeren, een functie die zeer op prijs wordt gesteld door inhoudeditors.

In-context editing

Hoe het werkt

Spotlight voegt een aantal nieuwe inhoudstypen toe aan jouw project. Het belangrijkste is het Pagina-content type. Naast andere velden heeft het Pagina-content type een veld met gekoppelde items, subpagina's genaamd, dat wordt gebruikt om te koppelen met de subpagina's van die pagina. Naast content typen voegt Spotlight een nieuwe weergave toe met een boomstructuur van de navigatiestructuur, die is gebaseerd op dit veld. Als je op een pagina in de navigatiestructuur klikt, wordt de veldeditor weergegeven, maar het is ook mogelijk om de pagina in een voorbeeldmodus te openen. Deze modus toont de daadwerkelijke pagina en stelt je in staat om de in-content van de pagina te bewerken.

Een ander veld dat wordt toegevoegd door het content type van de Pagina, is het inhoudsveld. Dit veld verwijst naar het inhoudsitem dat op de pagina moet worden weergegeven. Als best practice mogen pagina's alleen tijdelijke aanduidingen voor de pagina zijn en naar een inhoudsitem verwijzen. Pagina's zijn specifiek voor het sitekanaal en door de daadwerkelijke inhoud van deze pagina te scheiden, kun je die inhoud hergebruiken voor verschillende kanalen.

Als best practice mogen pagina's alleen tijdelijke aanduidingen voor de pagina zijn en naar een inhoudsitem verwijzen

NextJS

Bij Unplatform zijn we een grote fan van NextJS, een op React gebaseerde Static Site Generator die ook Server Side Rendering en Incremental Static Generation ondersteunt. NextJS heeft pagina's op basis van routering, waarmee je routes kunt toevoegen door pagina's aan de broncode toe te voegen. Als je bijvoorbeeld een service.js toevoegt aan de paginamap, wordt de pagina weergegeven op http://jouwsite/service.

Dynamische routes

NextJS ondersteunt ook dynamische routes. Dit is handig als de data ergens anders is gedefinieerd dan in de broncode, bijvoorbeeld in een Content Management Systeem of in een Commerce Systeem. Laten we als voorbeeld een eenvoudige productdetailpagina maken waarop de naam van de producten wordt weergegeven. Eerst moeten we de pagina toevoegen. Door een nieuwe productmap aan te maken in de paginamap en een bestand met de naam [id].js toe te voegen, zijn de producten beschikbaar op http://jouwsite/product/[id].

Om de naam weer te geven, voegen we een eenvoudige React-component toe, die de productnaam in de props ontvangt:

export default function ProductDetailPage({name}) {
    return <div>{name}</div>
}

Om NextJS te informeren welke pagina's moeten worden gegenereerd voor de productroute, moet het bestand ook een functie getStaticPaths bezitten.

export async function getStaticPaths() {

    // Invoke the commerce system to get the product ids
    const productIds = GetProductsIds();

    return {
        paths: productIds.map(pId => ({
            params: {id: pId}
        }))
    };
}

NextJS roept deze functie op tijdens de bouwtijd en genereert alle productpagina's. Om ten slotte de naam prop door te geven aan de ProductDetailPage-component, moeten we een functie getStaticProps aan het bestand toevoegen. Deze functie wordt ook aangeroepen tijdens de bouwtijd, maar voor elke productdetailpagina. Het product-ID die is geretourneerd door de functie getStaticPaths, wordt doorgegeven in de property params.

export async function getStaticProps({params, preview}) {
    // Invoke commerce solution to get the product
    const product = getProduct(params.id)

    // The props field of the result is passed into the component
    return {
        props: {
            name: product.name
        }
    }
}

Dit is het uiteindelijke resultaat van de productdetailpagina, die de productnaam voor elke pagina weergeeft en beschikbaar zal zijn op de dynamische productenroute:

export default function ProductDetailPage({name}) {
    return <div>{name}</div>
}

export async function getStaticProps({params, preview}) {
    // Invoke commerce solution to get the product
    const product = getProduct(params.id)

    // The props field of the result is passed into the component
    return {
        props: {
            name: product.name
        }
    }
}

export async function getStaticPaths() {

    // Invoke the commerce system to get the product ids
    const productIds = GetProductsIds();

    return {
        paths: productIds.map(pId => ({
            params: {id: pId}
        }))
    };
}

Spotlight combineren met NextJS

Beschouw de volgende sitestructuur in Spotlight: een startpagina met een selfservice-subpagina en een klantenservice-subpagina. Beiden hebben een aantal extra subpagina's:

Sitestructuur in Kentico Kontent

Elke pagina heeft een url-eigenschap. Je zou hier gewoon de hele url kunnen invoeren, bijvoorbeeld: zelfbediening/winkels voor de winkelpagina. Dit is gemakkelijker met een code in kaart te brengen, maar minder handig voor de content editor. Laten we winkels invoeren voor de winkelpagina en toewijzing oplossen met codes.

De spotlight-structuur toewijzen aan NextJS-pagina's

Om de urls toe te wijzen aan pagina's in NextJS, moeten we de getStaticPaths aanpassen om de pagina's op te halen. Om de structuur van Kontent op te halen, kun je de Kontent Delivery SDK gebruiken. In de navolgende code worden de pagina's opgehaald uit Kontent en recursief toegewezen om de URL's te bouwen:

function getContentUrlsRecursive (item) {
    const url = item.url ? [item.url.value] : []

    const urls = [url, ...item.subpages.value.map(subpage => {
        var subpageUrls = getContentUrlsRecursive(subpage)
        
        return subpageUrls.map(sUrl => {
            return [...url, ...(typeof sUrl === 'string' ? [sUrl] : sUrl)]
        })
    })].reduce((a, b) => a.concat(b))

    return urls
}

export async function getStaticPaths() {
    const homepage = await deliveryClient.item('homepage')
        .depthParameter(10)
        .queryConfig({
        usePreviewMode: previewMode
        })

    var urls = getContentUrlsRecursive(homepage.item)

    return {
        paths: urls.map(c => ({
            params: {slug: c}
        }))
    }
}

NextJS vereist dat elke url een array is die de individuele url-delen /service/winkels bevat, in kaart wordt gebracht als ['service', 'winkels']. Het toewijzingsresultaat ziet er als volgt uit:

[
  [ 'service' ],
  [ 'service', 'stores' ],
  [ 'service', 'technical-support' ],
  [ 'service', 'customer-support' ],
  [ 'service', 'latest-news' ],
  [ 'service', 'for-companies' ],
  [ 'service', 'global-conditions' ],
  [ 'customer-care' ],
  [ 'customer-care', 'help-center' ],
  [ 'customer-care', 'payment' ],
  [ 'customer-care', 'how-to-buy' ],
  [ 'customer-care', 'shipping-delivery' ],
  [ 'customer-care', 'international-product-policy' ],
  [ 'customer-care', 'how-to-return' ],
  [ 'customer-care', 'faq' ]
]

Genereren van de pagina's

De eigenschap params die wordt doorgegeven aan de functie getStaticProps, bevat nu de slug die we in de getStaticPaths hebben gegenereerd. De pagina /service/winkels krijgt bijvoorbeeld een slug met ['service', 'winkels']. De uitdaging is nu om in Kentico Kontent de pagina op te halen die bij deze route past. Een manier zou zijn om het startpagina-item op te halen en de pagina op te lossen op basis van de slug. Met een recursieve functie zou de pagina als volgt kunnen worden opgelost:

function getPage(item, slug) {
    const next = item.subpages.value.find(c => c.url.value === slug[0])

    if(!next) {
        return item
    }

    return getPage(next, slug.splice(1))
}

We zouden deze functie dan kunnen gebruiken om de pagina op te halen en de benodigde props terug te sturen naar de paginacomponent:

export async function getStaticProps(slug, preview) {
    const homepage = await getItemByCode('homepage', preview, 10)

    const page = getPage(homepage.item, slug)   

    return {
        props: {
            title: page.title.value,
            description: page.description.value
        }
    }
}

Een andere manier om de pagina te vinden met behulp van de slug, zou zijn om op de een of andere manier de Kontent-codenaam van de getStaticPaths-methode door te geven aan de getStaticProps-methode. Helaas is het niet mogelijk om direct andere gegevens dan de slug door te geven. Een manier om het te laten werken, is door een toewijzing naar de schijf te schrijven in de getStaticPaths-methode en deze vervolgens te lezen in de getStaticProps, zoals beschreven in dit github-probleem. Deze oplossing kan handig zijn wanneer je moet toewijzen van seo-URL naar categorie-ID, wat anders onmogelijk is.

Ondersteuning van verschillende soorten inhoudspagina's

De inhoudseditors in jouw team zullen waarschijnlijk niet erg enthousiast zijn als ze maar één type inhoudspagina kunnen gebruiken, dus laten we ondersteuning voor meerdere toevoegen. Naast het subpaginaveld heeft het pagina-inhoudstype van Spotlight-pagina's ook een inhoudsveld. Dit inhoudsveld wordt gebruikt om te verwijzen naar het inhoudsitem dat op die pagina moet worden weergegeven. Meestal vereisen verschillende inhoudspagina's ook verschillende velden en maak je meerdere inhoudstypen voor de verschillende pagina's. Laten we zeggen dat we een normaal inhoudstype hebben: 'content' en we een inhoud typen met held: 'content_with_hero'. We zullen verschillende React-componenten maken om deze inhoudstypen weer te geven. Laten we dit soort componenten voor de organisatie in een aparte map met sjablonen plaatsen. Om deze componenten te gebruiken, wijzigen we de rootpaginacomponent in [... slug].js en kiezen we een component op basis van het type contentitem:

import Content from '../templates/content'
import ContentWithHero from '../templates/contentwithhero'

export default function ContentPage({type, ...other}) {
    if(type === 'content_with_hero') {
        return <ContentWithHero {...other}/>
    }
    
    return <Content {...other}/>
}

In-context bewerking

Om de in-content bewerkingsfunctionaliteit van Spotlight te ondersteunen, kunt u de preview-functie van NextJS gebruiken. Door preview-URL's in Kontent te configureren die verwijzen naar de API die zijn gemaakt door de NextJS preview-tutorial te volgen, kan Spotlight een preview-versie van de geselecteerde pagina weergeven. Door ook speciale data attributen aan de HTML toe te voegen, zal Kontent ook een bewerkingsknop weergeven naast de velden in de preview modus.

Inhoudseditors zullen Spotlight geweldig vinden

Als jouw project inhoudseditors nodig heeft om de sitenavigatie te beheren, zullen ze erg blij zijn met Spotlight. Kentico Kontent in combinatie met Spotlight geeft je alle voordelen van een headless CMS zonder de gebruikelijke concessies op het gebied van bruikbaarheid. Het maakt het een stuk eenvoudiger om de navigatie te beheren en geeft ze de flexibiliteit die ze gewend zijn van alles-in-één platforms. Met deze NextJS-implementatie kunnen ze de sitenavigatie beheren zonder afhankelijk te zijn van ontwikkelaars.

Wil je meer weten?

Heb je een vraag, wil je meer weten of wil je onze spannende Jamstack storefront demo zien? Gebruik ons contactformulier of stuur ons een mail: info@unplatform.io.

Jonne Kats
Geschreven door Jonne Kats
Op 4 februari 2021