Understanding External Component in Content Hub with Use Case Using React
Hello everyone, In this blog we will see how we can create external component using react in Sitecore Content Hub. In one of my old blogs, I have created a similar component using Knockout JS. We will consider the same use case to build this component but using React. Before starting our blog, I will highly encourage you to read my old blog here. Creating external component, assigning the component to Asset Details page, making it visible on the page will be exactly same as we have done in the old blog. The changes what we are going to do in Content Hub, I will add the screenshot below.
Before developing the component in react, we need to setup our React front end solution. I would highly recommened to watch the youtube tutorial for setting up the front end solution here. Adding few helpful command below which is required during setup. I will provide all reference link below in reference section.
// For creating new react project using Vite
npm create vite@latest
//Quickly install SSL dependency
npm i --save-dev @vitejs/plugin-basic-ssl
//For node js env variable
npm i --save-dev @types/node
//Installing content hub webclient sdk
npm i @sitecore/sc-contenthub-webclient-sdk
I hope now your front end solution setup is done.Now our task is to accomplish our functionality. So let's quickly jump on to that. Our task mainly focus on creating related asset external component which will list down all the assets having the title of the page where it is added. We will use full text search functionality to achieve the same. The below screenshot is the desired output.
Let's build our Entity definition API in content hub to get the data. The url format remain the same as per old blog with change in full text query value. In our case it is DXBoston-Home. The url : https://{Your content hub sandbox domain}/api/entities/query?query=Definition.Name=="M.Asset" AND FullText=="DXBoston-Home"&take=10&skip=0
Now in our front end application, let's create our component. Create component folder under src --> create SearchResult folder under component --> Create index.tsx file under SearchResult. Also create a searchhelper.tsx file under SearchResult folder. Refer the below screenshot
Paste the below code in index.tsx file.
import { ContentHubClient } from "@sitecore/sc-contenthub-webclient-sdk";
import { createRoot } from "react-dom/client";
import Searchhelper from "./searchhelper";
interface Context {
client: ContentHubClient
options: {
entityId?: number;
}
}
export default function createExternalRoot(container : HTMLElement){
const root = createRoot(container);
return{
render(context: Context) {
console.log(context)
console.log(context.options.entityId)
root.render(
<Searchhelper client={context.client} entityId={context.options.entityId} />
);
},
unmount() {
root.unmount();
},
}
}
In the above code, we are just taking the client and option properties from context. We are passing the values to searchhelper component for our main api calling.
Paste the below code in searchhelper.tsx file.
import React,{useEffect, useState} from "react";
import { ContentHubClient } from "@sitecore/sc-contenthub-webclient-sdk";
interface Props {
client: ContentHubClient;
entityId?:number
}
interface Item {
id: number;
properties: {
Title: string;
};
self: {
href: string;
};
}
interface ItemList{
items: Item[];
}
const Searchhelper: React.FC<Props> = ({ client,entityId }) => {
const [assetEnitity, setAssetEnitity] = useState<ItemList>({
items:[]
});
console.log("The page entity id ",entityId)
const [error, setError] = useState<string | null>(null);
useEffect(()=>{
const fetchAssets = async() =>{
try{
var entity = await client.entities.getAsync(entityId!);
let entitytitle: string | undefined = entity?.getPropertyValue("Title") as string | undefined;;
console.log("the entity title is ",entitytitle)
const cleanedTitle = entitytitle?.replace(/\.jpg$/i, '');
console.log("the clean entity title is ",cleanedTitle)
const url = `/api/entities/query?query=Definition.Name=="M.Asset" AND FullText=="${cleanedTitle}"&take=10&skip=0`;
const response = await client.raw.getAsync<any>(url);
const arrayitems = response?.content?.items;
console.log(arrayitems);
if (Array.isArray(arrayitems)) {
setAssetEnitity({
items : arrayitems});
} else {
setError("Unexpected response format.");
}
}catch(err){
setError(`Failed to fetch campaigns. Error: ${err}`);
console.error("Error fetching campaigns:", err);
}
}
fetchAssets();
},[client,entityId]);
return(
<>
<h3>Related Assets</h3>
{error && <p>{error}</p>}
{assetEnitity.items.length <= 0 ? (
// If no items are available
<p>No data available with similar title</p>
) : (
// If items are available, render the table
<table style={{borderCollapse: 'collapse',marginTop : '10px' }}>
<thead>
<tr>
<th style={{ textAlign: 'left',border: '1px solid #ddd', padding: '8px', backgroundColor: '#f2f2f2',fontWeight: '600' }}>Entity ID</th>
<th style={{ textAlign: 'left',border: '1px solid #ddd', padding: '8px',backgroundColor: '#f2f2f2', fontWeight: '600'}}>Title</th>
</tr>
</thead>
<tbody>
{assetEnitity.items.map((item) => (
<tr key={item.id}>
<td style={{border: '1px solid #ddd',padding: '8px',textAlign: 'left'}}><a href={item.self.href} target="_blank" rel="noopener noreferrer">
{item.id}</a>
</td >
<td style={{border: '1px solid #ddd',padding: '8px',textAlign: 'left'}}>{item.properties.Title}</td>
</tr>
))}
</tbody>
</table>
)}
</>
)
}
export default Searchhelper;
In searchhelper component, we are forming the api url which we need to call. With the help of react hooks like UseEffect and UseState we are making api calls and storing the data and maintaing it. With the help of entity id, we are fetching the entity. Once entity is available, the we are reading the title property which we are passing as query parameter to FullText. After getting the data, we are creating the html structure for loaded results in tabular format.
Run the below command to generate the production build with minified JavaScript. Once the process completes, a new dist folder will be created in the project directory, containing the minified searchresult.js file.
npm run build --component=searchresult
Run the below command to start the local server. Once that is started, you can just browse the searchresult.js file. URL: https://localhost:5173/dist/searchresult.js
npm run dev
Navigate to the Asset Details page in Content Hub. Click on Manage --> Pages --> Asset Details. Add a new external component shown below. Ensure to check the visible checkbox. Once done, it will look like below screenshot.
Click on your external component, select the path radio button and give your local dev js url as shown below.
Note: The minified js can be directly uploaded to portal assets and used from there. To upload the asset from Portal Assets, check the From Asset radio button. In higher environments, portal asset option is must.
Open your any asset detail page from Asset page. You can see your external component Related Asset with desired result as shown below.
Thank for reading and keep learning!!.
You can check my other blogs too if interested. Blog Website
Steps to avoid from youtube video
- Avoid running npm config set @sitecore:registry=https://slpartners.myget.org/F/m-public/api/v3/index.json command: Not required as dependent packages is moved to npm registry.
- Avoid adding --https to dev scripts in package.json file : Not required
References
Comments
Post a Comment