Preventing a Visualforce ActionFunction from Refreshing the Page
I came across a restriction the other day regarding the apex:actionFunction Visualforce component when I was trying to implement a to-do list that allowed a user to complete a to-do with a click on a checkbox next to a list of to-dos. While using one of the many great two way binding Javascript frameworks like AngularJS with a @RemoteAction annotated Apex method would have worked great, it was a bit of overkill for this, as using a StandardSetController required minimal code.
Functionally, that all worked fine. The problem I ran into when I was setting up my code was that my entire Visualforce page was refreshing when I invoked the actionFunction. Even though I tried both setting the invoked Apex method to a return type of void (and later tried returning a null PageReference), the entire page refresh continued. Unfortunately this took away from the “behind the scenes save” behavior that I wanted the user to experience.
What I ended up doing to resolve this was setting a reRender attribute on the actionFunction that pointed to an ID that didn’t even exist on my page. While I didn’t have anything that I actually wanted to re-render, it appears that setting that attribute stopped the default action of a full page refresh and instead traded for the possibility of re-rendering an individual component instead.
The Visualforce page ended up looking like this very simple snippet of code:
<apex:page controller="TodosController">
...
<apex:form>
<apex:repeat value="{!todos}" var="todo">
<apex:inputField value="{!todo.Completed__c}" onchange="saveUpdates()"/>
<apex:outputField value="{!todo.Name}"/>
</apex:repeat>
<!-- need to point to nonexistent ID so page doesn't refresh -->
<apex:actionFunction name="saveUpdates" action="{!saveUpdates}" rerender="fakeresults"/>
</apex:form>
</apex:page>
Leveraging the StandardSetController makes implementing that custom saveUpdates method in the custom Apex controller quite easy:
public class TodosController {
public ApexPages.StandardSetController stdSetController;
...
public List<Todo__c> getTodos() {
return this.stdSetController.getRecords();
}
public void saveUpdates() {
this.stdSetController.save();
}
}