Adding JavaScript and jQuery to Visualforce pages

Visualforce is pretty tolerant of how custom JavaScript is added, but this post suggests a couple of patterns to use (where possible):

  • External JavaScript file references or local JavaScript is best placed at the end of the page rather than the beginning which looks a bit strange at first sight. The benefit is that the page content can then be rendered by the browser before the browser becomes blocked loading the external JavaScript file and then executing that file and the local Javascript. So a potential page load delay is avoided, with the JavaScript work being completed in the user’s “thinking time” as they first see the page content.
  • It is easy to unintentionally add references to Javascript’s global scope and so potentially interfere with other references in that scope. In JavaScript scope is delineated by functions (not by blocks – curly brackets have no impact on scope) so var declarations should always be used and should always be used within a function.

So when not using external libraries put this executed (via the ()) anonymous JavaScript function (to create a new scope) at the end of the page:

<apex:page ...>
    <apex:sectionHeader .../>
    <apex:pageBlock ...>
        ...
    </apex:pageBlock>

<script>
(function() {
    // All the custom JavaScript goes in here
    // Always use var
    var i = ...;
    ...
})();
</script>
</apex:page>

and when using jQuery put both the include and the executed anonymous JavaScript function at the end of the page:

<apex:page ...>
    <apex:sectionHeader .../>
    <apex:pageBlock ...>
        ...
    </apex:pageBlock>

<apex:includeScript value="{!URLFOR($Resource.jQueryZip, 'jquery.js')}"/>
<script>
(function($) {
    // All the custom JavaScript goes in here
    // Always use var
    var i = ...;
    // Use $ in here for jQuery
    var j = $('table.notes');
    ...
})(jQuery.noConflict());
</script>
</apex:page>

This allows $ to be used as the reference to jQuery within the function, while ensuring that whatever the symbol $ was set to before jQuery was included is restored via the noConflict call.

Creating a custom global describe API using @RestResource

A colleague is working on a client that needs to know all the SObject names and all the field names within those SObjects. The Apex describe APIs provide this information but also a lot of other information that is not required in this case. So it is worth doing work at the server-side to cut down the information to only what is required by the client.

(In the org in question, the 300 SObjects produce JSON output of 800 kB, well below the 3 MB governor limit on HTTP responses.)

Salesforce’s @RestResource mechanism makes doing this pretty easy. The code below transfers the required information into instances of simple Apex classes, sorts the data based on label first then API name second, and then leaves it up to the platform to serialise those as JSON:

@RestResource(urlMapping='/v1/describe')
global with sharing class DescribeRest {

    global class Sob implements Comparable {
        
        public String sobLabel;
        public String sobApi;
        public Field[] sobFields;
        
        Sob(SObjectType t) {
            DescribeSObjectResult r = t.getDescribe();
            sobLabel = r.getLabel();
            sobApi = r.getName();
            sobFields = new Field[] {};
            for (SObjectField f : r.fields.getMap().values()) {
                sobFields.add(new Field(f));
            }
            sobFields.sort();
        }
        
        public Integer compareTo(Object o) {
            Sob that = (Sob) o;
            if (this.sobLabel < that.sobLabel) return -1;
            else if (this.sobLabel > that.sobLabel) return 1;
            else {
                if (this.sobApi < that.sobApi) return -1;
                else if (this.sobApi > that.sobApi) return 1;
                else return 0;
            }
        }
    }
    
    global class Field implements Comparable {
        
        public String label;
        public String api;
        
        Field(SObjectField f) {
            DescribeFieldResult r = f.getDescribe();
            label = r.getLabel();
            api = r.getName();
        }
        
        public Integer compareTo(Object o) {
            Field that = (Field) o;
            if (this.label < that.label) return -1;
            else if (this.label > that.label) return 1;
            else {
                if (this.api < that.api) return -1;
                else if (this.api > that.api) return 1;
                else return 0;
            }
        }
    }
    
    @HttpGet
    global static Sob[] get() {
        
        Sob[] sobs = new Sob[] {};
        for (SObjectType t : Schema.GetGlobalDescribe().values()) {
            sobs.add(new Sob(t));
        }
        sobs.sort();
        
        return sobs;
    }
}

When accessed using /services/apexrest/cveep/v1/describe.json, this produces JSON (formatted here to better illustrate the structure) taking about 10ms per object at the server-side:

[
    {
        "sobLabel":"Absence",
        "sobFields":[
            {"label":"Absence","api":"Name"},
            {"label":"Absence Type","api":"Type__c"},
            {"label":"Claim","api":"Claim__c"},
            ...
        ],
        "sobApi":"Absence__c"
    },
    ....
]

Now that the governor limits have been removed on describe calls the first limit that will be hit is probably the 3 MB response size limit.

Fixing a common cause of System.LimitException: Apex CPU time limit exceeded

When developing code, automated unit tests and interactive testing naturally tend to use small numbers of objects. As the number of objects increases, the execution time has to increase in proportion – linearly. But it is all too easy to introduce code where the execution time grows as the square of the number of objects or the cube of the number of objects. (See e.g. Time complexity for some background.) For example, even with 100 objects, code that takes 100ms for the linear case, takes 10s for the squared case and 1,000s for the cubed case. So for the squared or cubed cases

System.LimitException: Apex CPU time limit exceeded

exceptions can result with even modest numbers of objects.

Is this governor limit a good thing? On the positive side, it forces a bad algorithm to be replaced by a better one. But on the negative side, your customer is stuck unable to work until you can deliver a fix to them. Some sort of alarm and forgiveness from the platform for a while would be more helpful…

Here is a blatant example of the problem (assuming all the collections include large numbers of objects):

// Linear - OK
for (Parent__c p : parents) {
    // Squared - problem
    for (Child__c c : children) {
        // Cubed - big problem
        for (GrandChild__c bc : grandChildren) {
            // ...
        }
    }
}

So review all nested loops carefully. Sometimes the problem is hidden, with one loop in one method or class and another loop in another method or class, and so harder to find.

Often the purpose of the loops is just to find the objects/object in one collection that match an object in another collection. There are two contexts that require two different approaches (though in more complicated cases the approaches can be combined) to fix the problem or better to avoid it in the first place:

  • In e.g. a controller where the query is explicit, a parent and child can be queried together with direct relationship references available using a relationship query.
  • In a trigger, no __r collections are populated, so maps have to be used. A map allows a value to be looked up without the cost of going through every entry in a list.

Here is how to fix the problem for the two contexts in parent-child relationships:

// Explicit SOQL context
for (Parent__c p : [
        select Name, (select Name from Childs__r)
        from Parent__c
        ]) {
    // Loop is over the small number of related Child__c not all of the Child__c
    for (Child__c c : p.Childs__r) {
        // ...
    }
}


// Trigger context
Map<Id, List<Child__c>> children = new Map<Id, List<Child__c>>();
for (Child__c c : [
        select Parent__c, Name
        from Child__c
        where Parent__c in Trigger.newMap.keySet()
        ]) {
    List<Child__c> l = children.get(c.Parent__c);
    if (l == null) {
        l = new List<Child__c>();
        children.put(c.Parent__c, l);
    }
    l.add(c);
}
for (Parent__c p : Trigger.new) {
    // Loop is over the small number of related Child__c not all of the Child__c
    if (children.containsKey(p.Id)) {
        for (Child__c c : children.get(p.Id) {
            // ...
        }
    }
}

And for the two contexts in child-parent relationships:

// Explicit SOQL context
for (Child__c c : [
        select Name, Parent__r.Name
        from Child__c
        ]) {
    // The one parent
    Parent__c p = c.Parent__r;
    if (p != null) {
        // ...
    }
}


// Trigger context
Set<Id> parentIds = new Set<Id>();
for (Child__c c : Trigger.new) {
    if (c.Parent__c != null) {
        parentIds.add(c.Parent__c);
    }
}
if (parentIds.size() > 0) {
    Map<Id, Parent__c> parents = new Map<Id, Parent__c>([
            select Id, Name
            from Parent__c
            where Id in :parentIds
            ]);
    for (Child__c c : Trigger.new) {
        // The one parent
        if (c.Parent__c != null) {
            Parent__c p = parents.get(c.Parent__c);
            if (p != null) {
                // ...
            }
        }
    }
}

This fixed code will operate in close to linear time.

In praise of apex:inlineEditSupport

I recently had a requirement where one date value in a calculated table needed to be manually editable. While it would be possible to use an apex:inputField for all the values, using apex:inlineEditSupport seemed like a better approach because the need to edit is relatively rare. That allows the table to look uncluttered for the common case of no editing, yet still allows the values to be changed.

This is the result:

inlineedit

The point of this post is to highlight how easy this is to accomplish and to give a +1 to apex:inlineEditSupport in case anyone has been wary of using it in the several years it has been available…

All that was needed was to change this:

<apex:pageBlockTable value="{!payments}" var="p">
    ...
    <apex:column value="{!p.IssueDate__c}"/>
    ...
</apex:pageBloackTable>

to this:

<apex:pageBlockTable value="{!payments}" var="p">
    ...            
    <apex:column>
        <apex:facet name="header">
            <span class="inlineEditPencil">
                {!$ObjectType.Payment__c.fields.IssueDate__c.label}
            </span>
        </apex:facet>
        <apex:outputField value="{!p.IssueDate__c}">
            <apex:inlineEditSupport event="ondblclick"
                    showOnEdit="save, cancel"
                    hideOnEdit="submit, approve, send"
                    />
        </apex:outputField>
    </apex:column>
    ...
</apex:pageBloackTable>

and to add a couple of new command buttons (“save” and “cancel”) for the inline editing mode.

This CSS displays the pencil icon in the heading (to hint that the column is different):

<style type="text/css">
/* Fragile */
.inlineEditPencil {
    padding-right: 16px;
    background: url(/img/func_icons/util/pencil12.gif) no-repeat right 2px;
}
</style>

An Apex implementation of the OAuth 2.0 JWT Bearer Token Flow

This flow allows an access token (AKA a session ID) to be obtained for a user based on a certificate shared by the client and the authorization server. Unlike most other OAuth 2.0 flows, no password is needed. This avoids having to prompt for a password in a browser or having to have a stored password. So the flow works well for server to server interactions.

There is a Java sample implementation in the OAuth 2.0 JWT Bearer Token Flow help. Here is an Apex implementation of that. Having this in Apex allows e.g. one org to connect to another or a Force.com Site to obtain a session ID. This code could also be used to establish a connection from Salesforce to some other platform that supports the flow, but details like the encryption algorithm used might need changing.

public class Jwt {
    
    public class Configuration {
        public String jwtUsername {get; set;}
        public String jwtConnectedAppConsumerKey {get; set;}
        public String jwtSigningCertificateName {get; set;}
        public String jwtHostname {get; set;}
    }
    
    private class Header {
        String alg;
        Header(String alg) {
            this.alg = alg;
        }
    }
    
    private class Body {
        String iss;
        String prn;
        String aud;
        String exp;
        Body(String iss, String prn, String aud, String exp) {
            this.iss = iss;
            this.prn = prn;
            this.aud = aud;
            this.exp = exp;
        }
    }
    
    private class JwtException extends Exception {
    }
    
    private Configuration config;
    
    public Jwt(Configuration config) {
        
        this.config = config;
    }
    
    public String requestAccessToken() {

         Map<String, String> m = new Map<String, String>();
         m.put('grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer');
         m.put('assertion', createToken());
    
         HttpRequest req = new HttpRequest();
         req.setHeader('Content-Type','application/x-www-form-urlencoded');
         req.setEndpoint('https://' + config.jwtHostname +'/services/oauth2/token');
         req.setMethod('POST');
         req.setTimeout(60 * 1000);
         req.setBody(formEncode(m));
         
         HttpResponse res = new Http().send(req);
         if (res.getStatusCode() >= 200 && res.getStatusCode() < 300) {
             return extractJsonField(res.getBody(), 'access_token');
         } else {
             throw new JwtException(res.getBody());
         }
    }
    
    private String formEncode(Map<String, String> m) {
        
         String s = '';
         for (String key : m.keySet()) {
            if (s.length() > 0) {
                s += '&';
            }
            s += key + '=' + EncodingUtil.urlEncode(m.get(key), 'UTF-8');
         }
         return s;
    }
    
    private String extractJsonField(String body, String field) {
        
        JSONParser parser = JSON.createParser(body);
        while (parser.nextToken() != null) {
            if (parser.getCurrentToken() == JSONToken.FIELD_NAME
                    && parser.getText() == field) {
                parser.nextToken();
                return parser.getText();
            }
        }
        throw new JwtException(field + ' not found in response ' + body);
    }
    
    private String createToken() {
        
        String alg = 'RS256';
        
        String iss = config.jwtConnectedAppConsumerKey;
        String prn = config.jwtUsername;
        String aud = 'https://' + config.jwtHostname;
        String exp = String.valueOf(System.currentTimeMillis() + 60 * 60 * 1000);
        
        String headerJson = JSON.serialize(new Header(alg));
        String bodyJson =  JSON.serialize(new Body(iss, prn, aud, exp));
        
        String token = base64UrlSafe(Blob.valueOf(headerJson))
                + '.' + base64UrlSafe(Blob.valueOf(bodyJson));
        String signature = base64UrlSafe(Crypto.signWithCertificate(
                'RSA-SHA256',
                Blob.valueOf(token),
                config.jwtSigningCertificateName
                ));
        token += '.' + signature;
        
        return token;
    }
    
    private String base64UrlSafe(Blob b) {
        
        return EncodingUtil.base64Encode(b).replace('+', '-').replace('/', '_');
    }
}

And a test for the class. Note that the certificate has to be manually created in the org because there is no API that allows the test to automatically create it:

@isTest
private class JwtTest {
    
    // No API for createing a certificate so must be manually pre-created
    // using Setup -> Security Controls -> Certificate and Key Management
    private static final String PRE_CREATED_CERTIFICATE_NAME = 'JWT';
    private static final String FAKE_TOKEN = 'fakeToken';
    
    private class Mock implements HttpCalloutMock {

        public HTTPResponse respond(HTTPRequest req) {
            
            HTTPResponse res = new HTTPResponse();
            System.assertEquals('POST', req.getMethod());
            System.assert(req.getBody().contains('grant_type'), req.getBody());
            System.assert(req.getBody().contains('assertion'), req.getBody());
            
            res.setStatusCode(200);
            res.setBody('{"scope":"api","access_token":"' + FAKE_TOKEN + '"}');

            return res;
        }
    }

    @isTest
    static void test() {
        
        Jwt.Configuration config = new Jwt.Configuration();
        config.jwtUsername = 'abcdef@ghijkl.com';
        config.jwtSigningCertificateName = PRE_CREATED_CERTIFICATE_NAME;
        config.jwtHostname = 'login.salesforce.com';
        config.jwtConnectedAppConsumerKey = '6MVG9ZsNvTsRRnx.BZjJLCHB.hXYNAVb_oM';
        
        Test.setMock(HttpCalloutMock.class, new Mock());
        Test.startTest();
        String accessToken = new Jwt(config).requestAccessToken();
        Test.stopTest();
        System.assertEquals(FAKE_TOKEN, accessToken);
    }
}

Cool data tables using @RestResource, AngularJS and trNgGrid

I have an AngularJS application that shows tables of data using:

  • an Apex class that does dynamic SOQL and populates instances of a simple Apex class that are serialised to the client as JSON via the @RestResource annotation
  • the client side is AngularJS that pretty much just passes the JSON data through to a page template
  • the presentation work is all done by the excellent trNgGrid component and Bootstrap styling

The Apex code is clean and simple:

@RestResource(urlMapping='/report')
global without sharing class ReportRest {
    global class Claim {
        public String employeeName;
        public String department;
        public String reportsTo;
        public String claimNumber;
        public String status;
        public String leaveType;
        public Date startDate;
        public Date endDate;
        Claim(SObject c) {
            SObject e = c.getSObject('Employee__r');
            employeeName = (String) e.get('Name');
            department = (String) e.get('Department');
            SObject r = e.getSObject('ReportsTo');
            reportsTo = r != null ? (String) r.get('Name') : null;     
            claimNumber = (String) c.get('Name');
            status = (String) c.get('Status__c');
            leaveType = (String) c.get('LeaveType__c');
            startDate = (Date) c.get('StartDate__c');
            endDate = (Date) c.get('EndDate__c');
        }
    }
    @HttpGet  
    global static Claim[] get() {
        Claim[] claims = new Claim[] {};
        String soql = ...;
        for (SObject sob : Database.query(soql)) {
            claims.add(new Claim(sob));
        }
        return claims;
    }
}

and the trNgGrid markup is even more impressive:

<table tr-ng-grid="tr-ng-grid" class="table table-condensed" items="items"
      order-by="orderBy" order-by-reverse="orderByReverse">
  <thead>
    <tr>
      <th field-name="employeeName"/>
      <th field-name="department"/>
      <th field-name="reportsTo"/>
      <th field-name="claimNumber"/>
      <th field-name="status"/>
      <th field-name="leaveType"/>
      <th field-name="startDate" display-format="longDate" display-align="right"/>
      <th field-name="endDate" display-format="longDate" display-align="right"/>
    </tr>
  </thead>
</table>

You just define the column headers and trNgGrid generates the rows from the JSON data array (called “items” here). The resulting table has column sorting and column searching and other features can be enabled too. As it is written in AngularJS, it leverages AngularJS features such as filters (“longDate” here) for custom formatting.

What is great about this arrangement is that there is no tedious coding involved: all the code serves a purpose and the grunt work is handled by the frameworks. It also scores high on “ease of modification”: an extra column only takes a few minutes to add.

(Contrast this with the JavaScript required in e.g. Connecting DataTables to JSON generated by Apex.)

Here is a screen shot from the real application (with different columns):

TrNgGrid

Hash code for any Apex type via System.hashCode

If you are writing your own class that you want to use in sets or as a map key, you must add equals and hashCode methods as described in Using Custom Types in Map Keys and Sets. But hashCode is usually implemented by combining the hash codes of the fields of the class and until now not all Apex types exposed a hash code value.

This is now addressed in Summer ’14 by the System.hashCode method. It saves a lot of work for SObjects and provides consistency for primitive types.

This test illustrates the method working:

@isTest
private class HashCodeTest {

    @isTest
    static void string() {
        // Already had a hashCode method
        System.assertEquals('hello world'.hashCode(), System.hashCode('hello world'));
    }
        
    @isTest
    static void decimal() {
        // Wasn't available before
        System.assertEquals(3843600, System.hashCode(123.987));
        System.assertEquals(3843600, System.hashCode(123.987));
        System.assertEquals(0, System.hashCode(0));
        System.assertEquals(1, System.hashCode(1));
        System.assertEquals(2, System.hashCode(2));
        System.assertEquals(1024, System.hashCode(3.3));
        System.assertEquals(-720412809, System.hashCode(4773463427.34354345));
    }
    
    @isTest
    static void sobject() {
        // Wasn't available before
        System.assertEquals(-1394890885, System.hashCode(new Contact(
                LastName = 'Doe')));
        System.assertEquals(-1394890885, System.hashCode(new Contact(
                LastName = 'Doe')));
        System.assertEquals(-1474401918, System.hashCode(new Contact(
                LastName = 'Smith')));
        System.assertEquals(744078320, System.hashCode(new Contact(
                LastName = 'Doe', FirstName = 'Jane')));
        System.assertEquals(744095627, System.hashCode(new Contact(
                LastName = 'Doe', FirstName = 'John')));
    }
    
    @isTest
    static void testNull() {
        try {
            System.assertEquals(0, System.hashCode(null));
            System.assert(false);
        } catch (NullPointerException e) {
        }
    }
}

Thanks to Daniel Ballinger for flagging this via a comment on Expose hashCode on all Apex primitives so hashCode viable for custom classes.