Friday, February 4, 2022

Display data from CMS using Lightning Web Component

 

Motivation behind this


We know that from B2B Commerce Lightning we can use readymade Lightning Web Components available through B2B Lex and use it for many purposes like Product Detail Card, Product Detail List, Cart Component, Order Summary etc. at Experience Cloud.
Refer B2B Commerce on Lightning Experience Components and it also fetches data from CMS.

What if I have to build similar LWC component which can be placed in Lightning RecordDetail page for internal users and data to be retrieved from CMS. This drives me to do a proof-of-concept and sharing you the steps to develop.





Use Case


Business wants to see the list of featured products for internal users and those product images are placed at CMS.

Developer wants to build LWC component to fetch records from CMS.

For sake of simplicity, those images will be displayed using lightning-carousel component.

Solution Approach


To start with, lets configure CMS and store images on CMS side.

Create CMS Workspace

From CMS Home Tab, Create CMS Workspace. Here given a name as Capricorn Store. Screen as follows:


Create a Channel

From Capricorn Store workspace, create a channel naming Capricorn Channel.




Add Content

Add 3 product related pictures as follows. For demo purpose I have used Coffee Machines pictures.


Sample product image and details will look like this:


Now let's create LWC component.

displayMediaFilesFromCMS.html

HTML will like below here lightning-carousel has been used.

<template>
    <lightning-card  title="Featured Products">
<div class="slds-medium-size_1-of-4 slds-align_absolute-center"> <lightning-carousel>
<template for:each={results} for:item="item"> <lightning-carousel-image key={item.title} src= {item.url} header= {item.title} href= {item.url}> </lightning-carousel-image>
</template> </lightning-carousel>
</div> </lightning-card>
</template>

displayMediaFilesFromCMS.js

Few notable things as follows:
  • Channel Name to be passed to Apex class's  retrieveMediaFromCMS() method using wire. For sake of simplicity channel name is hardcoded, it can be captured from design attributes too and using @api variable.
  • Loop through the array returned from Apex method and create new array with title and url as elements which will be used by carousel.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
import { LightningElement, track, wire } from 'lwc';
import retrieveMediaFromCMS from '@salesforce/apex/CMSConnectHelper.retrieveMediaFromCMS';

export default class DisplayMediaFilesFromCMS extends LightningElement {
    channelName = 'Capricorn Channel';
    @track results=[];
    @wire(retrieveMediaFromCMS,{channelName: '$channelName'})
    wiredData({ error, data }) {
        if (data) {
            let objStr = JSON.parse(data);
            objStr.map(element=>{
                this.results = [...this.results,{title:element.title,
                                                url:element.url}]
            });  
            this.error = undefined;            
        } else if (error) {
            this.error = error;
            this.results = undefined;
        }
    }
}


CMSConnectHelper.cls


Salesforce provides ConnectApi.ManagedContent class and its method to retrieve data from CMS. No need to fetch data using REST APIs. Refer documentation

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
 * @description       : This class is used to retrieve data from CMS
 * @author            : Santanu Boral
 * 
**/
public with sharing class CMSConnectHelper {
    
    @AuraEnabled (cacheable=true)
    public static String retrieveMediaFromCMS(String channelName){
        String channelId = getChannelId(channelName);

        //get the image content
        ConnectApi.ManagedContentVersionCollection obj = 
            ConnectApi.ManagedContent.getAllContent(channelId, 0, 5, 'en_US', 
                                                    'cms_image',false,
                                                    '2011-02-25T18:24:31.000Z','2021-09-25T18:24:31.000Z',true);
        
        List<ReturnWrapper> wrapperList = new List<ReturnWrapper>();
        System.debug('json value=' + JSON.serialize(obj));

        //loop through each item and prepare a wrapper list
        for(ConnectApi.ManagedContentVersion versionObj: obj.items){
            ReturnWrapper wrapper = new ReturnWrapper();
            wrapper.title = versionObj.title;
            
            //get the url
            Map<String,ConnectApi.ManagedContentNodeValue> contentNodesMap = versionObj.contentNodes;
            for(String str:contentNodesMap.keySet()){                
                if(str=='source'){
                    wrapper.url= ((ConnectApi.ManagedContentMediaSourceNodeValue)contentNodesMap.get(str)).url;
                }		
            }
            wrapperList.add(wrapper);	
        }
        return JSON.serialize(wrapperList);
    }

    @AuraEnabled (cacheable=true)
    public static String getChannelId(String channelName){
        ConnectApi.ManagedContentChannelCollection channelRepObj = 
                ConnectApi.ManagedContent.getAllDeliveryChannels(0,2);        

        //loop through the channels and return the channel Id
        for(ConnectApi.ManagedContentChannel channelObj: channelRepObj.channels){
            if(channelObj.channelName == channelName){
                return channelObj.channelId;
            }
        }
        return null;
    }

    public class ReturnWrapper{
        String title {get;set;}
        String url {get;set;}
    }
}

getAllContent() returns a JSON as follows which needs to be parsed to form title and url combination for each item elements.

 
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
{
  "currentPageUrl": "/services/data/v51.0/connect/cms/delivery/channels/0ap2v000000Pu1w/contents/query?endDate=2021-09-25T18%3A24%3A31.000Z&language=en_US&managedContentType=cms_image&page=0&pageSize=5&startDate=2011-02-25T18%3A24%3A31.000Z",
  "items": [
    {
      "contentKey": "MCR6FJVMX4BRHSVJD4LPWUPPN3OI",
      "contentNodes": {
        "thumbUrl": {
          "nodeType": "Url",
          "value": "https://live.staticflickr.com/65535/49815666003_4973d84842.jpg"
        },
        "title": {
          "nodeType": "NameField",
          "value": "TR-COFMAC-001"
        },
        "source": {
          "fileName": null,
          "isExternal": true,
          "mediaType": "Image",
          "mimeType": null,
          "nodeType": "MediaSource",
          "referenceId": null,
          "resourceUrl": null,
          "unauthenticatedUrl": null,
          "url": "https://live.staticflickr.com/65535/49815666003_4973d84842.jpg"
        }
      },
      "contentUrlName": "tr-cofmac-001",
      "language": "en_US",
      "managedContentId": "20Y2v000000HbISEA0",
      "publishedDate": "2021-05-09T16:32:44.000Z",
      "title": "TR-COFMAC-001",
      "type": "cms_image",
      "typeLabel": "Image",
      "unauthenticatedUrl": "https://myinstance.my.salesforce.com/cms/delivery/v51.0/0ap2v000000Pu1wAAC/contents/20Y2v000000HbISEA0?oid=00D2v000002A3VoEAK"
    },
    {
      "contentKey": "MCSC6EEJVMZZB5HDHRKMTEQH6TIQ",
      "contentNodes": {
        "thumbUrl": {
          "nodeType": "Url",
          "value": "https://live.staticflickr.com/65535/49815637818_d892f8127d.jpg"
        },
        "title": {
          "nodeType": "NameField",
          "value": "E-ESP-001-1"
        },
        "source": {
          "fileName": null,
          "isExternal": true,
          "mediaType": "Image",
          "mimeType": null,
          "nodeType": "MediaSource",
          "referenceId": null,
          "resourceUrl": null,
          "unauthenticatedUrl": null,
          "url": "https://live.staticflickr.com/65535/49815637818_d892f8127d.jpg"
        }
      },
      "contentUrlName": "e-esp-001-1",
      "language": "en_US",
      "managedContentId": "20Y2v000000HbIIEA0",
      "publishedDate": "2021-05-09T16:32:06.000Z",
      "title": "E-ESP-001-1",
      "type": "cms_image",
      "typeLabel": "Image",
      "unauthenticatedUrl": "https://myinstance.my.salesforce.com/cms/delivery/v51.0/0ap2v000000Pu1wAAC/contents/20Y2v000000HbIIEA0?oid=00D2v000002A3VoEAK"
    },
    {
      "contentKey": "MCKPXLBGW36RDYXN75NDWFVTB5FA",
      "contentNodes": {
        "thumbUrl": {
          "nodeType": "Url",
          "value": "https://live.staticflickr.com/65535/49816090811_622af115a8.jpg"
        },
        "title": {
          "nodeType": "NameField",
          "value": "B-C-COFMAC-001"
        },
        "source": {
          "fileName": null,
          "isExternal": true,
          "mediaType": "Image",
          "mimeType": null,
          "nodeType": "MediaSource",
          "referenceId": null,
          "resourceUrl": null,
          "unauthenticatedUrl": null,
          "url": "https://live.staticflickr.com/65535/49816090811_622af115a8.jpg"
        }
      },
      "contentUrlName": "b-c-cofmac-001",
      "language": "en_US",
      "managedContentId": "20Y2v000000HbIDEA0",
      "publishedDate": "2021-05-09T16:28:55.000Z",
      "title": "B-C-COFMAC-001",
      "type": "cms_image",
      "typeLabel": "Image",
      "unauthenticatedUrl": "https://myinstance.my.salesforce.com/cms/delivery/v51.0/0ap2v000000Pu1wAAC/contents/20Y2v000000HbIDEA0?oid=00D2v000002A3VoEAK"
    }
  ],
  "total": 3
}

Finally, Create a Lightning App Builder page and place the component, it will look like this:


We are done and thanks for reading.

You can also extend this code to expose this component to Experience Cloud with some minor changes.


Further Reading


No comments:

Post a Comment