Build a Testimonials Module: Part 1

We had a lot of speakers at SuiteWorld 2016 talking about SuiteCommerce Advanced. You may remember that I wrote an article recommending five breakout sessions, and that one of them was Pablo Zignani's on building a testimonials module. If you couldn't make it then don't worry as I'm going to take you through the same process of adding a testimonials module.

This article makes for a great post-tutorial project as it will put to work a lot of the new skills you've learned, focussing them into a useful module that you can actually use on your site. I'm not going to go into the same level of detail like I did in the first tutorial, but I won't skip important parts. Thus, while this is a good module for beginners, I expect you to have basic experience.

As is it contains a fair amount of detail, this tutorial is split up across multiple parts. The link to the next part is at the bottom of the page.

NOTE — you must be using Mont Blanc or newer to complete this tutorial.

What are Testimonials?

Testimonials are recommendations. But rather than simply reviewing a particular product, they are like reviews of the entire site, talking about your service and a shopper's overall experience dealing with your brand. Pablo has identified three core user stories that are at play here:

  1. As a shopper, I want to leave my opinion of the site so it can be evaluated and displayed on the site
  2. As a shopper, I want to see other people's opinion of the site so that I can evaluate if I should buy from it
  3. As a store manager I want to moderate testimonials so that only opinions that are appropriate are shown on the site

It's going to look like this when we're doing:

In this context, an ideal testimonial for us would capture the following data:

  • Name of referee
  • Title/summary of testimonial
  • Testimonial text
  • Submission date
  • Approval status
  • Numerical rating (out of 5)

We're going to display these prominently on the homepage, along with a link to the form so that the shopper can submit their own.

Prepare the Record

We're going to need a new custom record in the backend to store our data.

In the backend, go to Customization > Lists, Records & Fields > Record Types > New. In the fields, add the following values:

  • Name — Testimonial
  • ID — _testimonial
  • Access Type — No Permission Required

All other fields can be left to the defaults. Click Save. When the page refreshes, add the following fields:

NameIDTypeList/RecordShow in List
Text_t_textLong Text No
Rating_t_ratingInteger Number Yes
Creation Date_t_creation_dateDate/Time Yes
Status_t_statusList RecordTestimonial StatusYes
Customer Name_t_entity_nameFree-Form Text Yes


For the status field, you'll need to add a custom list. You can do that while you're setting up the field. Add three values: Submitted, Approved and Rejected. You'll also need to go to the Validation & Defaulting tab and select Submitted from the Default Selection dropdown; this means that all newly created testimonials will automatically be assigned the correct status.

Similarly for the field for the creation date, go to the Validation & Defaulting tab and select Current Date/Time from the Dynamic Default dropdown; this will ensure the current date and time is automatically used when the testimonial is created.

This is all we need to do here, so let's move onto the module workspace.

Module Workspace and Router

You should now be able to set up your workspace for a new custom module. If not, refer to the instructions in the first part of the Artist module. To summarize:

  1. Create a new directory for the module in the folder you use for your site's customizations. I'm using Testimonials@1.0.0 for mine.
  2. Create sub-directories for all the different type of things our module needs: JavaScript, Sass, SuiteScript and Templates.
  3. Create ns.package.json. For now, just enter the required key/value for JavaScript; we'll add the others as we go.
  4. Update distro.json to register the new module and include the JavaScript in shopping.js.

We'll also need to set up an entry point file in the JavaScript directory. Let's keep it pretty standard. Create Testimonials.js and put the following in it:

, [
, function (
  'use strict';

  return {

    mountToApp: function(application) {
      return new Router(application);



We've put the router (which we haven't made yet) as a dependency. Then we've returned the router in our mountToApp, which is what plugs the module into the application.

Routes, Views and Templates

At this stage we have enough to create a "hello world" like thing that would print out a message in the developer console. Well, except it would through an error message because we didn't create a router. So, best not do that (yet).

What we're going to do now is add in a little complexity to the module. Let's do that in the form of routes (URL paths that determine what a user wants to do), views (things that get data ready for templates) and templates (things that the user sees when browsing).

Create a Router

Create Testimonials.Router.js in the JavaScript folder. In it, put the following:

, [
  , 'Backbone'
, function TestimonialsFormRouter(
  , Backbone
  'use strict';

  return Backbone.Router.extend({

    routes: {
      'testimonials/new': 'newTestimonial',
      'testimonials/new?*options': 'newTestimonial'

  , initialize: function initialize(application) {
      this.application = application;

  , newTestimonial: function newTestimonial() {
      var view = new FormView({
        application: this.application

      console.log('newTestimonial function called');



So we've got two dependencies: the view for the form (which doesn't exist yet) and Backbone. We need Backbone because we're going to add a Backbone router.

We've also added two routes, but they essentially perform the same function: to direct the user to the view that loads the form to submit a new testimonial. Routes work on the basis of mapping a URL to a function to call; in these instances, newTestimonial will be called. If you're wondering why we have the second route, this is in case additional GET parameters are tacked on to the end; even if they are not used by the module, we still want the user to be routed to the view for the form.

Next we initialize the router. We need to keep a copy of application to pass to parent views. It ensures they know the application and layout of where they're being appended to.

Finally, we add the function that instantiates the view for a new testimonial. We make the view render and append itself on the application's layout. This behavior is defined in suitecommerce > BackboneExtras > Backbone.View.js, which calls ApplicationSkeleton.Layout.js in ApplicationSkeleton.

Create a View

Views help translate your data into stuff your users can see, and also listen out for events and perform actions accordingly. When we use templates for our final pages, we need some way to connect them to the application so that we can manipulate the data and fill in the blanks on the frontend.

Create Testimonials.Form.View.js in the JavaScript directory. Put the following in it:

, [
  , 'Backbone'
, function TestimonialsFormView(
  , Backbone
  'use strict';

  return Backbone.View.extend({

    template: testimonialsFormTpl

  , getContext: function getContext() {
      return {
        example: 'Example text'


We have two dependencies: a template that will contain the form (which hasn't been made yet) and Backbone, as we'll be extending the standard view in Backbone.

In the code we return two properties: one that defines what template to use, and the other which is getContext. A view's context is an object containing values that you want to pass to the template. In our code we're just passing an example value so that we can test it. Later, we'll replace these with actual values.

Create a Template

The final part of this equation is the template. The template is the culmination of this other work: it is the final output and it is what the user sees when they visit your site. We use Handlebars for our templating. It is logicless templating, which means that unlike other templating systems, you do not put in conditional statements. With that said, it does some basic operations, but they are very limited and the templates only support a mixture of HTML and Handlebars syntax: no JavaScript will be evaluated in the template. In short: templates are for presentation only.

Create testimonials_form.tpl in the Templates folder and put the following in it:

    <h1>Testimonials Template: {{example}}</h1>


So all we're doing is creating a very basic template. In it we're translating the example property into the template using a Handlebars expression (the bit wrapped in double curly braces).

Update Package File, Deploy to Local Server

We've done all the hard work, now we just need to do some admin to get our changes up and running.

We've already updated the distribution file, but as you may have worked out, our package file isn't sufficient. After all, we created the module's ns.package.json only containing information on the JavaScript files. Update it to include values for templates. It should look like this:

  "gulp": {
    "javascript": [
    "templates": [


Ensure you've saved all your changes. Now head to the command line, change directory to the base SCA directory and run gulp local. This starts a lightweight server on your computer, which serves your local changes to your browser when you visit a modified version of your site's URL.

When the server has started, open your browser and then modify your site's URL along the lines of What this does is call all backend services and data from NetSuite, and then serve Backbone JavaScript, Handlebars templates and other resources from the local server. If you struggle to find the right URL, see running a local server in our documentation.

When you visit the page to create a new testimonial, you should be greeted by something like the following:

You'll also note that our console.log, which we put into router, also shows in the browser console.


At this point we've created the beginnings of a module. We've done the prep work for a new custom module and then plugged in a basic router, view and template. The router creates URL paths and associates them with functions, which in turn invoke views. These views connect to templates, which are what the user sees on the frontend.

At the moment, we've effectively created a "hello world" program. In part 2 we develop the module further by, for example, building a form and plugging it into the model.

If you're experiencing issues, or you think you've done something wrong, you can compare your files to mine by downloading