People working on laptops

Reference AngularJS Templates Within a Visualforce

by Nikita Verkhoshintcev

I‘m doing a lot of Angular + Salesforce work right now. When I’ve just started, I noticed that reference template file to a VisualForce page is tricky.

In this article, I’ll explain the three common ways of referencing AngularJS templates. I’ll also mention the drawbacks of each approach. Let’s say we have a simple Angular application with one custom directive.

angular
  .module('myApp', [])
  .directive('myDirective', myDirective);

function myDirective() {
  return {
    restrict: 'AE',
    replace: true,
    controllerAs: 'template',
    controller: function() {
      this.name = 'myDirective';
    },
    template: '<h1>Hello, my name is {{::template.name}}</h1>',
  };
}

<html ng-app="myApp">
  <head></head>
  <body>
    <my-directive></my-directive>
  </body>
</html>

Now we are using the inline template, but how could we link the external one (templateUrl: ???)? There are three common ways to do so:

  1. Template as an individual VisualForce page;
  2. Template as a VisualForce component;
  3. Template as a Static Resource.

1. Template as an individual VisualForce page (VFP)

To reference an Angular template in that way we need to create a separate VFP. Let’s name it MyDirective.page. We should use the HTML5 mode and place our code between apex:page tags.

<apex:page standardStylesheets="false" sidebar="false"  showHeader="false"
  applyBodyTag="false" applyHtmlTag="false" docType="html-5.0">
  <h1>Hello, my name is {{::template.name}}</h1>
</apex:page>

After that, we will be able to access our template using that’s page URL.

function myDirective() {
  return {
    restrict: 'AE',
    replace: true,
    controllerAs: 'template',
    controller: function() {
      this.name = 'myDirective';
    },
    templateUrl: '/apex/MyDirective',
  };
}

This approach has an own drawback: it might be slow. If you are planning to use multiple directives on the same page and manipulate them (e.g. filtering), it will take a while (up to 1s in my case) before new elements appear in the DOM.

2. Template as a VisualForce component

Apex Component is an another way to reference the Angular template. Let’s start by creating a new MyDirective.component with the following code inside.

<apex:component>
  <script type="text/ng-template" id="temp1">
    <h1>Hello, my name is {{::template.name}}</h1>
  </script>
</apex:component>

At first, to use this template, we need to include MyDirective.component to our VFP.

<body>
  <my-directive></my-directive>

  <c:MyDirective />
</body>

Then we can access it by its id.

function myDirective() {
  return {
    restrict: 'AE',
    replace: true,
    controllerAs: 'template',
    controller: function() {
      this.name = 'myDirective';
    },
    templateUrl: 'temp1',
  };
}

It has the same drawback as the VFP approach.

3. Template as a Static Resource

That was the first approach which I’ve tried when started to work with Salesforce. I already had my application on a local server, so it was easy to put my templates in Static Resource. Here we can use usual HTML file.

<h1>Hello, my name is {{::template.name}}</h1>

Let’s say our directives are inside the “/templates” directory. We need to create a new Static Resource in Salesforce with this directory. Then, the tricky part begins. There are two different solutions to accomplish that: via a global variable or base element.For the first solution, we need to define the path to our static resources in the global variable and use it to access our template.

function myDirective() {
  return {
    restrict: 'AE',
    replace: true,
    controllerAs: 'template',
    controller: function() {
      this.name = 'myDirective';
    },
    // `myDirective.html` - name of our template
    templateUrl: RESOURCE_ROOT + '/templates/MyDirective.html',
  };
}

<script>
  // `myTemplates` - name of our Static Resource.
  window.RESOURCE_ROOT = "{!URLFOR($Resource.myTemplates, '')}";
</script>

The second solution is element.

function myDirective() {
  return {
    restrict: 'AE',
    replace: true,
    controllerAs: 'template',
    controller: function() {
      this.name = 'myDirective';
    },
    // `myDirective.html` -
    template: '/templates/myDirective.html',
  };
}

<!-- `myTemplates` - name of our Static Resource. -->
<base href="{!URLFOR($Resource.myTemplates, '')}" />

Probably the biggest drawback is that you can’t use apex tags inside your templates with that approach. They just won’t be rendered. The second problem which I’ve found is that it’s harder to maintain. You need to update your Static Resource each time you want to change your template.

Conclusion

If you have to use tags inside your templates then either first or the second option is for you. The possible issue is that you may exceed the number of allowed tags in the production. I will suggest avoiding that.In the most of the cases, if you are using Angular, you don’t need inside the templates. Then, you should consider the third option.

Nikita Verkhoshintcev photo

Nikita Verkhoshintcev

Salesforce Freelance Developer / Solution Architect

I'm a senior freelance Salesforce and full-stack web developer based in Helsinki, Finland. I help companies, consulting agencies, and ISV partners build custom Salesforce applications.

Let's work together!

We help Salesforce customers and SI/ISV partners build custom Salesforce applications. Let's discuss how we can help you!

Contact us