SharePoint Framework - Retrieve Search Results

Overview

Search has been an integral part of SharePoint over the years. Search helps us to get the security trimmed results across the tenant. Unfortunately, modern SharePoint sites do not provide the search related web parts by default.

In this article, we will explore to query the Search REST API and get the results. We will use React JS in this example.

Create SPFx Solution

Open the command prompt. Create a directory for SPFx solution.
  1. md spfx-react-search  
Navigate the above-created directory.
  1. cd spfx-react-search  
Run Yeoman SharePoint Generator to create the solution.
  1. yo @microsoft/sharepoint  
Yeoman generator will present you with the wizard by asking questions about the solution to be created.

Create SPFx Solution 
 
Solution Name

Hit enter to have the default name (spfx-react-search in this case) or type in any other name for your solution.
Selected choice - Hit enter
 
Target for component

Here, we can select the target environment where we are planning to deploy the client web part i.e. SharePoint Online or SharePoint OnPremise (SharePoint 2016 onwards).
Selected choice - SharePoint Online only (latest)
 
Place of files

We may choose to use the same folder or create a subfolder for our solution.
Selected choice - Same folder
 
Deployment option

Selecting Y will allow the app to deployed instantly to all sites and will be accessible everywhere.
Selected choice - N (install on each site explicitly)
 
Type of client-side component to create

We can choose to create client-side webpart or an extension. Choose the webpart option.
Selected choice - WebPart
 
Web part name

Hit enter to select the default name or type in any other name.
Selected choice - SearchResultsViewer
 
Web part description

Hit enter to select the default description or type in any other value.
Selected choice - Retrieve search results using the REST API.
 
Framework to use

Select any JavaScript framework to develop the component. Available choices are (No JavaScript Framework, React, and Knockout)
Selected choice - React

Model for Search Results

We will define an interface for representing the SharePoint search results.

Add an interface (ISPSearchResult.ts) representing the SharePoint search result.
  1. export interface ISPSearchResult  
  2. {  
  3.     Title: string;  
  4.     Description: string;  
  5.     Url: string  
  6. }  
React JS acts on the state change. Let us add state to our solution (ISearchResultsViewerState.ts)
  1. import {ISPSearchResult} from './ISPSearchResult';  
  2.   
  3. export interface ISearchResultsViewerState {  
  4.     status: string;  
  5.     searchText: string;  
  6.     items: ISPSearchResult[];  
  7. }  
Model for Search Results 
 
Configure SearchResultsViewer.tsx for this state.

Configure SearchResultsViewer.tsx 

Add Controls to WebPart

Open SearchResultsViewer.tsx under “\src\webparts\searchResultsViewer\components\” folder.

Modify the Render method to include the required controls.
 
Text field
  1. // Import Textfield component   
  2. import { TextField } from 'office-ui-fabric-react/lib/TextField';  
  3. import './TextField.Examples.scss';  
  4.   
  5. <TextField     
  6.     required={true}     
  7.     name="txtSearchText"     
  8.     placeholder="Search..."    
  9.     value={this.state.searchText}    
  10.     onChanged={e => this.setState({ searchText: e })}    
  11. />  
Button
  1. // Import Button component    
  2. import { IButtonProps, DefaultButton } from 'office-ui-fabric-react/lib/Button';  
  3.   
  4. <DefaultButton    
  5.      data-automation-id="search"    
  6.      target="_blank"    
  7.      title="Search"    
  8.      onClick={this._searchClicked}    
  9.      >    
  10.      Search    
  11. </DefaultButton>  
  12.   
  13. private _searchClicked(): void {  
  14. }  
List
  1. // Import List component  
  2. import { FocusZone, FocusZoneDirection } from 'office-ui-fabric-react/lib/FocusZone';  
  3. import { List } from 'office-ui-fabric-react/lib/List';  
  4.   
  5. // Import Link component  
  6. import { Link } from 'office-ui-fabric-react/lib/Link';  
  7.   
  8. <FocusZone direction={FocusZoneDirection.vertical}>  
  9.     <div className="ms-ListGhostingExample-container" data-is-scrollable={true}>  
  10.          <List items={this.state.items} onRenderCell={this._onRenderCell} />  
  11.     </div>  
  12. </FocusZone>  
  13.   
  14. private _onRenderCell(item: ISPSearchResult, index: number, isScrolling: boolean): JSX.Element {  
  15.     return (  
  16.       <div className="ms-ListGhostingExample-itemCell" data-is-focusable={true}>  
  17.         <div className="ms-ListGhostingExample-itemContent">  
  18.           <div className="ms-ListGhostingExample-itemName">  
  19.             <Link href={item.Url}>{item.Title}</Link>            
  20.           </div>  
  21.           <div className="ms-ListGhostingExample-itemName">{item.Description}</div>  
  22.           <p></p>  
  23.         </div>  
  24.       </div>  
  25.     );  
  26.   }  
In the command prompt type “gulp serve” to see the controls on webpart.
command prompt type “gulp serve” 
 
Implement service to retrieve search results
  1. Add a folder “services” to the solution.
  2. To the “services” folder, add a file SearchService.ts
  3. We will use the REST API to get the search results.
  1. import { SPHttpClient, SPHttpClientResponse } from '@microsoft/sp-http';  
  2. import { IWebPartContext } from '@microsoft/sp-webpart-base';  
  3. import { ISPSearchResult } from '../components/ISPSearchResult';  
  4. import { ISearchResults, ICells, ICellValue, ISearchResponse } from './ISearchService';  
  5. import { escape } from '@microsoft/sp-lodash-subset';  
  6.   
  7. export default class SearchService {  
  8.     constructor(private _context: IWebPartContext) {  
  9.     }  
  10.   
  11.     public getSearchResults(query: string): Promise<ISPSearchResult[]> {  
  12.           
  13.         let url: string = this._context.pageContext.web.absoluteUrl + "/_api/search/query?querytext='" + query + "'";  
  14.           
  15.         return new Promise<ISPSearchResult[]>((resolve, reject) => {  
  16.             // Do an Ajax call to receive the search results  
  17.             this._getSearchData(url).then((res: ISearchResults) => {  
  18.                 let searchResp: ISPSearchResult[] = [];  
  19.   
  20.                 // Check if there was an error  
  21.                 if (typeof res["odata.error"] !== "undefined") {  
  22.                     if (typeof res["odata.error"]["message"] !== "undefined") {  
  23.                         Promise.reject(res["odata.error"]["message"].value);  
  24.                         return;  
  25.                     }  
  26.                 }  
  27.   
  28.                 if (!this._isNull(res)) {  
  29.                     const fields: string = "Title,Path,Description";  
  30.   
  31.                     // Retrieve all the table rows  
  32.                     if (typeof res.PrimaryQueryResult.RelevantResults.Table !== 'undefined') {  
  33.                         if (typeof res.PrimaryQueryResult.RelevantResults.Table.Rows !== 'undefined') {  
  34.                             searchResp = this._setSearchResults(res.PrimaryQueryResult.RelevantResults.Table.Rows, fields);  
  35.                         }  
  36.                     }  
  37.                 }  
  38.   
  39.                 // Return the retrieved result set  
  40.                 resolve(searchResp);  
  41.             });  
  42.         });  
  43.     }  
  44.   
  45.      /** 
  46.      * Retrieve the results from the search API 
  47.      * 
  48.      * @param url 
  49.      */  
  50.     private _getSearchData(url: string): Promise<ISearchResults> {  
  51.         return this._context.spHttpClient.get(url, SPHttpClient.configurations.v1, {  
  52.             headers: {  
  53.                 'odata-version''3.0'  
  54.             }  
  55.         }).then((res: SPHttpClientResponse) => {  
  56.             return res.json();  
  57.         }).catch(error => {  
  58.             return Promise.reject(JSON.stringify(error));  
  59.         });  
  60.     }  
  61.   
  62.     /** 
  63.      * Set the current set of search results 
  64.      * 
  65.      * @param crntResults 
  66.      * @param fields 
  67.      */  
  68.     private _setSearchResults(crntResults: ICells[], fields: string): any[] {  
  69.         const temp: any[] = [];  
  70.   
  71.         if (crntResults.length > 0) {  
  72.             const flds: string[] = fields.toLowerCase().split(',');  
  73.   
  74.             crntResults.forEach((result) => {  
  75.                 // Create a temp value  
  76.                 var val: Object = {}  
  77.   
  78.                 result.Cells.forEach((cell: ICellValue) => {  
  79.                     if (flds.indexOf(cell.Key.toLowerCase()) !== -1) {  
  80.                         // Add key and value to temp value  
  81.                         val[cell.Key] = cell.Value;  
  82.                     }  
  83.                 });  
  84.   
  85.                 // Push this to the temp array  
  86.                 temp.push(val);  
  87.             });  
  88.         }  
  89.   
  90.         return temp;  
  91.     }  
  92.   
  93.     /** 
  94.      * Check if the value is null or undefined 
  95.      * 
  96.      * @param value 
  97.      */  
  98.     private _isNull(value: any): boolean {  
  99.         return value === null || typeof value === "undefined";  
  100.     }  
  101. }  
Test the WebPart
  1. On the command prompt, type “gulp serve”
  2. Open SharePoint site
  3. Navigate to /_layouts/15/workbench.aspx
  4. Add the webpart to page.
  5. Type the search text and verify the search results.
Test the WebPart
 
Troubleshooting

In some cases, SharePoint workbench (https://[tenant].sharepoint.com/_layouts/15/workbench.aspx) shows below error although “gulp serve” is running.

Troubleshooting 

Open below URL in the next tab of the browser. Accept the warning message.

https://localhost:4321/temp/manifests.js

Summary

Modern SharePoint does not have web part for displaying search results. However, using the search REST API the results can be achieved.