Article
3 comments

First HandlebarsJS Web Part in SPFx

My last blog post focused on the general installation and configuration of Handlebars together with SPFx. I haven’t explained much on the code I used. Now it’s time to go more into detail how to deal with Handlebar templates and the overall code of the web part.

Location of templates in project

As I mentioned in a previous blog post, I like to separate the web part code from reusable components. Therefore I create a ‘templates’ folder in the ‘src’ directory.

Templatefolder in SPFx Project

The default web part comes already with a predefined content, so I gave it a try to convert it into a Handlebar template. The first step was to copy the content into a new file named ‘HelloWorld.hbs’. This file is not ready yet to be used as a template it needs to be slightly adopted.

Modify default content for Handlebars

Inside the content, two different type of elements needs differently. Those are the style definitions and the variable that render the data of the web part.

Style Definitions

The style definitions are prefixed with a $-sign and encapsulated in curly brackets.

<div class="${styles.helloWorld}">
 <div class="${styles.container}">

To make it replaceable for handlebar, remove the $-sign and encapsulate the variable with additional curly brackets.

<div class="{{styles.helloWorld}}">
  <div class="{{styles.container}}">

Now Handlebars can replace the style variable with the defined and prefixed value.

Variables

Like the style definitions, all variables are prefixed with the $-sign and encapsulated with single curly brackets.

<p class="ms-font-l ms-fontColor-white">${escape(this.properties.description)}</p>

Again replace the $-sign and double the curly brackets. Also, remove the ‘escape’ function that wraps the variable name. The result of this cleanup is the code below.

<p class="ms-font-l ms-fontColor-white">{{this.properties.description}}</p>

The ‘escape‘ function URL encode the string stored in the variable, and Handlebars will manage this. Especially this function can cause problems in future because browser sooner or later won’t support this deprecated JavaScript feature anymore. Instead, ‘encodeURI‘ or ‘encodeURIComponent‘ are better options.
Now that all the code have been cleaned up and prepared for Handlebars the code looks as shown below.

<div class="{{styles.helloWorld}}">
  <div class="{{styles.container}}">
    <div class="ms-Grid-row ms-bgColor-themeDark ms-fontColor-white {{styles.row}}">
      <div class="ms-Grid-col ms-u-lg10 ms-u-xl8 ms-u-xlPush2 ms-u-lgPush1">
        <span class="ms-font-xl ms-fontColor-white">Welcome to SharePoint!</span>
        <p class="ms-font-l ms-fontColor-white">Customize SharePoint experiences using Web Parts.</p>
        <p class="ms-font-l ms-fontColor-white">{{description}}</p>
        <a href="https://aka.ms/spfx" class="{{styles.button}}">
          <span class="{{styles.label}}">Learn more</span>
        </a>
      </div>
    </div>
  </div>
</div>

View on Github

Use template in web part

The final step is to make use of the template directly in the web part. Already defined in the file ‘config.json’ is the location of the external Handlebars library.
The import statement below makes sure that it loads during the initialization of the web part.

// Importing handlebars
import * as Handlebars from 'handlebars';

View on GitHub

The second thing is to load the template.

// load and precompile template
var HelloWorldTemplate = <HandlebarsTemplateDelegate>require('../../templates/HelloWorld.hbs');

View on GitHub

In general, require imports external files to your code. With the handlebar loader in place, Before the ‘require’ statement loads the file, the webpack Handlebar loader, precompiles the template and returns a HandlebarsTemplateDelegate. In other words, the template is a JavaScript function. You can pass the data to this function, and it returns an HTML string, ready to be added to the page.

Define data

Now we need to set data for the template. In this simple case, it is an easy task.

// bind data to template
var data = {
  styles: styles,
  description: this.properties.description
}

View on GitHub

The ‘data’ JSON object contains two attributes. The ‘style’ attribute directly pass the existing style information of the web part through. The ‘description’ attribute pass the web part description property to the template.

Generate HTML code from template

Now that the data we like to pass to the template are in place we can create the content of the web part.

// compile and add template
this.domElement.innerHTML = HelloWorldTemplate(data);

View on GitHub

The only thing left to do is to assign the resulting HTML to the innerHTML of the web part. Now the web part looks like this.

Final Handlebar Web Part

Final things to consider

Handlebars per se is a front end library and can be used directly in the browser. It might take some time and cause potentially slow loading of the web part. The recommended way it to precompile the templates and only bind data to the templates in your web part.
My SimpleStyle Style Guide application is based on Handlebars too to make it as open as possible and allows you to add additional frameworks. During the development of the first beta version, I had to deal with approximately 130 different templates. All those were loaded during the initial page load and took about two seconds.
After switching to precompiled templates, I reduced the loading time down to 50 to 60 milliseconds.
Even when it is not likely that you load that many templates in your web part, from my point of view, the handlebars web pack loader are the better way to do it.

Get this solution from GitHub

3 Comments

  1. Awesome Posts Stefan, thanks

    So if I wanted to store the templates in a sharepoint list so a savvy user could edit them themselves I would just replace the require(‘../../templates,,,, in the line below with the text of the template I read from a sharepoint list?

    var HelloWorldTemplate = require(‘../../templates

    I want to let users customize their own display form, maybe make different display forms too

    Reply

    • Thank you. Have you tried that this will work? The reason I’m asking is because you won’t have the template directly inside the web part code. The source is already pre-compiled and only the HandlebarJS remains.

      In your case you don’t have an actual template included. You just need to load the template via Ajax and compile it through HandlebarsJS during runtime in the client. This works but is slower than this pre-compilation. Possible? Yes definitely but you can skip actually the web pack loader too because it is not needed in your scenario.

      Reply

      • no , i haven’t tried it. i just checked ou you code today and was wondering if it would work.
        i understand now that the templates need to be compiled. we could create a webhook that listened for changes to the template template library, and then grabbed the new template source code, compiled it, and wrote it to a ‘compiled templates’ library.

        Reply

Leave a Reply

Required fields are marked *.


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