Skip to main content
TrailblazerDX, the ultimate AI learning event is heading back to San Francisco March 6-7, 2024. Register Now and save 35% with code T24DEYE424.

Create & Use Custom Controllers

Learning Objectives

After completing this unit, you’ll be able to:

  • Explain what a custom controller is and describe its key attributes.
  • Create a custom controller class.
  • Use a custom controller on a Visualforce page.

Introduction to Custom Controllers

Custom controllers contain custom logic and data manipulation that can be used by a Visualforce page. For example, a custom controller can retrieve a list of items to be displayed, make a callout to an external web service, validate and insert data, and more—and all of these operations will be available to the Visualforce page that uses it as a controller.

Elsewhere you were introduced to how Visualforce supports the Model–View–Controller (MVC) design pattern for building web apps. Controllers typically retrieve the data to be displayed in a Visualforce page, and contain code that executes in response to page actions, such as a button being clicked. When you use the standard controller, a great deal of, well, standard functionality is provided for you by the platform.

But one size does not fit all, and not all web apps are standard. When you want to override existing functionality, customize the navigation through an application, use callouts or Web services, or if you need finer control for how information is accessed for your page, Visualforce lets you take the reins. You can write a custom controller using Apex and completely control your app’s logic from start to finish.

Create a Visualforce Page that Uses a Custom Controller

Add a custom controller to a Visualforce page by referencing the name of the controller class in the <apex:page>  controller attribute.

When your page uses a custom controller, you can’t use a standard controller. Pages use a different attribute to set the custom controller.

  1. Open the Developer Console and click File | New | Visualforce Page to create a new Visualforce page. Enter ContactsListWithController for the page name.
  2. In the editor, replace any markup with the following.
    <apex:page controller="ContactsListWithController">
    <apex:pageBlock title="Contacts List" id="contacts_list">
    <!-- Contacts List goes here -->
    When you try to save this page, you’ll get an error, because ContactsListWithController doesn’t exist yet. No worries, we’ll fix that next.

Create a Custom Controller Apex Class

A custom controller is just an Apex class, which you write yourself using the Developer Console.

There are a lot of system and utility classes to help you write custom controller logic, but the only requirement for a class to be used as a custom controller is that it exists.

  1. Open the Developer Console and click File | New | Apex Class to create a new Apex class. Enter ContactsListWithController for the class name.
  2. In the editor, replace any code with the following.
    public class ContactsListWithController {
    // Controller code goes here
    As with Visualforce pages, you need to save your changes to Apex when you change it. It’s not much, and it doesn’t do anything yet, but it does make the error go away on the Visualforce page. So…
  3. Switch back to the Visualforce page and save it again. The error message should go away, and the page is saved successfully.
  4. Click Preview to open a preview of your page that you can look at while you make changes. A new window should open, showing the standard Salesforce page header and sidebar elements, but no content yet.

At first glance, these two new items you’ve created don’t seem very interesting. But even though they are 90% placeholder code, the two items—Visualforce page and Apex controller—are linked to each other. As soon as you add some more code to the controller your page will be able to use it.

Beyond the Basics

You might have noticed that this custom controller class doesn’t inherit from another class, nor does it implement an interface promising to conform to the requirements of a Visualforce controller. Even complex controllers don’t do these things, because there isn’t any such class to inherit from or interface to implement. This leaves you free to create your own classes and interfaces as your experience with Apex increases.

Add a Method to Retrieve Records

Create a getter method that runs a SOQL query that returns records you want to display on your page.

The primary purpose of most controllers is to retrieve data for display, or handle updates to data. In this simple controller, all you need to do is run a basic SOQL query that finds contact records, and then make those records available to the Visualforce page.

  1. In the ContactsListWithController class, replace the // Controller code goes herecomment line with the following code.
    private String sortOrder = 'LastName';
    public List<Contact> getContacts() {
    List<Contact> results = Database.query(
    'SELECT Id, FirstName, LastName, Title, Email ' +
    'FROM Contact ' +
    'ORDER BY ' + sortOrder + ' ASC ' +
    'LIMIT 10'
    return results;
    This code adds one private member variable, a string named sortOrder, and one public method, getContacts(). sortOrder is pretty easy to understand, it’s just the name of the field to sort the contacts by. getContacts() is also fairly simple, but if you haven’t seen Apex before, it might be hard to parse at first. The effect of the method is to perform a SOQL query to get a list of contact records, and then return that list of contacts to the method caller. And who will the caller be? The Visualforce page, of course!
  2. In the ContactsListWithController page, replace the <!-- Contacts List goes here -->comment line with the following markup.
    <!-- Contacts List -->
    <apex:pageBlockTable value="{! contacts }" var="ct">
    <apex:column value="{! ct.FirstName }"/>
    <apex:column value="{! ct.LastName }"/>
    <apex:column value="{! ct.Title }"/>
    <apex:column value="{! ct.Email }"/>
    When you save this page you should see a familiar looking table of contact information. A contacts list backed by a custom controller

The markup for the ContactsListWithController page should look fairly familiar. Except for the controller attribute of the <apex:page> tag, it’s pretty much the same code you would use to create the page with the standard controller.

What’s different is what happens when the {! contacts } expression is evaluated. On this page, Visualforce translates that expression into a call to your controller’s getContacts() method. That method returns a list of contact records, which is exactly what the <apex:pageBlockTable> is expecting.

The getContacts() method is called a getter method, and it’s a general pattern, where {! someExpression } in your Visualforce markup automatically connects to a method named getSomeExpression() in your controller. This is the simplest way for your page to get access to the data it needs to display.

Add a New Action Method

Create action methods in your custom controller to respond to user input on the page.

Showing data is great, but responding to user actions is essential to any web app. With a custom controller, you can create as many custom actions as you want to support on a page, by writing action methods to respond to user activity.

  1. In the ContactsListWithController class, below the getContacts()method, add the following two methods.
    public void sortByLastName() {
    this.sortOrder = 'LastName';
    public void sortByFirstName() {
    this.sortOrder = 'FirstName';
    These two methods change the value of the sortOrder private variable. sortOrder is used in the SOQL query that retrieves the contacts, and changing sortOrder will change the order of the results.
  2. In the ContactsListWithController page, replace the two <apex:column> tags for ct.FirstName and ct.LastNamewith the following markup.
    <apex:column value="{! ct.FirstName }">
    <apex:facet name="header">
    <apex:commandLink action="{! sortByFirstName }"
    reRender="contacts_list">First Name
    <apex:column value="{! ct.LastName }">
    <apex:facet name="header">
    <apex:commandLink action="{! sortByLastName }"
    reRender="contacts_list">Last Name
    Although the visual appearance remains the same, if you click the First Name and Last Name column headers now, they will change the sort order for the contacts list. Nice!

The new markup adds two nested components to each of the <apex:column> components. <apex:column> by itself has a plain text header, but we want to make the header clickable. <apex:facet> lets us set the contents of the column header to whatever we want. And what we want is a link that calls the right action method. The link is created using the <apex:commandLink> component, with the action attribute set to an expression that references the action method in our controller. (Note that action methods, in contrast to getter methods, are named the same as the expression that references them.)

When the link is clicked, it fires the action method in the controller. The action method changes the sort order private variable, and then the table is rerendered. When the table is rerendered, {! contacts } is reevaluated, which reruns the query with whatever sort order was just set. The final result is the table is resorted in the order requested by the user’s click.

Beyond the Basics

The header text for the first name and last name columns is hard-coded in this markup. But what if your users don’t all use English? The standard Salesforce user interface has translated versions of the field names for all standard objects, and you can provide your own translations for custom objects. How would you access these? Instead of the plain text, try this markup: <apex:outputText value="{! $ObjectType.Contact.Fields.FirstName.Label }"/>. That’s the right way to reference a field’s label, even if your organization all uses the same language, because it will automatically update if the field name is ever changed.

Tell Me More...

Custom controllers and the Apex language let you do pretty much anything you can think of in your Visualforce pages.

Getter methods pull data out of your controller onto your page. There are corresponding setter methods that let you submit values from the page back up to your controller. Like getter methods, you prefix your setters with “set”, and other than that, they’re just methods that take an argument.

An alternative to getters and setters is to use Apex properties. Properties are kind of a combination of a variable with getter and setter methods, with a syntax that groups them together more clearly. A simple property that references a custom object might be declared like this.

public MyObject__c myVariable { get; set; }

Properties can be public or private, and can be read-only, or even write-only, by omitting the get or set. And you can create implementations for the get or set methods, when you want to perform additional logic besides simply saving and retrieving a value.

Properties are a general feature of Apex, not specific to Visualforce. Apex is a complete programming language, and in addition to being the natural partner for building complex Visualforce pages, it’s used in many other Lightning Platform development contexts. See the Apex topics elsewhere here, and the resources at the end of this page for many ways to learn to use Apex fully.

The lifecycle of a Visualforce request and response can seem complex initially. In particular, it’s important to understand that there’s no specific order in which getters or setters (or properties, if you use them) are called, so you must not introduce order-of-execution dependencies between them. There’s a lot more detail available to you in the relevant sections of the Visualforce Developer’s Guide, in particular the Custom Controllers and Controller Extensions chapter.


Keep learning for
Sign up for an account to continue.
What’s in it for you?
  • Get personalized recommendations for your career goals
  • Practice your skills with hands-on challenges and quizzes
  • Track and share your progress with employers
  • Connect to mentorship and career opportunities