8

I am creating a Visualforce plugin that allows me to add data to the Account and Opportunity pages. The data I am adding easily follows the model of a SPA, so I decided to use AngularJS within my Visualforce plugin.

So far, everything worked fine when I was only using the Account page -- I was able to just add text/ng-template scripts to render my particular views in the application, and edit just one file.

Now, I am extending the plugin to work on the Opportunity Page, too; the front-facing portion of plugin literally requires the exact same code to render on both the opportunity and account page.

I attempted first to find some way to render a partial view in Apex so that I could just have all my display logic in one file; it wasn't ideal, and there didn't seem to be a way to do that.

Then, I tried using linking to StaticResource html files that contain my angular page templates, but for some reason (despite getting a url for them), I cannot link to them; I think Salesforce simply does not let me link to static resources in this manner.

How exactly can I use template files in order to not have to copy/paste all my code, once in a visualforce page for the AccountController, and once in my visualforce page for the OpportunityController?

5 Answers 5

8

I do alot of salesforce angular work.

I take it you're using a Router of some sort, be it the built in $router or Ui-Router.

Regardless of which one you use, when you're given a choice to include a template via url do this:

{
  templateURL: '/apex/NameOfTemplateVFPage'
} 

In your template partial, make sure to turn the sidebar, headers, and standard css OFF. And set the doctype to html-5.0

Lastly, if you're utilizing ngForce (shameless plug) when the lawyers get done pissing at each other, there will be an update that enables you to strip off the excess JS that Salesforce inserts into all vf pages when you're loading partials.

14
  • I was attempting to add a router, but resorted to using inline templates because I could not figure out how to properly lead to the router. So If I were to add a page, say, 'Foo', that I want to use as my template, the url to reference it is just /apex/Foo? Feb 21, 2014 at 19:06
  • I was hoping for an answer to be posted here that allows many templates to be kept in a single Salesforce resource so that a single GET will pull and cache the templates at the client side (and profiles don't have to be changed each time a template is added). Especially given mechanisms like <script type="test/ng-template" id="zippy.html">...</script> and ng-include. Are you saying that this is not possible on Salesforce?
    – Keith C
    Feb 21, 2014 at 19:12
  • I attempted to do this, but the content inside my partial didn't load and I just got a weird bar that has an 'x', the page name, and a few icons to disable development mode. Feb 21, 2014 at 19:37
  • 1
    @PhilBarresi hope you post this approach as an answer if it works; a Visualforce page per template doesn't seem a good way to go.
    – Keith C
    Feb 21, 2014 at 19:43
  • 1
    @Mugambo IMHO it is the best approach. The static resource can be a single ZIP file with many templates in it or you can use a static resource per template which is a bit easier for edits while you are developing.
    – Keith C
    Dec 4, 2015 at 8:50
3

What I did was define a global path to my static resources by doing this ahead of my javascript include files in the main page.

<script>window.RESOURCE_ROOT = "{!URLFOR($Resource.MyAngularApp, '')}";</script>

Then I could reference my templates with:

templateUrl: RESOURCE_ROOT+"/templates/mytemplate.html"

I know globals are bad but in this case it seemed to be ok since I really and truly needed a global variable.

2

You can use apex:composition to create the same front-facing UI code with different controllers; it also allows customizable sections using apex:insert. Also, apex:include lets you literally include one page inside another, which may be all you need. Finally, if all of that seems like overkill, just make an apex:component and include it within both pages that serve as the shells for the content (e.g. <c:dosomething withparent="{!account}"/>).

2

I recently did some angular work where I had lots of partial templates and didn't want to create a new visualforce page for each of them.

Instead I structured my app so that the main (master) view was inside a VF page. Everything else was included in a compressed static resource.

-MyAngularApp (static resource)
--app
---js
---templates
---lib
---css

The trick to get this to work is that you must set the HTML base tag in the head.

<base href="{!URLFOR($Resource.MyAngularApp, 'app/')}" />

Then you can set your routes like so:

$routeProvider.when('/', {
            templateUrl: 'templates/homeView.html',
            controller: 'HomeController'
        });

I'm sure there are some disadvantages to this approach (maybe browser support), but for my purposes it worked great.

3
  • Did you find any disadvantages thus far using this approach?
    – Sam
    Dec 4, 2015 at 9:06
  • 1
    @Mugambo nothing that really stood out as a show stopper. One limitation is that you cannot use the visualforce {!} binding notation in your static resources. IMO these really shouldn't be used in an angular app anyways, but there are some use cases that come to mind where it might be needed.
    – NSjonas
    Dec 4, 2015 at 23:28
  • Thanks that helped. I think incase we need some vf bindings then we can declare store them in some global vars on the main VF and refer them in the angular code.. Thanks again
    – Sam
    Dec 5, 2015 at 7:27
1

@KeithC,

So your path towards using a unified resource for your templates is to do something like this:

var myApp = angular.module('myApp', []);
myApp.run(function($templateCache) {
  $templateCache.put('templateId.html', 'This is the content of the template');
});

Once you've populated your template cache with your templates, you can access them in appropriate contexts via something like:

ng-include=" 'templateId.html' "

Note the single quotes inside the double quotes, You have to use that pattern, so that you can send a literal string to ng-include.

While the demo above shows the $templateCache service being populated in the .run() method of your app (presumably defined in Application.js or app.js) you can Dependency Inject your $templateCache into any angular controller / service / etc. You can then fire use the same $templateCache.put() method to add or manipulate templates.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .