Rendering based on Content Type
Contents
In this chapter you will create a new component that renders persons
Person content type
The Headless Movie DB contains sample content, and content types. One of these is Person
or more specifically com.enonic.app.hmdb:person
.
From Content Studio, you may easily find and list persons. By selecting the JSON preview, you can also study the properties of a person:
{
"_id": "a8b374a2-c532-45eb-9aa1-73d1c37cd681",
"_name": "lea-seydoux",
"_path": "/hmdb/persons/lea-seydoux",
"creator": "user:system:su",
"modifier": "user:system:su",
"createdTime": "2021-11-23T10:01:16.711Z",
"modifiedTime": "2021-11-23T13:06:38.493Z",
"owner": "user:system:su",
"type": "com.enonic.app.hmdb:person",
"displayName": "Léa Seydoux",
"hasChildren": true,
"language": "en",
"valid": true,
"childOrder": "modifiedtime DESC",
"data": {
"photos": "09b3af0e-6da3-4bcf-88d9-11cbe9c41283",
"bio": "French actress Léa Seydoux was born in 1985 in Paris, France, to Valérie Schlumberger, a philanthropist, and Henri Seydoux, a businessman.",
"dateofbirth": "1985-07-01"
},
"x": {},
"page": {},
"attachments": {},
"publish": {
"from": "2024-11-13T13:32:39.427Z",
"first": "2024-11-13T13:32:39.427Z"
},
"workflow": {
"state": "READY",
"checks": {}
}
}
The Person component
To implement the rendering we need to create a new component, and accompanying data processor:
-
Create a processor:
/src/main/resources/react4xp/components/content/PersonProcessor.tsimport {get as getContentByKey} from '/lib/xp/content'; import {imageUrl} from '/lib/xp/portal'; import {toArray} from "/react4xp/utils/arrayUtils"; import {PageDescriptor} from '@enonic-types/core'; import type {Content} from '@enonic-types/lib-content'; import type {ComponentProcessorFunction} from '@enonic-types/lib-react4xp/DataFetcher'; function fetchAdditionalPhotos(photosIds) { return photosIds.map(photoId => { if (photoId) { const photoContent = getContentByKey<Content>({key: photoId}); return { _id: photoContent._id, title: photoContent.displayName, imageUrl: imageUrl({id: photoContent._id, scale: 'block(175, 175)'}) }; } }); } export const personProcessor: ComponentProcessorFunction<PageDescriptor> = (params) => { const photos: string[] = toArray<string>(params.content.data.photos as string | string[]) const firstPhotoId = photos[0] || ''; const remainingPhotoIds = photos.slice(1); let firstPhoto = null; if (firstPhotoId) { const {_id, displayName} = getContentByKey<Content>({key: firstPhotoId}); firstPhoto = { _id, title: displayName, imageUrl: imageUrl({id: _id, scale: 'block(1200, 675)'}) } } const extraPhotos = fetchAdditionalPhotos(remainingPhotoIds); return { displayName: `${params.content.displayName}`, photo: firstPhoto, birthDate: params.content.data.dateofbirth, restPhotos: extraPhotos }; };
-
Add it to the dataFetcher, with the following two lines:
dataFetcherimport {personProcessor} from './components/content/PersonProcessor'; dataFetcher.addContentType('com.enonic.app.hmdb:person', {processor: personProcessor});
-
Create the Person component, it consists of two files:
/src/main/resources/react4xp/components/content/Person.tsx (React component)import React from 'react' import styles from './Person.module.css'; export const Person = (props) => { const {displayName, photo, restPhotos, bioHtml, birthDate} = props as any; return ( <div className={styles.person}> <h1>{displayName}</h1> <p>{birthDate}</p> <div> { photo ? ( <> <div className={styles.photos}> <img src={photo.imageUrl} title={photo.title} alt={photo.title} height={675} width={1200} loading="eager" /> </div> </> ) : ( <p>No photo available</p> ) } {restPhotos && restPhotos.length > 0 && ( <> <h2>Photos</h2> <div className={styles.photoContainer}> <div className={styles.photoGrid}> <div className={styles.photoScroll}> {restPhotos.map((photo, index) => ( <img key={index} src={photo.imageUrl} title={photo.title} alt={photo.title} height={175} width={175} /> ))} </div> </div> </div> </> )} </div> </div> ) }
/src/main/resources/react4xp/components/content/Person.module.css (CSS module).person .bio { font-family: sans-serif; } .person { display: flex; flex-direction: column; margin-inline: auto; min-height: 1600px; } .person h1 { margin-bottom: 0; } .person p { margin-top: 0; } .flexBlock { display: flex; justify-content: space-between; } .photoContainer { position: relative; display: flex; ::-webkit-scrollbar { -webkit-appearance: none; height: 6px; } ::-webkit-scrollbar-thumb { border-radius: 8px; background-color: ghostwhite; } } .photoGrid { overflow-x: auto; } .photoScroll { display: flex; gap: 25px; white-space: nowrap; padding: 0; margin: .5rem 0 .5rem 0; } .photoScroll img { border-radius: 5px; } .photos { margin-block: 1rem; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); grid-gap: 50px; gap: 50px; } .restPhotosContainer { padding: 1rem 0; max-width: calc(90vw - 1rem); } .restPhotos { display: grid; grid-template-columns: repeat(auto-fill, minmax(15%, 1fr)); gap: 5%; margin-block: 1rem; } .photos img { width: 100%; height: auto; border-radius: 8px; } .restImg { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; object-position: 50% 50%; border-radius: 5px; } .richText { font-size: 24px; } .richText figure img { border-radius: 5px; } .photosHeader { margin-top: 0; color: #EF82F0; }
-
Plaec it in the component to the registry: by adding the following two lines:
import {Person} from './components/content/Person';
componentRegistry.addContentType('com.enonic.app.hmdb:person', {View: Person}); (1)
+ <1> By using addContentType()
we are instructnig the componentRegistry to render this component when the content is of type 'com.enonic.app.hmdb:person'.
Back in Content Studio, select a random person to see what you have made:
Next
You are on a roll, next - lets have a look at Rich text handling.