MVC-structured Visualforce Example
In my previous post, I gave a high-level overview of a Visualforce page lifecycle. In this post, I’d like to discuss how I like to apply the MVC pattern to my Visualforce pages to keep them logically structured and make them easier to maintain.
In my previous post, I mentioned that Visualforce, like JSP and ASP.NET, use a variation of the classic MVC pattern called the Model2 pattern. This means that the Visualforce framework enforces this pattern in its implementation. It does this by explicitly giving you, the developer, two tools: a Visualforce page, and an Apex class that has properties and methods that the page can reference. It is pretty easy to keep the Visualforce page organized by traditionally separating content and presentation, but the functionality in the Apex page can easily grow out of hand. To try to keep this Apex page under control, it is best to ‘swim with the current’ by being cognizant of the Model2 pattern when organizing a Visualforce page and its controller.
Before looking at the code I’ve prepared, notice that I use the term ‘viewModel’. There is a variation of the MVC pattern called MVVM which also uses the term ViewModel, but you should know that the meanings in these two cases are different. My ‘viewModel’ is indeed inspired by MVVM’s ViewModel, but because Visualforce doesn’t offer the exact same data-binding features that the MVVM model expects, my viewModel is a bit different.
Another thing to note when reading the code below is that I like to use a the idea of a Model for more than just ‘wrapping’ domain data to display on the page. Rather than just wrapping domain data, I also like to use a model to represent, or wrap, the data on the page. I include a property on my ‘viewModel’ for every part of the page that I want to change. This creates handles on the page that the controller can easily grab, making it easy to keep simple logic in the controller. When a developer comes to update this page later, it should be simple enough to see the separation between the View, the Models, and the Controller.
I’ll paste my code here, and add comments inline to continue the discussion.
<apex:page controller="Ctlr_LeadEntryWizard">
<apex:form id="the-form">
<apex:messages id="page-messages"/>
<!-- To give an example of how to control a page with page modes,
I'm using two pageBlocks, each of which has a different set of fields.
Whether they are rendered is controlled by a property on the Model.
It is important to note that the logic of toggling this property is in
the Controller. We *could* use a single variable on the Model called
'shouldRenderFieldset1', and then on the next pageBlock,
use 'rendered="NOT(shouldRenderFieldset1)"', but I prefer to keep all page logic
in the Controller. -->
<apex:pageBlock title="Lead Entry with Fieldset 1" mode="edit" rendered="{!viewModel.isRenderedLeadEntryFieldset1}" id="lead-entry-fieldset1-block">
<apex:pageBlockButtons >
<!-- This action logic belongs in the Controller. I suppose another option is to
put it in the Model, but I like to keep a thin model, using it only to hold state. -->
<apex:commandButton action="{!controller.saveLead}" value="Submit"/>
</apex:pageBlockButtons>
<apex:pageBlockSection title="Lead Name" columns="2" >
<apex:pageBlockSectionItem >
<!-- This is data input, not logic, so it should be in the Model. -->
<apex:outputLabel value="First Name" for="lead-first-name"/>
<apex:inputText value="{!viewModel.newLead.firstName}"/>
</apex:pageBlockSectionItem>
<!-- We can put other fields here, but it would just add noise to this example. -->
</apex:pageBlockSection>
</apex:pageBlock>
<apex:pageBlock title="Lead Entry with Fieldset 2" mode="edit" rendered="{!viewModel.isRenderedLeadEntryFielset2}" id="lead-entry-fieldset2-block">
<!-- Different fieldset for this mode. We can render this section by
setting the 'isRenderedLeadEntryFieldset2' property on the Model to true. -->
</apex:pageBlock>
</apex:form>
</apex:page>
And here’s the Apex controller and model. This is where the most interesting idea lie, in my opinion. Notice the very distinct separation between the model and the controller. Apart from the data validation, the model has no logic in it! It’s purely a state container! This seems to conform perfectly with how traditional MVC was envisioned. Likewise, the controller contains nothing but functions! Apart from the reference to the model, there is zero state in the controller! Check it out:
public with sharing class Ctlr_LeadEntryWizard {
/**
Notice that I have created two inner-classes, 'LeadEntryWizardViewModel'
and 'LeadEntryWizardController'. This is not necessary, but I did this
to emphasize the separation between the Controller and the Model here.
Also, you can more easily see the relationship between the Model and
the Controller here, as the Controller has a reference to the Model,
not vice-versa.
**/
public LeadEntryWizardViewModel viewModel {get; set;}
public LeadEntryWizardController controller {get; set;}
public Ctlr_LeadEntryWizard() {
this.viewModel = new LeadEntryWizardViewModel();
this.controller = new LeadEntryWizardController(this.viewModel);
}
//Page Model
public class LeadEntryWizardViewModel {
public Boolean isRenderedLeadEntryFieldset1 {get; set;}
public Boolean isRenderedLeadEntryFieldset2 {get; set;}
/** We could easily use the standard Lead object here, but read my reasons below. **/
public LeadModel newLead {get; set;}
public LeadEntryWizardViewModel() {
this.initEventSelect();
}
public void initEventSelect() {
this.isRenderedLeadEntryFieldset1 = true;
this.isRenderedLeadEntryFieldset2 = false;
this.newLead = new Lead();
}
}
/**
This Model could easily be the standard Lead object, but
what if you want this page to edit two related objects? In a case
like that, I find it easier to use one Model that pulls the two
related models into one.
Note that by using String fields here instead of standard SF objects
like Lead, we can't use 'apex:inputField' tag on the View. This is
bad because it injects validation, date pickers, and lookup field features.
If you need one of those features, you can obtain it here by using a
standard SF object, such as Lead, to reference the field. See the commented property below for
an example.
**/
public class LeadModel {
public String firstName {get; set;}
public String lastName {get; set;}
public String company {get; set;}
public String streetAddress {get; set;}
public String city {get; set;}
public String state {get; set;}
public String zip {get; set;}
//public Lead followupDate {get; set;}
// VF reference:
public LeadModel() { }
}
public class LeadEntryWizardController {
/**
Note that the Controller keeps a reference to the Model.
The Controller needs to read and modify the state of the page,
which should be stored in the Model.
**/
private LeadEntryWizardViewModel viewModel;
public LeadEntryWizardController(LeadEntryWizardViewModel pViewModel) {
this.viewModel = pViewModel;
this.initPage();
}
/**
This is where we can set the initial state of the page. That is,
this is where we can set the initial state of the Model.
**/
public void initPage() {
this.viewModel.isRenderedLeadEntryFieldset1 = true;
this.viewModel.isRenderedLeadEntryFieldset2 = false;
}
/**
Here's the logic for saving a new Lead. We read the state of the
page from the Model to get the necessary values to create the new
Lead to insert. We can create a validation class and pass these
values into the validator to ensure field formats, required fields, etc.
**/
public void saveLead() {
LeadModel submittedLead = this.viewModel.newLead;
Lead newLeadToInsert = new Lead();
newLeadToInsert.FirstName = submittedLead.firstName;
newLeadToInsert.LastName = submittedLead.lastName;
newLeadToInsert.Company = submittedLead.company;
newLeadToInsert.State = submittedLead.state;
newLeadToInsert.City = submittedLead.city;
newLeadToInsert.PostalCode = submittedLead.zip;
newLeadToInsert.Phone = submittedLead.phone;
}
}
}
I’ve tried this technique with a pretty complex Visualforce page that I wrote recently, and this method of organization seemed to scale pretty easily, keeping the functionality and responsibilities understandable even with many hundreds of lines of code in the controller. What do you think of this method of organization? Do you use a similar structure? Do you use a different structure that you think is better? I’m always looking for discussion and debate that produces awesome code.

Comments
Be the first to comment!
Leave A Comment