How to: use Handlebars.js to template Visualforce Remoting

how to use handlebars.js to template visualforce remotingUsing Handlebars.js, you can create Visualforce templates to render data you retrieve via Visualforce Remoting from Salesforce servers.

Visualforce Remoting is an excellent way to quickly get data from the Salesforce server with very little overhead. Unlike using the traditional AJAX actions with apex tags, Visualforce Remoting allows a request to the Salesforce server without sending the whole viewstate. This makes for a much faster and less data-hungry operation, which is especially critical if you are developing for the new Salesforce1 mobile platform.

One drawback to getting data this way (via Visualforce Remoting) is that you cannot feed it to the Visualforce engine for rendering. This means that developers must format the data on their own. Some common approaches include (1) generating the html in Apex and returning this as the response, or (2) parsing the response in the callback function and writing the html in JavaScript. Both of these options become difficult to maintain when you start generating large amounts of html. Thankfully, Handlebars.js provides a great solution to this problem.

Using Handlebars.js, you can create a template to render your data as you make calls to the Salesforce server. Because you create these templates using Visualforce, Salesforce validates them when saved which helps prevent headaches down the road. (Searching through your Apex code for where you forgot a closing anchor tag in a bunch of html scattered throughout string variables is not fun for anyone.)

Using the templates only adds one new element to the Visualforce Remoting Pattern: a new Visualforce Component for our template.

Below is a simple example of a Visualforce page that uses Remoting to search for Assets on an Account and uses a Handlebars Template to print out the response. This example does not rely on any external components so you can copy it straight to your developer org and give it a try.

Visualforce Page

<apex:page standardController="Account" extensions="HandleBarsJsDemoCtrl" docType="html-5.0">
    <apex:includeScript value="//cdnjs.cloudflare.com/ajax/libs/jquery/2.1.1/jquery.min.js"/>
    <apex:includeScript value="//cdnjs.cloudflare.com/ajax/libs/handlebars.js/2.0.0/handlebars.min.js"/>
    <apex:sectionHeader title="Handlebars Demo" subtitle="{!Account.Name}"/>
    
    <apex:pageBlock mode="detail"> 
    	<apex:pageBlockSection title="Account Information" collapsible="false">
        	<apex:outputField value="{!Account.Name}"/>
            <apex:outputField value="{!Account.Type}"/>
            <apex:outputField value="{!Account.Description}"/>
        </apex:pageBlockSection>
        <apex:pageBlockSection title="Asset Manual Search" columns="1">
        	<apex:pageBlockSectionItem>
                <apex:outputLabel value="Product Name" />
                <apex:outputPanel>
                    <form id="searchForm">
                    	<input type="search" id="queryString" placeholder="Enter a search query..." />
                    	<button id="searchAction">Search</button>
                    </form>
                </apex:outputPanel>
            </apex:pageBlockSectionItem>
            <div id="results"></div>
        </apex:pageBlockSection>
    </apex:pageBlock>
    <script id="assetResultTemplate" type="text/x-handlebars-template">
    	<c:HandleBarsJsAssetResultTemplate />
    </script>
    <script>
		
    	var asset_result_template;
    	
    	$(function() {
            asset_result_template = Handlebars.compile(
                $('#assetResultTemplate').html()	// Compile the template from the component
            );
            $('#searchForm').submit(function(e){	// Catch search and run our method
            	e.preventDefault();
                $('#results').html('Searching...');
                HandleBarsJsDemoCtrl.doAssetSearch('{!Account.Id}', $('#queryString').val(), function(response, event){
                    var result_html = asset_result_template(response);	// Pass the response to the template
                    $('#results').html(result_html);	// Add the result to the page
                });
            });
        });
    </script>
</apex:page>

Apex Controller

public class HandleBarsJsDemoCtrl {

    public ApexPages.StandardController stdCtrl {get;set;}
    
    public HandleBarsJsDemoCtrl(ApexPages.StandardController stdCtrl) {
        this.stdCtrl = stdCtrl;
    }
    
    @remoteAction
    public static List<Asset>; doAssetSearch(Id accountId, String q) {
        return Database.query(
        	'SELECT Name, Product2Id, Product2.ProductCode FROM Asset WHERE AccountId = \'' + accountId + '\' AND Name LIKE \'' + q + '%\''
        );
    }
}

Visualforce Component Template

<apex:component layout="none">
    <ul class="search-results">
    <!-- use the '.' character to iterate over the top level list -->
    {{#each .}}
    	<li class="search-result">
            <a href="/{{this.Id}}">
                {{this.Name}} - 
                <!-- print Product2 information if it exists -->
                {{#if this.Product2}}
                	{{this.Product2.ProductCode}}
                {{else}}
                	No associated Product2
                {{/if}}
            </a>
        </li>
    <!-- fallback if no results -->
    {{else}}
    	<li class="search-result no-results">No Results</li>
    {{/each}}
    </ul>
</apex:component>

–DH

Read more about Salesforce Development