threeheadsonapike

Salesforce frustrations? Ask one of our heads on a pike !

Salesforce URL Hacking the right way – Prepopulate fields, Record Type Selection and No hard-coded ID’s (deployable)

This post was inspired by an article found here:  http://raydehler.com/cloud/clod/salesforce-url-hacking-to-prepopulate-fields-on-a-standard-page-layout.html

And also:

http://blog.deliveredinnovation.com/2012/09/17/create-a-custom-new-button-on-a-related-list-to-return-to-the-parent-record-after-clicking-save/

With credits to Wes on:

http://force201.wordpress.com/2012/01/22/hack-to-find-field-ids-allows-a-default-ui-new-page-to-be-pre-populated/

Everyone has the solution how to pre-populate fields after clicking on a custom ‘New’ button but no one has addressed the issue of hard coded IDs  in such a way that making new buttons easy and re-usable!

It’s a great way to populate fields on a Salesforce page however the problem with this method is that we are hardcoding the ID’s of the various labels on the record. If you are used to working on a sandbox before deploying on a production environment you will soon realize that having to change all these ID’s because you hard coded these, it is not only time-consuming, it is bad practice.

Attached to this post is a little script that I wrote that allows you create buttons without hard-coding anything, you can deploy the code between org’s and all will work !

Examples how to pre-populate different fields using the script (just add this into a Visualforce button), ofcourse you can add more fields to the URL if you need to prepopulate more than one:

1. Pre-populate a text field having API label: ‘Text__c’ on a custom object called: ‘Custom__c’:

  • /apex/RedirectWithVariables?object=Custom__c&Text__c=Text Value

2. Pre-populate a picklist having API label: ‘Picklist__c’ on a custom object called ‘Custom__c’:

  • /apex/RedirectWithVariables?object=Custom__c&Picklist__c=Picklist Value

3. Create a new record of a given record type (use the label of the record type instead of the name):

  • /apex/RedirectWithVariables?object=Custom__c&RecordType=Custom Record Type

4. Pre-populate an example lookup field having API label ‘Lookup__c’ on a custom object called: ‘Custom__c. Important to note that a lookup field requires two parameters, the ID of the record that it references and the text value of the field as seen by the user. To set the text value use the API name of the lookup and to set the ID value of the lookup append ‘ID_’ to the API name of the lookup as seen in the following example:

  • /apex/RedirectWithVariables?object=Custom__c&Lookup__c=Text Value&ID_Lookup__v=ID_Value

Hope this will anyone out there !!

Classed used in this post:

RedirectWithVariables.class

StaticFunctions.class

Page used in this post:

RedirectWithVariables.page

Dear SantaForce… My Christmas wishlist for test driven development.

Dear Santa,

I’ve been a good developer all year, making sure my code is compete, well commented and has good unit tests. Hopefully I’ve made your good list. But I haven’t always found it as easy (or as fast) as it could be to have great unit tests. There have been some great new features in this space (e.g. setmock), so hopefully there will be continued development in this space. Here’s what I would like for Christmas:

1. Usable debug logs in the IDE.
Logging levels in the IDE have been behaving weird for a while now. Despite only wanting my debug messages back (so using an info level when writing to the log and selecting info as the filter level) I get so much data back that’s it’s unusable. This also makes running tests from the IDE really slow – there is some horrible code doing string manipulation over the whole log that is killing even my mega dev machine. This is killing developer velocity, and I’m fairly sure is a blocker to good test practice in the force community.

2. Separate context for test data setup.
I understand the reasons for not allowing mixed dml operations and not allowing dml before a web callout… in normal code. But these restrictions make setting up test data and then testing code a nightmare. Setmocks, for testing callouts, is fairly useless without being able to setup test data.

What would be ideal is a way to declare a block of code as test data setup and have the context reset after this has run. It would also be handy to be able to suppress debug logs for this bit of the test, so the logs focus on the area of interest.

3. Test suites.
I’ve used unit test frameworks before that allow suites of unit tests. The suite shares the same test data setup. This makes it faster to develop tests and also faster to run the tests, as the test data setup time is shared. The only way to do this at present is to have a mega unit test, which makes monitoring and reporting on unit failures really hard.

I appreciate this letter is a little early but I thought that these presents might take longer to build than other toys.

Yours hopefully,

ThreeHeads

Emulating Cron Jobs (Time-Based Jobs) in Salesforce Using time-dependent Workflows

Cron Jobs
On our project we had a need to be able to schedule specific ‘jobs’ to create records on specific times without using batches. Though batches work, we felt they are cumbersome to maintain. However, now that Salesforce has released Spring ’12 we can use a new feature for time-dependent workflows to emulate cron jobs. This feature is called: ‘Re-evaluate Workflow Rules after Field Change’.

What does it do: ‘If this field update changes the field’s value, all workflow rules on the associated object are re-evaluated. Any workflow rules whose criteria are met as a result of the field update will be triggered.’. Great, so now we can fire a new workflow FROM a workflow! Exciting! Be aware though – if you require huge amounts of data to be inserted or updated stick to batches, however, if you simply require a cron-based system to change somewhere between 1 and 10.000 records, this might be your method!

Before we start, here is (my cool) flow chart of how it works:

CronJob - State Diagram

CronJob – State Diagram

How does it work?

We will use our setup to explain the workflows in place. Let’s assume we have a custom object called CronJob, with three fields: Startdate, Enddate and one checkbox field called ‘System Fire Trigger’. The start date will indicate when the job will start, the end date will indicate when the job should end and the checkbox field will be used to fire a trigger that will run our daily code (updating it to true or false will fire an update trigger where our code will be put).

So if we update the checkbox field to true or false, it will trigger our code that needs to be executed at any specific time. So, now on to the workflows to create our first Cron Job!

Workflows

In our example we want to make sure our cycle starts running at 0:00 on the night of our start date, and keeps running every day at 0:00 until the end date. Lets start with the first workflow that will make sure our cycle starts at the start date:

Workflow Name: Workflow 1 – Start

  1. The first workflow we create on the CronJob object gets fired on creation, and will have the following rule: Start Date GREATER THAN TODAY.
  2. This workflow will have a time-dependent workflow action that fires ‘1 Day after Start Date’ (Start date comes from our custom object)
  3. The time-dependent workflow action will update the field ‘System Fire Trigger’ to true and has the ‘Re-evaluate Workflow Rules after Field Change’ set to true (checked).

Salesforce will treat ‘1 day after start date’ as being the next day at 0:00, so it will not add 24 hours but instead will start exactly the next day. So what will happen now is, exactly at 0:00 the next day after the start date, the field ‘System Fire Trigger’ gets updated to true. Furthermore, this workflow will stop working as Startdate <= Today.

Workflow Name: Workflow 2 – Round 1

  1. The second workflow we create on the CronJob object gets fired whenever the object gets created or updated, and will have the following rule: (CronJob: Start Date LESS OR EQUAL TODAY) AND (CronJob: End Date GREATER OR EQUAL TODAY) AND (CronJob: System Fire Trigger EQUALS True)
  2. This workflow will have a time-dependent workflow action that fires ‘1 Day After Rule Trigger Date’.
  3. The time-dependent workflow action will update the field ‘System Fire Trigger’ to false and has the ‘Re-evaluate Workflow Rules after Field Change’ set to true (checked).

The second workflow gets activated when the checkbox is set to true AND we are in the current cycle (which it will be after workflow 1). One day after the trigger date, the checkbox gets set to false. This will get picked up by the 3rd workflow.

Workflow Name: Workflow 3 – Round 2

  1. The third workflow we create on the CronJob object gets fired whenever the object gets created or updated, and will have the following rule: (CronJob: Start Date LESS OR EQUAL TODAY) AND (CronJob: End Date GREATER OR EQUAL TODAY) AND (CronJob: System Fire Trigger EQUALS False)
  2. This workflow will have a time-dependent workflow action that fires ‘1 Day After Rule Trigger Date’.
  3. The time-dependent workflow action will update the field ‘System Fire Trigger’ to true and has the ‘Re-evaluate Workflow Rules after Field Change’ set to true (checked).

The third workflow gets activated when the checkbox is set to false AND we are in the current cycle. One day after the trigger date, the checkbox gets set to true. This will get picked up again by the 2nd workflow.

And there we have it. The second workflow triggers the third workflow exactly at 0:00 and the third workflow in turn will trigger the second workflow at 0:00. Furthermore, because we update the record, it will fire an update trigger every night. Inside the update trigger you can write any code you want:


trigger CronJobTrigger on CronJob__c (before update) {
    if(Trigger.isBefore){
        if(Trigger.isUpdate){

           // put your code or calls to classes here.

        }
    }
}

Enjoy !

Visualforce Charts and Google Visualization

Visualforce Charting – great feature but unusable as of now
Many customers would like to expose their data to specific clients using nice charts. One of the upcoming features of Salesforce is Visualforce charting.

These features would allow any developer to write either direct functions or remoting functions to load data into the charts. However as of now there are two main drawbacks to this feature:

  1. Salesforce will initially release this feature for the sandbox only. The official release is planned for Winter 2013 so if you are planning to develop charting and release them during the summer, you will be disappointed.
  2. Visualforce charts look great and do an easy job to create charts however; its current functionality is very limited. For example, for a bar chart there is no way to control the individual colors of the bars nor can you group a set of bars together having a single title on the y-axis and do this for each title (having a set of statuses per country for example).

Obviously there are hacks around this. Using jQuery you can manipulate the charts directly, coloring each individual bar, but after a mouse hover the colors will revert back to its original default coloring meaning you will require extra re-color functions. You can imagine this is not scalable and turns your page into a mess of Javascript horror.

That said, Salesforce is on the right path with Visualforce charts, being able to expose the data and having the innate power of Salesforce charts used on pages. Until the time we can actually use it on a production environment and being able to customize it, I would like to point to the great Google Visualization library and show you an example how this allows for customization.

Google Visualization
Let’s do an example with Google visualization and Javascript remoting. Google visualization allows for a rich set of charts, completely customizable and free to use. To see the possibilities, check out their gallery.

To begin, first load the library directly from Google in the head of your page, and we also a placeholder for a bar chart on the page:

<apex:page controller="DataController">

<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<div id="barChart"></div>
</apex:page>

This will load the Google library however now we need to fetch some data from Salesforce to actually show a bar chart and a pie chart. First let’s create our controller and add a container class that we can use to store our result and return it as a JSON object. We will call this GraphData.


global with sharing class DataController {

	global class GraphData {

		public String label { get; set; }
   		public List points { get; set;}

    		public GraphData(String label){
      			this.label = label;
      			this.points = new List();

    		}
         }
}

Our ChartData class will store a label for the point(s) and a list called points of type Double to store any number of points. Next – let us write a Javascript remoting function that can be accessed from our page to fetch the data. Notice:This is a fake query and will not work, it is simply used for example purposes.

Extending our Data Controller:


global with sharing class DataController {

	@RemoteAction
	global static List loadData(){

		List lGraphData = new List();

		List lCustomData = [ Select Country__c, Revenue1__c, Revenue2__c From CountryData__c ];

		for(CustomObjectData oData : lCustomData){
			GraphData oDataPoint = new GraphData(oData.Country__c);
			oDataPoint.points.add(oData.Revenue1__c);
			oDataPoint.points.add(oData.Revenue2__c);

			lGraphData.add(oDataPoint);
		}

		return lGraphData;
	}

	global class GraphData {

		public String label { get; set; }
   		public List points { get; set;}

    		public GraphData(String label){
      			this.label = label;
      			this.points = new List();

    		}
	}

}

Notice two things:

  1. We add the @RemoteAction tag before the function loadData to allow this function to become visible on the page using Javascript. Returning the list of data will automatically convert this object into JSON.
  2. Currently we add two custom fields in this example, Revenue1 and Revenue2. Using a list of points we can return any number of points. The only thing we need to pay attention to is what each number means when showing the chart.

Now our controller is ready to go – let’s fetch the data on the page and create the bar chart. Please see the following html:

<apex:page controller="DataController">

<script type="text/javascript" src="https://www.google.com/jsapi"></script>
<script type="text/javascript">

		google.load('visualization', '1.0', {'packages':['corechart']});

	      	google.setOnLoadCallback(drawCharts);

	      	function drawCharts() {

			DataController.LoadData(
				function(result, event){

                     			var visualization = new google.visualization.BarChart(document.getElementById('barChart'));

                     			var data = new google.visualization.DataTable();

                     			data.addColumn('string', 'Country');
                     			data.addColumn('number', 'Revenue 1');
                     			data.addColumn('number', 'Revenue 2');

                     			for(var i =0; i<result.length;i++){
                    				var p = result[i];

                        			data.addRow(p.label, p.points[0], p.points[1]);
                      			}

                     			var options = {
			          		title: 'Revenue 1 versus Revenue 2 per Country',
			          		chartArea:{ width: '90%', height: '90%' }

			        	};

                    			visualization.draw(data, options);
              		}, {escape:true});

		}


<div id="barChart"></div>

</apex:page>

Please note the following:

  1. We tell the Google library to load the ‘corechart’ capabilities: google.load(‘visualization’, ‘1.0’, {‘packages’:[‘corechart’]});
  2. Then we define a function that will be called after the packages are loaded, in our case ‘drawCharts’.
  3. Since we have three values, one for the label and two for the revenue values we have to create three columns, one string and two numbers:data.addColumn(‘string’, ‘Country’);
    data.addColumn(‘number’, ‘Revenue 1’);
    data.addColumn(‘number’, ‘Revenue 2’);
  4. Then we add rows to the data object, the first column will be the label of the result, and the second and third column will be result.points[0] and result.points[1] respectively.
  5. Finally we can draw the chart:  visualization.draw(data, options);

Hope this helps!

Salesforce javascript hacks: full rich text editor, in a single click

Salesforce uses FCKEditor as it’s in-built WYSIWYG rich text field editor. If you’ve ever seen FCKEditor in the wild, you’ll probably have been frustrated with quite how chopped down Salesforce have made this. Most annoying missing feature? For me, not being able to strip out background colours from text I’ve copy and pasted from elsewhere (especially if it was another field that was highlighted blue whilst I was copying!).

Before sharing this javascript hack, it’s worth pointing out: the current editor is like this for a reason. Seriously annoying/bad things may happen (mostly affected page rendering) if you start to use these unsupported features. I would only use this in a supporting system used mostly by competent devs. I wouldn’t give this to real users.

The trick here is to remove the Toolbar attribute, making FCKEditor revert back to its full toolbar. We need to get this javascript to execute on the current page. The easiest way of doing this was to add it to the home page custom links component (Setup > Customize > Home > Home Page Components) as a hyperlink:

javascript:var iframes= document.getElementsByTagName('iframe');for (var frame in document.getElementsByTagName('iframe')){	iframes[frame].src = iframes[frame].src.replace('&Toolbar=SalesforceBasic','');}

Once this link is in place, to use this feature:

  • Edit a record
  • Click into the rich text field to launch the editor
  • Click on the custom javascript link created above
  • Click back into the rick text field – you will now see the full menu bar

And what does this do? It turns this:
SFDC toolbar

Into this:
Full FCKEditor toolbar

Happy editing. Be careful.

Customized Salesforce Customer Portal

In this brief article I will explain how you can brand the customer portal without the need for Site licenses by making use of a Site URL but logging in via Customer Portal.

For one of our clients it was necessary to expose the Customer Portal to their customers so they were able to view data and reports. However, this portal had to be branded starting with the login. The initial Customer login page is not very pretty and, as you can see from the image, in our case was completely unstyled:

Unstyled login

Unstyled login

You can also see from the URL that it points to the default “/secur/login_portal.jsp” page. Not very pretty so let’s spice things up. First, let’s create our own Visualforce login page.


<apex:page showHeader="false" controller="SiteLoginController"
    standardStylesheets="false" sidebar="false">

    <head>
        <title>Customized Customer Login</title>

        <script type='text/javascript'>
        function noenter(ev)  {
            if (window.event && window.event.keyCode == 13 || ev.which == 13) {
                javascriptLogin();
                return false;
             } else {
                  return true;
             }
         }
        </script>
        <style type="text/css">
             fieldset.login {
                background: none repeat scroll 0 0 white;
                border: 1px solid #AAAAAA;
                border-radius: 5px 5px 5px 5px;
                font-family: "Century Gothic","Lucida Grande",Arial,sans-serif;
                margin: 0 0 25px;
                padding: 10px;
            }

            fieldset.login label {
                clear: both;
                color: #333333;
                display: block;
                font-size: 12px;
                font-weight: bold;
                padding-top: 10px;
            }

            fieldset.login input.text {
                border: 1px solid #AAAAAA;
                color: #5E5E5E;
                float: left;
                padding: 5px;
                width: 320px;
            }

            .clear {
                clear: both;
            }

            .content {
                margin: 0 auto;
                width: 419px;
            }
        </style>
    </head>
    <body>

        <div class="content">

            <div class="login">
                <apex:form id="loginForm" forceSSL="true">
                    <apex:actionFunction name="javascriptLogin" action="{!login}" />

                    <fieldset class="login">
                        <b>Customer Login</b>
                        <apex:pageMessages id="error" />

                        <apex:outputLabel styleClass="label"
                            value="{!$Label.site.username}" for="username" />
                        <apex:inputText styleClass="text" id="username"
                            value="{!username}" />
                        <apex:outputLabel styleClass="label"
                            value="{!$Label.site.password}" for="password" />
                        <apex:inputSecret styleClass="text" id="password"
                            value="{!password}" onkeypress="return noenter(event);" />

                        <div class="clear"></div>
                        <span class="form-link"> </span>
                        <div class="clear"></div>
                        <p>
                            <apex:commandButton value="Login" styleClass="button"
                                action="{!login}" id="submitbutton" />
                        </p>
                    </fieldset>
                    <p></p>

                </apex:form>
            </div>
        </div>
    </body>
</apex:page>

Also, we need to add the controller that will actually handle the logging in SiteLoginController. NOTE: the variables strOrgID and strPortalID should be set to the IDs specific for your organization. Remember the URL mentioned in the ugly styled login box? It is ‘/secur/login_portal.jsp?orgId=&portalId=’. Also, the strURL should be set to your environment, for example https://cs8.salesforce.com (if that is your environment).

global with sharing class SiteLoginController {

  //set username/ password variables via page
    global String username {get; set;}
    global String password {get; set;}

    global PageReference login() {

      //static org-id and portal id
      String strOrgID = '';
      String strPortalID = '';
      String strURL = '';
      //start url of the page
      String startUrl = strUrl + '/secur/login_portal.jsp?orgId=' + strOrgID + '&portalId=' + strPortalID;

    startUrl += '&un=' + username;
        startUrl += '&pw='+ password;

        //set reference and attempt login
        PageReference portalPage = new PageReference(startUrl);
        portalPage.setRedirect(true);
        PageReference p = Site.login(username, password, startUrl);

        //if p==null, no login
        if (p == null) {
              return Site.login(username, password, null);
        } else {
              return portalPage;
        }
    }

    //test data provided by salesforce
     global SiteLoginController () {}

    @IsTest(SeeAllData=true) global static void testSiteLoginController () {
        // Instantiate a new controller with all parameters in the page
        SiteLoginController controller = new SiteLoginController ();
        controller.username = 'test@salesforce.com';
        controller.password = '123456';

        System.assertEquals(controller.login(),null);
    }
}

This looks much better:

Improved login

Improved login

Now let’s expose this to the outside world using Sites! Create your site:

Create new site

Create new site

You will see the following screen, please make sure that the Active Site Homepage points to the new login page we just created:

Create new site

Create new site

Save the site and try it out – go to the URL and attempt to login. You should be good to go! To further customize your portal, you can go to Look and Feel within the Customer Portal settings and add HTML files. Using CSS you can style the further looks of your portal.

Hope this helps!

Debugging public site pages

Want to debug a public site page for a non-logged-in/guest user? You can.

Setup > Monitoring > Debug Log  > New monitored user

In the user box, enter the name of the Site you want to debug. E.g. if you have a site called “pikehead” then enter “pikehead” in this box. Don’t use the search pop-up, as that won’t find anything, but if you click “Save” then salesforce will auto-magically start logging “pikehead Site Guest User”. Helpful.

URL fields on sObject are treated as relative in VisualForce… and how to force them to be absolute

So, you want to store a link to an external site in a  URL field on an sObject. Great. You set it up, entering some dummy data:

Record 1 URL: http://www.google.com

Record 2 URL: http://www.google.com

Within Salesforce standard pages, both will show as absolute URLs and correctly link to google. However, if you use this as a value of a link in a visual force page, these links show up as:

Link 1: http://site.yourorg.csX.force.com/www.google.com

Link 2: http://www.google.com

Frustrating, especially as this behaviour is inconsistent between the standard page and VisualForce.

Luckily, there is a quick trick to fix this:

  1. Check if the user contains the string “//”
  2. If it doesn’t it will have been entered without an http:// or https:// prefix, so add one

The markup for this is:

<apex:variable var="absoluteurl" value="{!IF(CONTAINS(Yourlink_URL__c,"//"),Yourlink_URL__c,"http://" + Yourlink_URL__c)}" >
<apex:outputlink value="{!absoluteurl}" target="_new">{!Yourlink_name__c}</apex:outputlink>

Hopefully this will save someone else 5 mins of annoyance.

Key manipulation of an APEX Map on a Visualforce page

One of the features of using maps in Salesforce is their ability to be used on a Visualforce page by looping of their keys and using the key to fetch whatever is stored at that position in a map. For example, assume we have a map called mapMenu and each key (defined as a string) is pointing to a string. We could loop over the contents printing all menu items by doing the following:

<apex:repeat value="{!mapMenu}" var="key ">
    <apex:repeat value="{!mapMenu[key]" var="item">

{!item}

    </apex:repeat>
</apex:repeat>

Now, not taking into account that the keys are returned in arbitrary order we have used this method in various ways to display data. However, one of the issues I would like to address is the manipulation of the key on the Visualforce page. Let us assume we would like to use the key not only the fetch the item, but also use it to create a link with a query string that will display some kind of sub category page:

<apex:repeat value="{!mapMenu}" var="key ">

<apex:repeat value="{!mapMenu[key]" var="item">

    <a href="{!$Page.SubCategory}?category={!key}"> {!item} </a>

    </apex:repeat>
</apex:repeat>

Great! Now we have a link pointing to some kind of sub category page with the ‘item’ as its title. But what if need to manipulate the key, for example strip out spaces when passing it as a query string? Now here is the issue. Since a key in a map can be any primitive type it is considered an object, so doing:

<a href="{!$Page.SubCategory}?category={!SUBSTITUTE(key,’ ’,’’)}">
{!item}
</a>

Will result in:
Error: Incorrect parameter for function ‘SUBSTITUTE()’. Expected Text, received Object

Ok, that’s fine we say, since we know the key is actually a string we can try and cast it:

<a href="{!$Page.SubCategory}?category={!SUBSTITUTE( TEXT(key) ,’ ’,’’)}"> {!item} </a>

But this will result in:
Error: Incorrect parameter for function ‘TEXT()’. Expected Number, Date, DateTime, received Object

Blast. It seems we are unable to perform any manipulation on the keys of the map as it is considered an object by all functions. However, we can still cast it into a string by assigning it to a Visualforce variable:

<apex:repeat value="{!mapMenu}" var="key ">

<apex:repeat value="{!mapMenu[key]" var="item">

<apex:variable value="{!key} " var="strParsedKey" />
<a href="{!$Page.SubCategory}?category={!SUBSTITUTE(strParsedKey,’ ‘,’’}">

     {!item}
</a>

    </apex:repeat>

</apex:repeat>

Notice how we assign it to the variable strParsedKey and we add an extra space t0 the value: value=”{!key} “. By adding this extra character (any character) we are forcing Visualforce to cast it into a string and now we can use any of the manipulation functions.

Hope this helps!

Controller functions fire twice on an AJAX postback on a Visualforce page.

During one of our developments we noticed the following behaviour which initially did not make sense at all. Every AJAX postback fired from a visualforce page would cause some specific functions that are used to rerender a set of outputpanels to be triggered twice. For example, our debug logs were showing it would call getMenuItems (used to load the menu) twice:

First call at 14:32:21.728, unnecessary call:

14:32:21.728 (728701000)|CODE_UNIT_STARTED|[EXTERNAL]|01pD0000000rSlG|Collaborate_CreateOrderBrandController get(getMenuItems)

Second call at 14:32:22.151, used to rerender part of the page:

14:32:22.151 (1151036000)|CODE_UNIT_STARTED|[EXTERNAL]|01pD0000000rSlG|Collaborate_CreateOrderBrandController get(getMenuItems)

Mind you, we are simply firing an AJAX request for a removal of an item. So what is happening here?

For some reason during the AJAX request, after the viewstate deserializes and the controller class gets instantiated, all functions that are used on the visualforce page to rerender specific aspects of the page, get fired before the actual AJAX function call is executed, for example removing an item in our case. Initially we figured this may be due to the fact our functions have mostly been written as properties (get, set properties). Currently we are still unsure what is causing this but here is our solution:

Salesforce has a nice property called ‘Transient’. Declaring any property transient means it will not be sent as part of the Viewstate and therefore will be null during an AJAX postback. Now we have a way of identifying which request is an AJAX postback, and which request isn’t. So somewhere in our class we declare:

public Transient Boolean doRerender = true;

The first time the page loads, this value is true. However because it is declared ‘Transient’, it will be null on any AJAX postback.

Now, if we check our debug logs and we find any function being run twice on an AJAX postback (mostly always those parts on a Visualforce page that get rerendered after the AJAX postback), we can now apply the following fix assuming the getMenuItems function is the function that is being fired twice:

public  List<Menu_Item>  getMenuItems(){

List<Menu_Item> lMenuItems = new List<Menu_Item>();

if(this.doRerender!=null)

lMenuItems = this.buildMenu();

return lMenuItems;

}

We now solved the issue for the first function call. It will detect the value is null and will not execute any logic. However, the 2nd time it fires the function we do need it to execute the logic as we want the specific parts of the page to rerender properly.
To do this we need to set this.doRerender = true in the function that is executing the AJAX request, in our example removeItem:

function void removeItem(){

this.doRerender = true;
Id idSOI = Apexpages.currentPage().getParameters().get('idSOI');
this.remove(idSOI);

}

Now the second time the getMenuItems is called (for the rerender) it will execute the logic needed. Applying this solution will make sure specific functions needed to rerender specific parts of the Visualforce page will only execute once. If anyone can shed some light on why this is happening in the first place, please leave a comment.

Design a site like this with WordPress.com
Get started