Find your content:

Search form

You are here

Caching Expensive Results

 
Share

I'm building a custom interface in Visualforce that uses dependent picklists. Since Apex doesn't expose information about dependent picklist values, I've had to resort to building that information on the client side and sending it up to my controller. The problem is that's expensive processing to do on every page load when the picklist options themselves will change very frequently. As such, is there a way to store/cache the information in SFDC so that I can update it only when it changes?

Approaches I've tried and ruled out:

  1. Custom Settings: this seemed like the obvious choice, but a JSON representation of my picklist data is significantly longer than the maximum field length.

  2. Static Resources: it's not possible to create Static Resources from Apex, so I'd have to manually update the resource when there are changes -- it wouldn't be possible to build a VF page to do this for me.


Attribution to: Benj

Possible Suggestion/Solution #1

Custom Settings are ideal for this.

  • You can either create a List based Custom Setting and split your information accross multiple rows.
  • Or span your information accross multiple fields, if you have reasonable confidence on the maximum size. I used this second approach recently to store Ebay's API ID which is quite large but finite.

Either way the following should hopefully help you...

    // Custom setting fields have a max of 255, so we need to split this api token up
    eBayAPI__c ebayAPI = eBayAPI__c.getInstance();
    List<String> chunks = splitIntoChunks(EbayAPIToken, 255);
    ebayAPI.UserTokenPart1__c = null;
    ebayAPI.UserTokenPart2__c = null;
    ebayAPI.UserTokenPart3__c = null;
    ebayAPI.UserTokenPart4__c = null;   
    if(chunks.size()>0)
        ebayAPI.UserTokenPart1__c = chunks[0];                  
    if(chunks.size()>1)
        ebayAPI.UserTokenPart2__c = chunks[1];                  
    if(chunks.size()>2)
        ebayAPI.UserTokenPart3__c = chunks[2];                  
    if(chunks.size()>3)
        ebayAPI.UserTokenPart4__c = chunks[3];                  
    upsert ebayAPI;
    return null;

This is the helper function I used to split the string.

    private List<String> splitIntoChunks(String text, integer chunkSize)
    {
        // Util code to split up large strings
        List<String> chunks = new List<String>();
        integer offset = 0;
        while (offset < text.length())
        {
            integer partSize = Math.min(chunkSize, text.length() - offset);
            chunks.add(text.substring(offset, offset + partSize));
            offset += partSize;
        }
        return chunks;
    }       

Attribution to: Andrew Fawcett

Possible Suggestion/Solution #2

Benj, since you're using the JavaScript Ajax Toolkit to get at the Dependent Picklist API, another option is to cache the information using JavaScript as well, by storing it in HTML5 Local / Session Storage, with a fallback either to Cookies or Custom Settings (as per Andrew Fawcett's 'striping' approach, which is a good solution as well) for older browsers that do not support HTML5. We've used this for caching calls to describeTabs, and it's worked great. HTML5 Session/Local storage is definitely the way to go looking forward, as it's lightning fast, gives you plenty of storage space, and is all client-side, so after the first call there's no work at all done in Apex.

Here's a great intro to HTML5 Local/Session Storage.

Here's the basic approach you'd need to do in Visualforce/JavaScript to get this going:

  1. Create some storage getter/setter methods that will do the hard work of checking for support of local/session storage and falling back to cookies if necessary.
  2. On page load, check your storage for a cached picklist dependencies key in your storage.
  3. If the cache exists, grab it! Otherwise, do a call to AJAX API to retrieve the info you need. Then, strip the info down to the bare minimum you need to store (the AJAX API calls often return a lot of needless bloat, I recommend parsing your responses down to just what you need), and then use JSON.stringify() to convert your data to JSON before storing it in your cache.

Part 1: Create your Local Storage Repo

// The canUseLocalStorage() function will let you know
// if HTML5 local storage is supported
var _canUseLocalStorage = 'nodata',
canUseLocalStorage = function(){
    if (_canUseLocalStorage === 'nodata') {
        try {
          return (_canUseLocalStorage = 'localStorage' in window && window['localStorage'] !== null);
        } catch (e) {
          return (_canUseLocalStorage = false);
        }
    } else return _canUseLocalStorage;
},
// Store stuff in local storage, if possible,
// otherwise fall back to cookies
localStorage = function(key,value) {
    // Can we use storage?
    var canUseStorage = canUseLocalStorage();
    return (
        // If we're RETRIEVING content...
        (typeof value === 'undefined') ? (
            // Use Local Storage
            (canUseStorage) ? window.localStorage[key] 
            // Use cookies
            : $.cookie(key,value)
        )
        // If we're STORING content
        : (
            // Use Session Storage
            (canUseStorage) ? (window.localStorage[key] = value) 
            // Use cookies
            : $.cookie(key,value)
        )
    );
};

Part 2: Check to see if your storage repo has a cached result

// Our key(s) will be called 'picklistCache_<objectName>_<fieldName>`
// So here's an example for the Account Type field
var cacheName = 'picklistCache_Account_AccountType',
    cacheResult = localStorage(cacheName);

// If we did NOT have such a result, 
// we'll need to query the AJAX API
if (!cacheResult) {
    if (sforce.connection.sessionId) {
        // Query the AJAX API for the Picklist Dependency info,
        // using the strategy in the following article:
        // http://iwritecrappycode.wordpress.com/2012/02/23/dependent-picklists-in-salesforce-without-metadata-api-or-visualforce/

        // ....
        // ANd stringify your result once you're done!
        cacheResult = JSON.stringify(cacheResult);

        // Once we've got our data, cache it!
        localStorage(cacheName,cacheResult);

    }

}

// Now use the result to do your client-side picklist dependency work!
if (cacheResult) {
     // Unpack our JSON result
     cacheResult = JSON.parse(cacheResult);

    // Render dependent picklists
}

Attribution to: zachelrath
This content is remixed from stackoverflow or stackexchange. Please visit https://salesforce.stackexchange.com/questions/5025

My Block Status

My Block Content