The <apex:actionFunction> tag enables JavaScript functions to call Apex controller methods through AJAX requests in Visualforce pages. This component bridges client-side JavaScript with server-side Apex logic, allowing dynamic page updates without full page refreshes.
What is Apex ActionFunction Tag?
The apex actionFunction tag defines JavaScript functions that can invoke Apex controller methods from client-side code. When called, it sends an AJAX request to the server, executes the specified Apex method, and optionally re-renders page components with updated data.
Key characteristics of <apex:actionFunction>:
- Must be a child component of
<apex:form> - Creates a JavaScript function with the specified name
- Supports parameters to pass data from JavaScript to Apex
- Can re-render specific page components after execution
- Handles AJAX communication automatically
ActionFunction Tag Syntax and Attributes
Basic syntax structure:
<apex:actionFunction name="functionName" action="{!controllerMethod}" reRender="componentId" />
Essential attributes:
| Attribute | Required | Description |
|---|---|---|
| name | Yes | JavaScript function name to create |
| action | Yes | Apex controller method to invoke |
| reRender | No | Component IDs to refresh after execution |
| immediate | No | Skip validation when true (default: false) |
| timeout | No | Request timeout in milliseconds |
| status | No | ID of actionStatus component to show progress |
| oncomplete | No | JavaScript to execute after completion |
| onbeforedomupdate | No | JavaScript to execute before DOM update |
ActionFunction Placement Restrictions
Since API version 23.0, <apex:actionFunction> cannot be placed inside iteration components such as:
<apex:pageBlockTable><apex:repeat><apex:dataTable><apex:dataList>
This restriction prevents JavaScript function name conflicts when multiple iterations would create functions with identical names. Place actionFunction outside iteration components and pass parameters to identify specific records.
Complete Lead Form Example
This example demonstrates apex actionFunction integration with JavaScript validation and Apex controller methods. The form creates Lead records with client-side validation before server submission.
Visualforce Page Implementation
<apex:page controller="InsertLeadController" showHeader="false">
<script type="text/javascript">
function validateAndSubmit() {
var lastName = document.getElementById('{!$Component.leadForm.leadBlock.lastNameField}').value;
var company = document.getElementById('{!$Component.leadForm.leadBlock.companyField}').value;
if(lastName === '' || company === '') {
alert('Last Name and Company are required fields');
return false;
}
// Call apex actionFunction
submitLeadRecord();
return true;
}
function handleSubmitComplete(result) {
if(result.success) {
alert('Lead has been created successfully');
clearForm();
} else {
alert('Error creating lead: ' + result.message);
}
}
function clearForm() {
document.getElementById('{!$Component.leadForm.leadBlock.firstNameField}').value = '';
document.getElementById('{!$Component.leadForm.leadBlock.lastNameField}').value = '';
document.getElementById('{!$Component.leadForm.leadBlock.companyField}').value = '';
document.getElementById('{!$Component.leadForm.leadBlock.mobileField}').value = '';
}
</script>
<apex:form id="leadForm">
<apex:actionFunction name="submitLeadRecord"
action="{!submitLead}"
reRender="leadBlock,messages"
oncomplete="handleSubmitComplete({!submitResult})" />
<apex:pageMessages id="messages" />
<apex:pageBlock title="Lead Registration Form" id="leadBlock">
<apex:pageBlockSection columns="1">
<apex:pageBlockSectionItem>
<apex:outputLabel value="First Name" />
<apex:inputText value="{!lead.FirstName}" id="firstNameField" />
</apex:pageBlockSectionItem>
<apex:pageBlockSectionItem>
<apex:outputLabel value="Last Name" />
<apex:inputText value="{!lead.LastName}" id="lastNameField" required="true" />
</apex:pageBlockSectionItem>
<apex:pageBlockSectionItem>
<apex:outputLabel value="Company" />
<apex:inputText value="{!lead.Company}" id="companyField" required="true" />
</apex:pageBlockSectionItem>
<apex:pageBlockSectionItem>
<apex:outputLabel value="Mobile Phone" />
<apex:inputText value="{!lead.MobilePhone}" id="mobileField" />
</apex:pageBlockSectionItem>
<apex:pageBlockSectionItem>
<apex:outputLabel value="Lead Status" />
<apex:selectList value="{!lead.Status}" size="1">
<apex:selectOptions value="{!statusOptions}" />
</apex:selectList>
</apex:pageBlockSectionItem>
</apex:pageBlockSection>
<apex:pageBlockButtons>
<apex:commandButton value="Create Lead" onclick="return validateAndSubmit();" />
<apex:commandButton value="Clear" onclick="clearForm(); return false;" />
</apex:pageBlockButtons>
</apex:pageBlock>
</apex:form>
</apex:page>
Enhanced Apex Controller
public class InsertLeadController {
public Lead lead { get; set; }
public String submitResult { get; set; }
public InsertLeadController() {
lead = new Lead();
// Set default status
lead.Status = 'Open - Not Contacted';
}
public List<SelectOption> getStatusOptions() {
List<SelectOption> options = new List<SelectOption>();
// Get picklist values from Lead.Status field
Schema.DescribeFieldResult fieldResult = Lead.Status.getDescribe();
List<Schema.PicklistEntry> picklistValues = fieldResult.getPicklistValues();
for(Schema.PicklistEntry entry : picklistValues) {
if(entry.isActive()) {
options.add(new SelectOption(entry.getValue(), entry.getLabel()));
}
}
return options;
}
public PageReference submitLead() {
try {
// Validate required fields
if(String.isBlank(lead.LastName)) {
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Last Name is required'));
submitResult = '{"success": false, "message": "Last Name is required"}';
return null;
}
if(String.isBlank(lead.Company)) {
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Company is required'));
submitResult = '{"success": false, "message": "Company is required"}';
return null;
}
// Insert lead record
insert lead;
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.CONFIRM, 'Lead created successfully'));
submitResult = '{"success": true, "message": "Lead created with ID: ' + lead.Id + '"}';
// Reset for next entry
lead = new Lead();
lead.Status = 'Open - Not Contacted';
} catch(DmlException e) {
ApexPages.addMessage(new ApexPages.Message(ApexPages.Severity.ERROR, 'Error creating lead: ' + e.getMessage()));
submitResult = '{"success": false, "message": "' + e.getMessage() + '"}';
}
return null;
}
}
ActionFunction with Parameters
Pass data from JavaScript to Apex methods using <apex:param> components:
<apex:actionFunction name="updateRecord" action="{!updateMethod}">
<apex:param name="recordId" value="" assignTo="{!selectedId}" />
<apex:param name="fieldValue" value="" assignTo="{!newValue}" />
</apex:actionFunction>
<script>
function callUpdate(id, value) {
updateRecord(id, value);
}
</script>
Error Handling and Best Practices
JavaScript Error Handling
function safeActionCall() {
try {
if(typeof myActionFunction === 'function') {
myActionFunction();
} else {
console.error('ActionFunction not available');
}
} catch(e) {
console.error('ActionFunction error:', e);
alert('An error occurred. Please try again.');
}
}
Apex Exception Handling
public PageReference processData() {
try {
// Business logic here
Database.SaveResult result = Database.insert(records, false);
if(!result.isSuccess()) {
for(Database.Error error : result.getErrors()) {
ApexPages.addMessage(new ApexPages.Message(
ApexPages.Severity.ERROR,
error.getMessage()
));
}
}
} catch(Exception e) {
ApexPages.addMessage(new ApexPages.Message(
ApexPages.Severity.ERROR,
'Unexpected error: ' + e.getMessage()
));
System.debug('ActionFunction error: ' + e.getStackTraceString());
}
return null;
}
Performance Considerations
- Governor Limits: ActionFunction calls count toward Apex execution limits
- View State: Minimize view state size by using transient variables for temporary data
- Re-rendering: Only re-render necessary components to reduce response time
- Timeout: Set appropriate timeout values for long-running operations
- Bulk Operations: Avoid multiple actionFunction calls in loops; batch operations instead
ActionFunction vs Alternatives
| Component | Use Case | JavaScript Integration | Page Refresh |
|---|---|---|---|
| actionFunction | JavaScript-triggered Apex calls | Full integration | Partial (reRender) |
| commandButton | Form submissions | Limited (onclick) | Full page |
| commandLink | Navigation with actions | Limited (onclick) | Full page |
| actionSupport | Event-driven actions | Event-based | Partial (reRender) |
| remoteAction | Direct JavaScript-to-Apex | Full control | None |
Migration to Lightning Web Components
For new development, consider Lightning Web Components (LWC) instead of Visualforce with actionFunction. LWC provides:
- Modern JavaScript framework (ES6+)
- Better performance and security
- Native Apex method calls via
@wireand imperative calls - Component-based architecture
- Mobile-responsive by default
Example LWC equivalent:
// JavaScript Controller
import { LightningElement, track } from 'lwc';
import submitLead from '@salesforce/apex/InsertLeadController.submitLead';
export default class LeadForm extends LightningElement {
@track leadData = {};
handleSubmit() {
submitLead({ leadRecord: this.leadData })
.then(result => {
// Handle success
this.showToast('Success', 'Lead created', 'success');
})
.catch(error => {
// Handle error
this.showToast('Error', error.body.message, 'error');
});
}
}
Frequently Asked Questions
Can apex actionFunction be used outside of apex:form?
No, apex:actionFunction must be a child component of apex:form. The form component provides the necessary context for AJAX requests and parameter binding.
Why can’t I place actionFunction inside iteration components?
Since API version 23.0, actionFunction cannot be placed inside iteration components like apex:repeat or apex:pageBlockTable because it would create multiple JavaScript functions with the same name, causing conflicts. Place the actionFunction outside the iteration and use parameters to pass record-specific data.
How do I pass multiple parameters to apex actionFunction?
Use multiple apex:param components within the actionFunction tag. Each param can bind to a controller property using the assignTo attribute. Call the JavaScript function with the same number of parameters in the same order.
What happens if the apex actionFunction call fails?
Failed actionFunction calls trigger the onerror JavaScript callback if specified. Server-side exceptions are caught by the Apex controller’s try-catch blocks. Use ApexPages.addMessage() to display user-friendly error messages and check ApexPages.hasMessages() in JavaScript callbacks.
Can I call multiple apex methods from one actionFunction?
No, each actionFunction can only call one Apex method specified in the action attribute. To call multiple methods, either create separate actionFunction components or call multiple methods from within a single Apex method.
How do I handle long-running operations with actionFunction?
Use the timeout attribute to set a longer timeout value, implement apex:actionStatus to show progress indicators, and consider breaking long operations into smaller chunks. For very long operations, use @future methods or Queueable Apex instead.