Monday, August 31, 2015

Javascript AS a Visualforce page

There are several reasons a developer may want or need to have a their Javascript inside a Visualforce page.  Before explaining what those reasons may be, let just look at how you go about it.

Step 1 - Create your Javascript Page

The source below comes from a page I named "TomTestJS.page"

<apex:page showHeader="false" sidebar="false" standardStylesheets="false" 
    contentType="text/javascript">
 console.log('We are here!');
 document.write('This is the Javascript');
</apex:page>

Step 2 - Include your Javascript inside another page

Use <apex:includeScript value="{!$Page.TomTestJS" /> if the Javascript needs to be loaded at the top of the page and <script src="{!$Page.TomTestJS" /> if it needs to be loaded later, perhaps after some content has been rendered to the DOM.

The page below renders:

This is the page.
This is Javascript.

<apex:page showHeader="false" sidebar="false" standardStylesheets="false">

<p>This is the page.</p>

<script src="{!$Page.TomTestJS}" />

</apex:page>

The page above included the Javascript below some page content, that's why the document.write() output appeared below the HTML output.

If we instead did it the more traditional way using <apex:includeScript /> at the top of the page, the output renders:

This is the Javascript.
This is the page.

<apex:page showHeader="false" sidebar="false" standardStylesheets="false">
<apex:includeScript value="{!$Page.TomTestJS}" />
<p>This is the page.</p>

</apex:page>

I can think of a few reasons why programmers may want to do this.  Coincidentally, there the reasons I've wanted to do this.
  1. It's easier to track the source code in a repository if the files exist as independent entities and not part of a zip file.
  2. It's easier to see when a specific Javasript was last modified
  3. It allows the Visualforce preprocessor to resolve merge fields in the Javascript before being loaded into the browser (for assets that may exist in a static resource or as another page.
  4. It allows what would normally live inside <script /> tags inside a Visualforce page to exist independently, change independently, etc.
There are other reasons, too.  Today I had to port some Javascript and HTML from a media team into a sandbox and the team had taken liberties with their templates and other references that required "fixing" to work inside Salesforce.  Moving one of these Javascript files into a page and letting the preprocessor take care of a merge field to resolve the location of a static resource worked like a charm.

Thursday, August 27, 2015

How I got started with Angular and Visualforce

If you're reading this then you may be early-on in exploring AngularJS and wondering how you can get the W3Schools Angular Tutorial working inside Salesforce's Visualforce.

The tutorial's first page looks relatively straightforward, and with a simple closing tag for the <input> it will even pass Visualforce's compiler.

<!DOCTYPE html>
<html lang="en-US">
<script src="http://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js"></script>
<body>

    <div ng-app="">
  <p>Name : <input type="text" ng-model="name" /></p>
  <h1>Hello {{name}}</h1>
    </div>

</body>
</html>

If you tried this inside Visualforce you likely got the same output I did.   Instead of behaving like it does in the tutorial, Visualforce stubbornly displays "{{name}}."

Without delay, here's a Visualforce-ized version of the W3Schools tutorial.

<apex:page showHeader="false" sidebar="false" standardStylesheets="false">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.3.14/angular.min.js" />

<div ng-app="noop">
    <p>Input something in the input box:</p>
 
    <p>Name : <input type="text" ng-model="name" placeholder="Enter name here" /></p>
 
    <h1>Hello {{name}}</h1>
</div>

<script>
    var myAppModule = angular.module('noop', []);
</script>

</apex:page>

Visualforce requires ng-app to have a value to pass its own syntax checker.  If a value is passed to ng-app to get past Visualforce then that value is interpreted by Angular as a module used to bootstrap your page and must be defined.

In the example above I created a module called "noop" that literally does nothing but take-up space to make something else work.

Now my page behaved just like W3Schools said it should.

Having Googled around some more, I found multiple tutorials and videos introducing the neat things people have done with Visualforce and Angular, but all of them are too complicated for the absolute novice.  But the search pages did alert me that Salesforce is so geeked about the combination of Angular and Visualforce that they've created an app for the appexchange that installs angular, underbar, bootstrap, and several other JS libraries.  The app is called Angilar MP [sic].  The page gives instructions for how to install it into your org and includes some demo pages showing how to put more complicated examples together.

Since the app loads all those Javascript libraries into a staticresource we can re-write our application to look just a tad more Visualforce-like.

<apex:page showHeader="false" sidebar="false" standardStylesheets="false">
<apex:includeScript value="{!URLFOR($Resource.AngularMP, 'js/vendor/angular.1.0.6.min.js')}" />
<div ng-app="noop">
    <p>Input something in the input box:</p>
 
    <p>Name : <input type="text" ng-model="name" placeholder="Enter name here" /></p>
 
    <h1>Hello {{name}}</h1>
</div>

<script>
    var myAppModule = angular.module('noop', []);
</script>

</apex:page>

All it really does is replace the <script src="..." /> with <apex:includeScript value="..." /> and use the staticresource's Angular JS source.

PS If you're not already familiar with it, another of the cool resources include in the package is UnderscoreJS.  Lots of cool Javascript utility functions in there I wish I'd known were around years ago.  Regardless, they'll make my current pages easier to write.


Follow @TomGagne