Tuesday 23 December 2014

Return of the $5 eBonanza

5 Dollar  Social Media

If you’ve ever visited this blog before, you’ll know that I am the author of the Visualforce Development Cookbook. There’s even a handy link to allow you to purchase on the top right of every page.  If you haven’t purchased a copy to date, there’s really no excuse now, as this year sees the return of the Packt $5 eBonanza.  

That’s right, your eyes aren’t deceiving you, you have the opportunity to buy my book in eBook format (or more than one - its the perfect Christmas gift for old and young alike). Even better, this isn’t limited to my book - in fact its every eBook and video available from Packt!

The offer runs until 6th January 2015 so there's plenty of time to make a considered selection - its the perfect opportunity to pick a new technology to learn for the coming year.

You can find out more information at:  http://bit.ly/1wHPcbd

Saturday 13 December 2014

Visualforce Markup and JavaScript Includes

When using JavaScript in Visualforce pages it is often useful to set the initial state via the Visualforce controller and then use JavaScript to manipulate the page layout and state based on user interaction.

Visualforce Markup and merge fields in JavaScript that are directly in the Visualforce page work fine - consider the following Visualforce page that uses unobtrusive JavaScript to attach click event handlers to the name elements of a table of opportunities. (Yes, I know this is a pretty contrived example!).

<apex:page standardController="Opportunity" recordSetVar="opps">
  <apex:includeScript value="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js"/>
  <script>
  $(document).ready(function() {
    <apex:repeat value="{!opps}" var="opp">
      $('[id$="nameOppCol{!opp.id}"]').on('click',
        function()
    	{
    	   window.open('/{!opp.Id}',
    	               'Opportunity','height=500,width=800,left=100,top=100');
    	}
      );
    </apex:repeat>
  });
  </script>
  
  <apex:pageBlock title="Opps Table">
    <apex:pageblocktable value="{!opps}" var="opp">
  	  <apex:column headerValue="Name">
  	    <div id="nameOppCol{!opp.Id}">
    	  <apex:outputField value="{!opp.Name}" />
  	    </div>
  	  </apex:column>
  	  <apex:column headerValue="Amount">
  	    <div id="amountOppCol{!opp.Id}">
    	  <apex:outputField value="{!opp.Amount}"/>
    	</div>
  	  </apex:column>
  	  <apex:column headerValue="Close Date">
  	    <div id="closeDateOppCol{!opp.Id}">
  	      <apex:outputField value="{!opp.CloseDate}"/>
  	    </div>
  	  </apex:column>
    </apex:pageblocktable>
  </apex:pageBlock>
</apex:page>

The JavaScript relies on a couple of Visualforce markup components to set up the click handlers - an <apex:repeat> to iterate the opportunities and a merge field to access the id of the opportunity, which identifies the <div> element that the click handler should be bound to:

<apex:repeat value="{!opps}" var="opp">
  $('[id$="nameOppCol{!opp.id}"]').on('click',
    function()
    {
       window.open('/{!opp.Id}',
                   'Opportunity','height=500,width=800,left=100,top=100');
    }
  );
</apex:repeat>

The page displays a list of opportunities:

Screen Shot 2014 12 13 at 12 34 41

and clicking on an opportunity name opens a popup window to display the opportunity details:

Screen Shot 2014 12 13 at 12 36 06

However, when getting the page ready for a production environment, its always good practice to place the JavaScript into its own static resource file (its also better to minify it, but for ease of understanding I’m skipping that part), resulting in a Visualforce page that simply includes the static resource:

<apex:page standardController="Opportunity" recordSetVar="opps">
  <apex:includeScript value="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-1.9.1.min.js"/>
  <apex:includeScript value="{!$Resource.oppJS}"/>
  
  <apex:pageBlock title="Opps Table">
    <apex:pageblocktable value="{!opps}" var="opp">
  	  <apex:column headerValue="Name">
  	    <div id="nameOppCol{!opp.Id}">
    	  <apex:outputField value="{!opp.Name}" />
  	    </div>
  	  </apex:column>
  	  <apex:column headerValue="Amount">
  	    <div id="amountOppCol{!opp.Id}">
    	  <apex:outputField value="{!opp.Amount}"/>
    	</div>
  	  </apex:column>
  	  <apex:column headerValue="Close Date">
  	    <div id="closeDateOppCol{!opp.Id}">
  	      <apex:outputField value="{!opp.CloseDate}"/>
  	    </div>
  	  </apex:column>
    </apex:pageblocktable>
  </apex:pageBlock>
</apex:page>

 However, when accessing this page the click event handlers no longer work and inspecting the page shows JavaScript errors. Clicking through to the included resource shows that the Visualforce markup hasn’t been replaced with the appropriate HTML elements:

Screen Shot 2014 12 13 at 12 49 02

When the JavaScript markup is in the Visualforce page, it will be processed by the Visualforce rendering engine and replaced with HTML elements, text etc as required, before being delivered to the browser.

However, when the JavaScript is moved into a static resource and included, it bypasses the Visualforce rendering engine. The containing page is processed as usual and delivered to the browser, but only then are the referenced JavaScript resources included, so any Visualforce markup, merge fields etc remain in their literal form. The browser's JavaScript then attempts to process the JavaScript, hits the Visualforce markup and generates a syntax error.