For those of us still supporting sites with the standard view we've had for the last decade or so — what Microsoft now calls "classic experience" — modern experiences have been rolling out to Office 365 tenants since late 2016 and now to SharePoint 2016 with Feature Pack 2. These modern team site pages are quick, with an easy authoring experience and support for rich multimedia content in a simple interface.
Additionally, these new types of pages are automatically responsive on any device, in a browser or from within the SharePoint mobile app. With the foundation of modern web part hosting showing up since the new Feature Pack, we can build web parts that are cross-page and cross-platform compatible.
Note: Microsoft is not depreciating the "classic" experience, with both "classic" and "modern" coexisting side by side until further notice.
To enhance the modern experience and bring our own line-of-business data into our pages, we have the ability to create SPFx web parts ourselves, since they’re just client-side components that run only inside the context of a SharePoint page.
Since we want to pull in search results to our modern web part, we need to map out our “inputs” and “outputs” to make our tool a reality. By inputs, I mean how do our users normally search for content? What do they look for and what features do we give them to find that content quickly? This won't be a replacement for the Enterprise Search Center but, instead, a way to find things quickly on the current page or navigate to the search center for more capabilities.
To help with the outputs, in the web part Property pane, I implemented three controls to manage the web part so it can be dropped on any page in any tenant and be customized as desired.
On the display portion of the web part itself, I added a standard search box/button that we're all used to, but I also added a checkbox so users could add the wildcard character (*) to the end of their keywords in case they wanted more results than just matching the word. SharePoint out of the box supports this capability like most enterprise search engines; you just have to know about it and add it yourself in the search box.
For the outputs portion, what we're talking about is what will look best for showing our users the SharePoint items returned from their queries. Since we're building the outputs on the page ourselves, we can style them any way we want. In this case, I wanted the results to look exactly as standard Display Templates, so I imported the SharePoint 'searchv15.css' Style Sheet and modeled the output HTML to use the same classes and hierarchy.
Since there are plenty of articles on getting started with SPFx development, we won’t cover that here, but the framework to build our web part is easy to set up. If you need a quick tutorial on getting your first SPFx web parts up and running, explore the guides at the end of this article.
The other big part of this web part, the elephant in the room, if you will, is the Search REST API. While I've built hundreds of solutions using all of the different SharePoint APIs, when it comes to search, I always need to pull up the reference documents since the syntax is so different from standard List/Library REST calls for doing the same things.
To help visualize this data, I always turn to the SharePoint Search Query Tool. This allows me to try multiple queries and refine how I want to pull the data before I even start coding.
After determining exactly how I wanted to make the search queries and which search managed properties I wanted to display, I went through the Yeoman steps of building a new project and then launched it in Visual Studio Code. The following section details the code I implemented with TypeScript and SASS to make it all work.
For lines 1–39 above, most of this top section is standard boilerplate code that’s generated from Yeoman, referencing SharePoint libraries. To get started, I added in lines 8–12 to bring in imports for making secure SharePoint REST calls. Then, on line 22, we added this import for setting up the special OData version needed to support a bug in the Search API (more on this later). Line 23 was added so that I could later load custom SharePoint objects/URLs into the code. And finally, lines 25–37 were built as interface classes for the search results that were returned to us.
Line 39 begins our processing web part code that standard SPFx web parts start with. Notice that on lines 40–43, I added an on-initialization section that tells the SPComponentLoader we imported above to call its load CSS method, passing in the relative reference to the core searchv15.css file in SharePoint. This ensures the styling always matches the default search experience users see.
Lines 46–66 show the HTML first loaded by the web part as it's added to a page — in this case, without Angular or React — with a results Div element on line 61 I'll use later to write the results into. On line 67, and the subsequent lines 70–75, is the _setButtonEventHandlers() function that adds a click event listener on the Search button. Inside the listener is a reference to the asynchronous _renderListAsync() function that will make the search queries for us.
Starting with lines 77–93, our _renderListAsync() function checks that a keyword was entered before the button click and, if so, calls the _getSearchResults() function on line 82. This is our first function that doesn't return void, instead sending back a promise of type ISearchResults so we can process the data on the return. In this function we do the core lookup logic, running from lines 95–129.
Lines 98–102 format our search keyword with or without the ending wildcard. From lines 115–121, the code sets up a SharePoint API call (spHttpClient) by building our target URL on line 116, implements the OData version override on lines 117–120 needed to compensate for the Search API bug, and finally returns our response object as JSON back up to line 83, where it was called from. Back up in our _renderListAsync() function, lines 83–88 reformat the JSON into ISearchResult objects so we can then pass the results to our final _renderList(items: ISearchResult) function.
In our _renderList() function, running from line 131–199, we take the search results by looping through each ISearchResult on line 136 to process them. On line 132, we set up a reference to the Div element to push our search results into. Then, for lines 137–153, we gather information from each looped result, calling our reusable _getValueByKey(itmKey: string,srchRow: any) & _buildNiceDate(strIsoDate: string) functions on lines 201–219.
Starting on line 155, we begin putting together the raw HTML of a standard search result that we'll inject into our target Div element mentioned above, ending on line 196 for each item returned.
Finally, on line 198, after looping through all our results, we take the collated HTML and inject it into the Div's HTML property for showing. This asynchronous and delegated approach allows us to query as often as we like without having to do a standard postback to the page like normal SharePoint Search does, and we reuse the same results area over and over.
This last section running from line 225–261 is our boilerplate Property pane section, where we define controls and values to help control the output of the SPFx web part. In this case, on lines 239–242, I added another PropertyPaneTextField for the Enterprise Search URL. And on lines 243–253, I inserted a PropertyPaneDropdown for admins to decide how many results to show on a page.
So at the end of all this, we now have a fully functioning modern web part that can be added into any classic or modern page, in almost any modern version of SharePoint, to give our users the capability to do quick searches without leaving the page they’re on. This web part could even be extended in the Property pane with more controls to allow for selecting resulting sources to target or even default keywords to fire on page load. The possibilities are almost endless, thanks to enterprise search and the SharePoint Framework.