Effectively working with AngularJS in and out of Salesforce

So we were creating an AngularJS app as the front-end for a Salesforce community. We used VisualForce remoting to get the data while in Salesforce. But we didn’t want to muck around having to constantly deploy assets into Salesforce during development. So, we came up with a pretty effective way of allowing front-end designers/dev’s to work outside of salesforce without having to modify the app when it was plugged into salesforce. Here’s how…

Solution overview

Here’s a really basic breakdown of the ways of working

  • single VisualForce page with single resource bundle
  • minified/bundled assets in production environment including cached html partials
  • expanded assets in development environment for easy debugging
  • node express to intercept remoting requests while in development environment
  • Modified version of certain bits of ngForce, an excellent library for integrating salesforce and angularjs.

The upshot of this solution is that we can work on a flat version of the site during development and inject realistic data into the app so we can have confidence it will work seamlessly (ish) when in the salesforce environment. I’ll take each of these points below…

Single VisualForce page

There’s only one visualforce page for the entire app. All other partials are cached using angularjs’s templatecache mechanism. This means that as long as the required variables are set in the visualforce page, they can be injected into any other components of the app that require them.

The visualforce page has a few properties set on the ‘apex:page’ tag, so that no salesforce stuff gets injected in. We want to be able to handle everything ourselves.

<apex:page controller="ttb_sc_dashController" extensions="ttb_sc_journeyController,ttb_sc_listController,ttb_sc_detailController" showHeader="false" sidebar="false" docType="html-5.0" standardStylesheets="false" applyHtmlTag="false" applyBodyTag="false">

This tag determines which is the main apex controller, which other controllers can be used by it, the docType, and a few other attributes that force salesforce not to inject anything.

In the production environment, we used grunt-angular-templates, which is a grunt task for automatically adding templates into the angular template cache. While in dev environment, the html partials aren’t added to the template cache, so the actual templates are served up. This makes it easier/quicker to update local files while developing. Here’s a snippet of the gruntfile…

ngtemplates: {
  dist: {
    options: {
      module: 'app',
      htmlmin: '<%= htmlmin.dist.options %>',
      usemin: 'scripts/scripts.js'
    cwd: '<%= yeoman.app %>',
    src: '{,**/}*.html',
    //this gets added to scripts.js during the build task
    dest: '.tmp/templateCache.js'

So this adds the templates into the main ‘app’ module, available throughout the app. All cached templates are added to the main ‘scripts.js’ file along with all the other app files. The other bits (cwd, src) just define where to get the html files from.

VisualForce variables

In the visualforce page, below the main script files, we added an angular constant to the core module of the app. This means that any component of the app that requires these variables simply has to use angular’s DI mechanism to get access to them. Here’s a little snippet…

<!--the main script.js file for the entire app, containing the app.core module-->
<script type="text/javascript" src="{!URLFOR($Resource.mysupport_assets, '/scripts/scripts.js')}"></script>
<script type="text/javascript">
  /* <![CDATA[ */
    var sitePrefix = '{!$Site.Prefix}';
    if(sitePrefix === '') sitePrefix = '/apex';
    //appConfig constant to be used throughout the app.
    angular.module('app.core').constant('appConfig', {
      userId: '{!userId}',
      sessionId: '{!$Api.Session_ID}',
      communityId: '{!networkId}',
      sitePrefix: sitePrefix,
      resourceUrl: '{!URLFOR($Resource.mysupport_assets)}',
      actions: {
        global: {
          account: '{!$RemoteAction.ttb_sc_dashController.getAccountInfo}'
        dashboard: {
          subscriptions: '{!$RemoteAction.ttb_sc_dashController.getSubscriptionsData}',
          overview: '{!$RemoteAction.ttb_sc_dashController.getOverviewData}'
  /* ]]> */

So, as can be seen, there are a load of variables that are set including the userId, sessionId, communityId etc. The url to the resource bundle is also defined here, meaning that assets (such as images) can be correctly referenced from html partials. The other main point of interest is the ‘actions’ object. This contains a collection of apex controller action names that can be used by the app. So instead of having to hardcode the action names in the angular app, they can be accessed through the ‘actions’ object.

For working outside of salesforce, we have a single ‘index.html’ page that is basically a duplicate of the VisualForce page, containing the same ‘appConfig’ variable. In this file, all scripts, stylesheets are referenced in full, not combined or minified. Since the page itself is fairly small, this doesn’t present too much of an issue.

Minified assets in production, expanded in dev

This is a pretty common concept but we found a couple of really useful node packages to help during development.

All these packages use comment blocks inside html or js files and are used to automatically inject and process other files. For an angular app, the number of js files does become large, and constantly remembering to include or rename a js file when one is created or changed is a real pain.

Wiredep and usemin

Here’s a simple code snippet to hopefully explain. Below is the index.html page used in our dev environment.

<!-- build:js(.) scripts/vendor.js -->
<!-- bower:js -->
  <!--all bower components automatically injected here-->
<!-- endbower -->
  <!--any files that should be included in the combined/minified vendor.js script but aren't bower components go here-->
<!-- endbuild -->

So there is the outer ‘build’ comment used by the usemin package. This says that anything in between the opening and closing build comments needs to be combined and minified and saved as the file ‘scripts/vendor.js’. The inner ‘bower:js’ comment auto injects all bower components (unless they’ve been excluded).


Another grunt package for auto-injecting files, again using comment blocks. Here’s a snippet of the ‘index.html’ page…

<!-- build:js({.tmp,./}) scripts/scripts.js -->
<!-- include: "type" : "js", "files" : "<%= yeoman.scripts  %>"  -->
<!-- files auto-injected here -->
<!-- /include -->
<!-- endbuild -->

We’re using the usemin task again here to combine and minify all files in between the ‘build’ opening and closing comments. The inner ‘include’ comment determines that the files to inject are javascript files. The ‘files’ property specifies the list of files to use. This is set from the variable that exists in the grunt file. Here it is…

scripts: [

So this loads the main module file first, then any sub-modules, then any js files and ignores any 3rd party libraries and spec files. This means there aren’t any loading order conflicts.

One really big advantage here is that during dev, all the js files exist on their own, meaning you can easily navigate to them and update them using chrome dev tools.

Node express

We use the grunt express task in the dev environment to simulate calls that would be made to the VisualForce remoting manager. Using express, remote calls can be intercepted and dummy data returned. This means that pages can be created around actual data, making development much easier.

Once installed, you created a ‘server.js’ file that is used to intercept and process requests. Here’s the js snippet from the gruntfile…

express: {
  options: {
    port: 9000,
    hostname: '*'
  livereload: {
    options: {
      server: path.resolve('./server'),
      serverreload: false, //this is important
      bases: [
        path.resolve(__dirname, appConfig.app),

The express:livereload task is run during the default grunt task. Another grunt task (grunt-open) can be used to automatically open a browser at the specified url and port. The grunt express task maps the location of the node server, and resolves any paths that are required by the app, such as the .tmp and bower_components folders. The .tmp folder is created by the ‘useminPrepare’ task which is an intermediary step used by the ‘usemin’ task during development.

Next, its a case of actually creating the node server file. I’m not going into detail here about node, but an example of intercepting an http request is below…

app.get('/RemoteAction/ttb_sc_dashController/getAccountInfo', function (req, res) {
var urlParts = url.parse(req.url, true);
var query = urlParts.query;
var data;
data = fs.readFileSync('test/data/account-'+ query.accId + '.json', 'utf-8');
  res.setHeader('Content-Type', 'application/json');
}, 500);

This snippet shows what happens when an http request is made to ‘/RemoteAction/ttb_sc_dashController/getAccountInfo’. When that request is made, some test (but realistic) data is obtained from the file system and returned as json.

An important aspect here is the way the angular app is constructed. When the app runs, a test is made to determine if its running in a prod or dev environment. This is handled by the ngForce-visualForceRemoting provider.


This is a great library for interacting with salesforce from angular. For the purposes of our app however, we didn’t need all the functionality it offers. So we modified one of the providers ‘ngForce-visualForceRemoting.’ The main modification was the addition of a ‘debug’ variable, which is set to false by default. It is set to true, however, when the ‘Visualforce’ javascript object doesn’t exist on the page. In the dev environment, this is the case. So, if the app is running in the dev environment, instead of making a call to the Visualforce remoting manager, a simple request is made using angular’s $http service. This can then be intercepted by the express server.


This solution means that there is minimal duplication of effort or code, real data can be used in a development environment and confidence is high that when integrating into salesforce, the app will work. This solution also means that Salesforce devs, angularjs dev and other designers aren’t stepping on each others toes, all trying to work on the same VisualForce pages.

Leave a Reply