The registerView() method was added to the layout component in 2018.2. It lets you define a view and template, so that it can be used in any template in your theme. We use it for the keyword search box and its button, but it can be used for any simple view you want to use.

The essence of the method is that it makes it much easier to add views arbitrarily to templates. By registering a view to a component, you make that view available anywhere that component is available. After you run the code, you just need to modify the theme templates of the places you want the view to appear and that's it.

Keep in mind that it shouldn’t be used for every view: it should only be used for views that are ‘self-contained’, ie don’t rely (or, at least, too heavily) on context or model data. It should also be used with designers in mind: the idea is that once a developer has created a view as a global one, the designer is then free to use it in any template they need.

Use Cases

When we implemented this functionality, we anticipated two broad use cases:

  1. Make it easier to move generic SuiteCommerce elements around in your theme
  2. In customer (your) extensions, you can register a child view and allow that child view to render in a companion theme, specifically in the locations you want

How We Use It

Regarding point one, you should have read in the 2018.2 release notes that we made enhancements to the site search bar. Well, the enhancements make use of this new method: by decoupling the site search elements (the button to reveal it, and the keyword input box), we enable designers to put them where they want with minimum fuss.

Indeed, if you take a look at SiteSearch > JavaScript > SiteSearch.js (SCA 2018.2+ only), you'll see a prime example of how to use registerView().

We use it twice to register two new views to the layout component. Accordingly, wherever the layout component is available (which is globally) you can modify templates (of an extension or a theme) and add data-view="SiteSearch" or data-view="SiteSearch.Button" to generate a keyword search input or site search button respectively.

For example, if you take a look at our base theme in Header > Templates > header.tpl you will see us including it.

For documentation specifically on the search elements, see Customize Site Search Elements.

How You Can Use It

What this means is that if you suddenly find yourself wanting to add site search to some arbitrary view/template combo you're working on, you can just do something like this:

<section class="mycoolmodule-info-card">
    <span class="mycoolmodule-info-card-content">{{message}}</span>
    <p>Testing placing the search bar in the header</p>
    <div data-view="SiteSearch.Button"></div>
    <div data-view="SiteSearch"></div>
</section>

Which, if we place just below the logo, looks like this:

A screenshot of an example web store. It shows the button for the site search appearing just below the site's logo.

Clicking the icon opens the search box for input.

Very simple views can be added via an entry point file in an extension. For example, if I want a "Hello World!" type message to be available anywhere, then I could do something like this:

define('HelloWorld'
, [
    'HelloWorld.View.js'
  ]
, function
  (
    HelloWorldView
  )
{
  'use strict';

  return {
    mountToApp: function mountToApp (container)
    {
      var Layout = container.getComponent('Layout');

      if (Layout)
      {
        Layout.registerView('HelloWorld', function HelloWorld ()
        {
          return new HelloWorldView
        });
      }
    }
  }
})

That's my entry point file in my extension.

Then I just need to create a view and template. My view looks like this:

define('HelloWorld.View'
, [
    'helloworld.tpl'
  ]
, function
  (
    helloworld_tpl
  )
{
  'use strict'

  return Backbone.View.extend({
    template: helloworld_tpl
  })
});

And then my template:

<p>Hello World!<p>

Finally, to render it, just edit any template and put the following in it:

<div data-view="HelloWorld"></div>

Of course, if you want to add some complexity to it, you can pass it options in the constructor, such as the container object.

And that’s it! You’ve just created a view that can be used anywhere!

Why Would I Use This Instead of addChildView()?

If you are a single-site developer, you may not need to worry about it too much. However, the prime benefit is that when it comes to customizing the look-and-feel of your site, it creates a clean separation between your JavaScript and your templates.

Let's take an example: Dan the developer has created a widget using addChildView() and has written it so that it renders on the home page. Denise the designer adds a new element in a home page template so that it shows. They push up their changes and activate them. Some time later, the business wants to change its location: Dan must now change the JavaScript so that it renders somewhere else, and Denise must update the theme. Again, both the JavaScript and theme must be uploaded and activated again.

However, if they use registerView() instead, Denise is the only person who must make a change in order to change its location.

There's also a second benefit: if you're an extension developer then you can ship your extension without forcing your functionality to appear in specific places. Or, to put it another way, customers are not restricted to the specific places you define in your extension; nor do they need to make changes to their JavaScript files to use it. All they would need to do is update their theme's templates!

To be clear: we're not saying that this is a replacement for addChildView(), instead it complements it by providing flexibility in situations where addChildView() can't.

  • addChildView() — I, the JavaScript developer, know exactly where this child view should go, and it's unlikely to move (eg because it is context-specific)
  • registerView() — this view could go in a number of places, sometimes more than one, and it's generic enough to work almost anywhere, so I'm going to leave that up to the theme developer

So, for example, we certainly wouldn't use registerView() for a checkout-only child view.

Can I Use Model Data?

Let's say you have a situation where you've used a service to get some data from NetSuite and you want to put that into the view and also have it available globally, will this work?

The answer is: only if that service has been called at the time.

Typically, we anticipate that you will use this method in an entry point file. These files are called when the modules are first loaded. Models, however, are typically only created when a particularly route has been hit (ie a shopper visits a particular page). If the user has hit that page and you then, at the point of creating your model, register the view then you can pass it along.

It is possible to delay the rendering of a view by performing the fetch in the view file and then only triggering showContent() once it promise resolves. That would be fine.

Generally speaking, however, this is not what we had in mind when creating this functionality.

Can I Use the Plugin Container To Dynamically Generate the View Placeholders?

This was a question I had and so I asked a colleague who worked on it if it would make her sad if I did this and she said that it definitely would.

I know what you're thinking: the PluginContainer module is super cool functionality from yesteryear that lets us inject HTML into templates from a JavaScript. Its power is that it lets us run some jQuery before/after compilation or before/after rendering a view. Specifically in our use cases, we could potentially use it to modify a templates' HTML to add in a container for our new child view without having to touch the theme at all.

However, as the developer pointed out to me: the point of this registerView() is that it is a service to theme development. If you're going to use something like this, then you're better off using addChildView(). Furthermore, as the plugin container functionality is not part of the extensibility API, we can't really guarantee its stability or availability going forward. It's risky to use, especially in something like an extension.