Handlebars templating and dynamic data: cool improvements for the react-search-refiners SPFx sample!

Handlebars templating and dynamic data: cool improvements for the react-search-refiners SPFx sample!


Recently, I’ve made some cool improvements to the reach-search-refiners SPFx sample. In fact, these features are directly inspired from a customer project where the goal was to help end users to build their own search experience according to their specific site requirements. To simplify upgrades and maintenance, the other objective was also to deploy a common set of search-related SharePoint Framework Web Parts allowing multiple UIs across organization sites depending on the context and managed either by IT (external templates) or by users themselves (inline templates). Sounds like good old ones SharePoint display templates, uh? This is basically the same concept so yes, no revolution here.

Templates with Handlebars

Handlebars is a popular JavaScript templating engine. Unlike React, Handlebars does not really concentrate on the application business logic aspect, but much more on the « rendering » things. Because the framework is very light, it is fairly easy to integrate in a JavaScript application.

First of all, I would like to give all the credit to Simon Pierre Plante for his awesome Content Query Web Part SPFx sample. The original idea and most of the code is reused from this sample, with only few modifications, listed above:

  • Reused the dedicated PropertyPaneTextDialog SPFx property control as is. This control allows to display the content of an HTML template (or other) in an Office UI Fabric dialog window using the Ace editor. Then it binds the template content to a Web Part property so you can edit it dynamically through the UI using the « Save » button. This control is pretty independent so you can reuse it easily in your solutions as well.

  • Reused the gulp configuration file (gulpfile.js) to get the Handlebars library work with SPFx and Webpack (seemed to be not straightforward at all).
  • Created a dedicated TemplateService class to handle the Handlebars integration and templating operations (register helpers and so on).
  • Created a dedicated React placeholder control SearchResultsTemplate.tsx to display the template content as raw HTML.

Use predefined styles or create your own template

For now, I’ve created two predefined layouts for search results: the list one and the tiles one. Maybe more will come later this year (carousel, etc.).

Of course, if you’re not satisfied with these two ones, you can just create your own. You can start from a default « blank » template or start from a predefined one. Also, you can specify an external template. This feature is especially useful if you want to provide tenant wide templates managed by IT. Notice that the external templates must reside on the same Office 365 tenant (ex a SharePoint site) and be accessible (read permission minimum) by the current user.

Create your own helpers

Creating your own helper is fairly easy as well. Just add it to the BaseTemplateService.ts file and you’ll be fine. You can pass the current search result item as a parameter like the example below:

By default, the 188 helpers from handlebars-helpers are also included.

Office UI Fabric integration challenge

Because the rendering is made using Handlebars templates instead of React controls, we can’t benefit of the Office UI Fabric React components anymore so we have to find a way to provide a flexible way to create templates that fit nicely in the Office/SharePoint UI. Even though I would be able to use a completely different UI framework to build the template, like Material IO or Semantic UI or even Bootstrap, I wanted to stick to the Office theme. As the SPFx official documentation points out, we can’t really use the Office UI Fabric core components and CSS classes directly in our Web Parts because it will cause conflicts due to global styles. To do this, I’ve found of a way to consume Fabric JS components in a « safe » way, The solution looks like this.

Like I did for the PnP Starter Intranet Bootstrap framework integration, I just encapsulated the default Office UI Fabric components SASS files into a root template CSS container class. As a result, as soon as templates use this root class, they won’t conflicts with the rest of the UI. It is enough to be able to use the grid system and some very useful components like the list one.

Dynamic data

Another cool feature is the replacement of the event aggregator with the new dynamic data feature coming with the 1.5.1 SPFx version (betaplus). This feature is used to send/receive information to/from SPFx components on the same page. It can be implemented by a Web Part but also by an extension. Typically, in this sample, the search box Web Part acts as data source for the search results Web Part to send the query input value entered by the user but, an extension is also defined as a data source to get the « q » query string parameter used by the search box or by the search result Web Parts. This data source is especially useful when building complete intranet search experiences with a home page containing a search box redirecting to another page, prepoluated with the search query.

Be careful with SPFx extensions

When you refresh the page, the data source may be unavailable at the time you want to get values from it (for instance in the onInit() or render() methods). This is typically the case when the source comes from an extension. Unfortunately, the data source id changes every time the page is refreshed (as I’ve seen for extension) so we can’t guarantee it will be available when calling the tryGetSource() method. To be able to « wait » for a particular source, I used the componentId as the unique indentifer instead of the generated source id. Unless the source id, the component id property doesn’t change during a page refresh. This value is saved in the Web Part property bag when the user updates the property pane:

Then, I used the « registerAvailableSourcesChanged » event listener to « reconnect » the right source in the Web Part property pane. This event fires every time a new source is available on the page so we can check if the componentId is matching and re-render the component after reading the correct value.

I also keep a reference to the DOM element due to an issue when the page mode is switched from display to edit mode and vice versa. Sometimes, this value turns to null for an unknown reason. Probably not the best way to solve this, but I don’t have any clue to resolve this yet.

I hope these new features will be useful in your organisation.

Feel free to share your templates!

+ There are no comments

Add yours

This site uses Akismet to reduce spam. Learn how your comment data is processed.