Building a Likes Web Component as a PnP Search Extension

In this blog post, I will show an example of a LikesWebComponent as a search extension for the PnP Search Web Part. This component will allow users to like and unlike items for SharePoint items that are found and displayed in the PnP Search Web Part. The following image shows an example of the “Likes component” can be used in a news listing:

Prerequisites

  • Basic understanding of React and TypeScript
  • Familiarity with SharePoint Framework (SPFx)
  • Node.js and npm installed on your machine
  • SharePoint Online site

Step 1: Setting Up the Project

First, set up a new SPFx project using the Yeoman generator.

yo @microsoft/sharepoint

Step 2: Installing PnPjs

Install the PnPjs libraries required for interacting with SharePoint.

npm install @pnp/sp @pnp/spfx-controls-react @pnp/logging @pnp/common @pnp/odata

Step 3: Creating the LikesWebComponent

Create a new TypeScript file for your component, e.g., LikesWebComponent.tsx.

Step 4: Implementing the Component

Implement the LikesWebComponent by following these steps:

Import Dependencies

    import * as React from 'react';
    import { spfi, SPFI } from '@pnp/sp';
    import { spSPFx } from '@pnp/sp/spfx';
    import { PageContext } from '@microsoft/sp-page-context';
    import { SPHttpClient } from '@microsoft/sp-http';
    import { DataService, ILikedByInformation } from './DataService';
    import LikesComponent from './LikesComponent';
    

    Define the Component

    export class LikesWebComponent extends BaseWebComponent {
    
        private _dataService: IDataService;
        private _sp: SPFI;
        private _spHttpClient: SPHttpClient;
    
        public async connectedCallback(): Promise<void> {
    
            /** Incoming properties */
            // sp-site-url: spSiteUrl
            // list-id: listId
            // item-id: itemId
            // icon-name: iconName
    
            // Get properties from the handlebar
            const props: any = this.resolveAttributes();
    
            console.log("PROPS FROM HANDLEBAR", props);
    
            // Get the service scope from the base web component
            this._serviceScope.whenFinished(async () => {
    
                // Get the page context and SPHttpClient  from the service scope
                const pageContext = this._serviceScope.consume(PageContext.serviceKey as any) as any;
                this._spHttpClient = this._serviceScope.consume(SPHttpClient.serviceKey as any);
    
                console.log("PAGECONTEXT", pageContext);
                // Get the SPFI object using the pageContext object
                this._sp = spfi().using(spSPFx({ pageContext }));
    
                // Initialize the dataservice using the component site url
                this._dataService = new DataService(this._sp, null, null, props.spSiteUrl, this._spHttpClient);
    
                const _likesInfo: ILikedByInformation = await this._dataService.getLikedByInformation(props.spSiteUrl, props.listId, props.itemId);
    
                const likeItem = async (): Promise<void> => {
                    try {
                        await this._dataService.likeItem(props.spSiteUrl, props.listId, props.itemId);
                    }
                    catch (error) {
                        console.error("Error liking item", error);
                    }
                };
    
                const unlikeItem = async (): Promise<void> => {
                    try {
                        await this._dataService.unlikeItem(props.spSiteUrl, props.listId, props.itemId);
                    }
                    catch (error) {
                        console.error("Error unliking item", error);
                    }
                };
    
                const customComponent = <LikesComponent
                    likesInfo={_likesInfo}
                    likeItem={likeItem}
                    unlikeItem={unlikeItem}
                />;
                ReactDOM.render(customComponent, this);
                return Promise.resolve();
            });
        }
        public disconnectedCallback(): void {
            // Unmount the component to avoid @microsoft/spfx/pair-react-dom-render-unmount warning
            ReactDOM.unmountComponentAtNode(this);
        }
    }
    

    DataService Implementation 

    Implement the DataService class to handle the data operations.

      public async getLikedByInformation(spSiteUrl: string, listId: string, itemId: number): Promise<any> {
        try {
          const url = `${spSiteUrl}/_api/web/lists('${listId}')/GetItemById(${itemId})/likedByInformation`;
          const response = await this._spHttpClient.get(url, SPHttpClient.configurations.v1);
          return await response.json();
        } catch (error) {
          console.error('Error fetching likes', error);
          return 0;
        }
      }
    
      public async likeItem(spSiteUrl: string, listId: string, itemId: number): Promise<void> {
        const httpClientOptions: IHttpClientOptions = {
          body: '{}'
        };
        const url = `${spSiteUrl}/_api/web/lists('${listId}')/GetItemById(${itemId})/like`;
        try {
          await this._spHttpClient.post(url, SPHttpClient.configurations.v1, httpClientOptions);
        } catch (error) {
          console.log('Error liking item', error);
          throw error;
        }
      }
    
      public async unlikeItem(spSiteUrl: string, listId: string, itemId: number): Promise<void> {
        const httpClientOptions: IHttpClientOptions = {
          body: '{}'
        };
        const url = `${spSiteUrl}/_api/web/lists('${listId}')/GetItemById(${itemId})/unlike`;
        try {
          await this._spHttpClient.post(url, SPHttpClient.configurations.v1, httpClientOptions);
        } catch (error) {
          console.log('Error unliking item', error);
          throw error;
        }
      }
    

    Step 5: Testing the Component

    Deploy the component to your SharePoint Online site and test its functionality, or run your project using gulp serve or similar.

    Conclusion

    In this post, we have built a LikesWebComponent as a PnP Search Extension for the PnP Search Web Part. This component allows users to like and unlike items in a SharePoint list, leveraging the power of PnPjs for SharePoint operations.

    Disclaimer

    The code and instructions provided in this blog post are for educational purposes only. While every effort has been made to ensure the accuracy and functionality of the code, the author and publisher assume no responsibility for any errors or omissions. Use the code at your own risk and always test thoroughly in a development environment before deploying to production.

    Leave a comment