1

The classic example Creating a Wizard shows how to build a wizard using 1 apex controller and several visualforce pages. In my case our wizard has 5 pages and 1 controller. The problem is that the amount of code in the apex controller got really huge which makes maintenance and creating new functionality a bit sluggish.

The goal of the wizard is to support creation of a record of the object MyMainObject__c (the real name does not matter). Once a user goes to the next step the current state should be saved (i.e. if a user selects account and goes to the next page, and then quit the wizard for some reason, the draft record of the object MyMainObject__c should be inserted into the database. So the next time the user comes back to finish the process, the previously selected account should be in place), In a nutshell the wizard steps (pages) are the following:

  1. Select Account and Opportunity
  2. Upload files and assign them to the record
  3. Select related contacts
  4. Provide other custom data
  5. Send the created record with all related data to an external system

As you can see, all these steps are not directly connected with each other, but the apex logic is in 1 controller. Although I moved all non-controller related code into separate service classes, the controller size is still enormous.

So I was thinking of using Visualforce Components and Extensions (on the components) to make the wizard modular and more maintainable. The idea is to have one Visualforce page and controller which would contain main apex login (e.g navigation, keeping the instance of MyMainObject__c, general validation) And Components will be rendered based on the current step. The instance of MyMainObject__c will be passed to each component as an attribute to allow connection between the main controller and component:

<apex:page>
    <!-- ... -->
    <c:wizardStep1 currentRecord="item" rendered="{! isStep1 }"/>
    <c:wizardStep2 currentRecord="item" rendered="{! isStep2 }"/>
    <!-- ... -->
</apex:page>

As far a I understand the main point of wizard is that it allows to keep view state between steps, reducing the number of GET requests.

So how can I keep a view state of a page between different components on the same page?

  • 1
    you can always pass as an attribute to the components the instance of the main controller and the component controllers can set values back into the main controller – cropredy Jan 15 at 0:17
  • @cropredy, yeah that makes sense. I am wondering if Visualforce Components is a good choice for building a wizard? I know it's a bit of an opinion-based question, but the official documentation doesn't speak about it. They built an example wizard using Visualforce pages. Moreover, none of the wizard examples I have seen used Visualforce Components. I thought it a bit odd, taking into consideration practical benefits that Visualforce Components bring, like modularization. – Eduard Jan 15 at 8:37
2

This is a broad question you are asking and I'll only touch on some of issues:

  • ViewState management - This will be dominant as you only have 135K to work with. Whether you use a monolithic controller, helper classes, or VF component controller classes, you will need to think carefully about which variables are kept in viewstate versus those that can be transient or buried inside of methods or recomputed on each wizard step.
  • VF page organization - A typical wizard has many VF pages with the controller using action methods to return pageReference variables of the next page to visit. Wizards also have "previous" buttons to take one back in the wizard sequence. By using components as in the OP, you need to have lots of conditional rendering based on where the user is within the wizard sequence.
  • VF Components - Typically, these are used if you plan on having the VF markup appear in several places in different "applications". By permitting the "calling" page to pass arguments, the components become reusable. If your application elements are not planned on being reused in, say, other wizards then perhaps you can "componentize" your master VF page using apex:include as there's lots of merit in making a VF page compact and readable. Component controllers can communicate with their parent controller by passing as an attribute value the master controller instance. But if the components are meant to be shared amongst multiple master controllers, then you need to design a common interface.
  • When to commit - It is not completely clear in the OP what you are planning but in a wizard, one needs to decide when the commit is done (i.e. the DML). Do you want all the steps to complete before saving anything? Or do you allow partial saves along the way? If the latter, what does that mean for database integrity? When files need to be uploaded, you can't really have them hang around in view state unless they are super tiny so perhaps file uploads should happen at the last step of the wizard.
  • Thank you for such a detailed breakdown. A few points re- your questions: (1) partial updates along the way are allowed: when a user goes to step2, the initial object is inserted into the database. Moving through the wizard the initial object is updated; (2) when a user in on the files upload step, files are inserted into the database and linked to the initial object - this is saved to the database. So the actual files are not hanging in view state, just their ids. – Eduard Jan 17 at 8:59

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.