JavaScript for Visualforce Developers

Introduction
Since Lightning Components burst onto the scene, developers in the Salesforce ecosystem could be forgiven for assuming that Visualforce is now a legacy technology and that all new development should be carried out in Lightning. To my mind this is a little too black and white — while it’s likely that Lightning will get the lion’s share of attention from Salesforce, and Visualforce is likely to receive maintenance rather than new functionality, Visualforce is a mature technology that is in use in millions of applications around the world so it’s not going away any time soon.
There’s also no reason to suppose that companies are going to drop their investment in Visualforce and rebuild all of their applications in Lightning — I always advise customers against this kind of thing unless there’s a compelling reason. Otherwise there’s a significant investment and the best possible outcome is that users don’t notice any difference, while the more likely outcome is that things don’t quite work as they did, or at all!
Another reason I think the assumption is flawed is that it pre-supposes all Visualforce developers can seamlessly transition to Lightning with minimal effort. While this might be true for the likes of me that have been programming for nearly 40 years, it most definitely isn’t the case for those that have self-taught themselves Apex and Visualforce by building applications with the help and support of the Salesforce community. A lot of the time this is the only programming these people have done, so moving to another language isn’t straightforward — especially JavaScript! Loose typing, closures, functions as first class objects and prototypes are completely alien concepts when you are only used to a strongly typed statically compiled language like Apex.
That said, I’d certainly advise Salesforce developers to start familiarising themselves with JavaScript, but this is much easier when done in the familiar surroundings of Visualforce, allowing the server to do most of the heavy lifting. Taking this approach isn’t a silver bullet though, so here’s some advice based on my own experience moving over.
Take some time and learn JavaScript
You don’t need to become an expert, but if you try to piece things together using snippets from other people’s code you’ll find this an exercise in frustration. JavaScript is very different to what you’ve been used to, and figuring out why the following code executes immediately and then throws ‘Uncaught ReferenceError: example is not defined’ could take a while.
(function example(){
alert('In example!');
}());example();
There are masses of JavaScript resources on the web, and I’ve heard very good reports from people who’ve used Code School to get started. My preference is always books and I found Object Oriented JavaScript by Stoyan Stefanov was the one that made everything click for me. Your mileage will almost certainly vary, but it’s definitely worth spending the time to find the right learning method and then applying it.
Separate JavaScript from HTML
Unobtrusive JavaScript mandates separation of your JavaScript code from your presentation markup, which means no more attaching event handlers directly to HTML elements, such as:
<input type="text" id="firstname" onchange="copyToLastname()" />
If you do this, a developer that comes after you has to search through all of your markup as well as your JavaScript to figure out where a function might be used. Instead, attaching the handler to the element programmatically helps to clarify things, albeit at the cost of an extra line of code — your future colleagues will thank you for taking the time!
document.getElementById('firstname').onchange = copyToLastName;
(as an aside, this is an example of how functions are first class objects in JavaScript — I’ve defined copyToLastName elsewhere and I can simply assign it as the onchange function for the firstname element).
Avoid Visualforce merge syntax in JavaScript
It’s very tempting to do something like the following to plug a value from a Visualforce controller directly into your JavaScript:
var firstname=’{!Contact.FirstName}’;
which seems pretty innocuous. However, what this seemingly innocent line of code does is tightly couple your JavaScript to the Visualforce rendering engine — if the JavaScript isn’t processed by the rending engine, the merge syntax will be taken as a string literal and anywhere you use the firstname property will display {!Contact.FirstName}
rather than the actual first name of the contact.
Why is this a big deal I hear you ask? To minimise the load time of a page, you typically want to move the JavaScript into an static resource so that it can be included as an external file once development is complete. This allows the browser to cache the external file, meaning that the JavaScript doesn’t have to be delivered each time a user accesses the page. External files are included after the Visualforce page is delivered to the browser, which means they are only processed by the browser, not the Visualforce rendering engine.
As a corollary to this, I always advise developing JavaScript in it’s own Visualforce component. This keeps it nicely separate from the page markup, while still delivering it as part of the page to make debugging easier. Once you are happy that everything is working as expected (yeah right!) it’s straightforward to migrate this to a static resource.
Avoid anonymous functions
I’ve said this in so many of my articles and presentations in the past, but it always bears repeating — use anonymous functions as sparingly as you can. For those new to JavaScript (and I’m expecting a few of you to be reading this), here’s an anonymous function:
doRequest(function(response){
console.log('Got response ' + response);
});
Which doesn’t look too bad on it’s own. Start adding more business logic and things quickly go bad:
doRequest(function(response){
console.log('Got response ' + response);
if (response=='OK') {
doSecondRequest(response, function(nextResponse){
console.log('Got response ' + nextResponse);
});
}
else {
handleError(response, function(okStatus) {
console.log('Handled error okay');
}),
function(errorStatus) {
console.log('Unable to handle error');
}
);
}
});
In the real world, with more business logic than simple console.log statements, when you get an exception in an anonymous function nested inside several other anonymous functions it’s pretty much impossible to figure out what actually happened. I know this because one of my early JavaScript ventures, Ticket to Ride,
I ended up in exactly this situation. I’d pulled some data back from the server, handled the response in an anonymous function and then carried out a bunch of asynchronous function to store offline and then query back, handling all responses with additional anonymous functions. A week or so before my dry run I was frantically rewriting everything!
Don’t pollute the global namespace
If you declare variables in JavaScript like the following, these will all be added to the global namespace:
var x=1;
var y=2;
var length=3;
var count=7;
The global namespace is the window
object, so these declarations effectively are:
window.x=1;
window.y=2;
window.length=3;
window.count=7
The global namespace, window
, is shared between all JavaScript in your page. Thus if you have include another JavaScript file that declares:
var count='This is the counter';
Both declarations assign values to window.count
and one JavaScript file is going to be disappointed when it acts on this and finds it’s suddenly become the wrong type.
Instead, create objects specific to your JavaScript and enclose the variables inside these objects. You still stand a chance of colliding with an object from another JavaScript file, but a sensible naming convention should mitigate this:
var bgAppObj={
x : 1,
y : 2,
length : 3,
count : 7
};
Using these is as simple as specifying the object name prior to the variable:
console.log('Length = ' + bgAppObj.length);
bgAppObj.count+=3;
Thus even if another JavaScript file specifies a count
variable in the global namespace, your variables are nicely isolated in a container and unaffected.
That’s all there is to it?
Believe that and you’ll believe anything. The JavaScript language is evolving — ES6 support is gradually being added to browsers, ES7 is finalised, frameworks rise faster than house prices and fall faster than politicians reputations, and JavaScript on the server via Node goes from strength to strength. Getting started with JavaScript means you are opening yourself up to a load more learning, but it’s the future and it’s best to get in as early as you can.
I’m better known in the Salesforce community as Bob Buzzard — Umpteen Certifications, including Technical Architect, 5 x MVP and CTO of BrightGen, a Platinum Cloud Alliance Partner in the United Kingdom who are hiring.
You can find my (usually) more technical thoughts at the Bob Buzzard Blog