Thursday, January 29, 2009

Building a multi-page wizards

I recently tried to develop a set of related pages, something like a multi-page wizard where the context of the data would be preserved from one page to the next.

I started out with these ideas:

  • I tried sending the object as a parameter, but only strings (or numbers in string format) can be sent out as parameters.
  • I tried static objects but the state was not preserved.

I tried a few other tricks, but the system did not preserve the state... All the objects were being instatiated, rather than using the previous instance. I knew it could be done, because I had seen the documentation... And re-reading it I found the answer...

There is a comment for the wizzard's controller code sample in the Visualforce Developer's Guide, that reads:

... Note that the redirect attribute does not need to be set on the PageReference because the URL does not need to change when users move from page to page.

This information should be re-worded to something like this:

Note that the redirect attribute on the PageReference must not be set to true (set it to false or do not set it at all) because the URL must not change when users move from page to page.

Anyways, this is how it should be set up: VisualForce Page:

<apex:page controller="theData">
    <H1>Page1</H1>
    <apex:form >
        Data value: <apex:inputText value="{!DataText}" />
        <apex:commandButton action="{!Page1}" value="Page1" />
        <apex:commandButton action="{!Page2}" value="Page2" />
        <apex:commandButton action="{!Page3}" value="Page3" />
    </apex:form>
</apex:page>

Controller:

public class aaData {
// Data
    private String strText = null;
    public String getDataText() {
        return strText;
    }
    public void setDataText(String value) {
        strText = value;
    }

// Navigation
    public PageReference Page1() {
        return getPage('/apex/aaPage1');
    }
    public PageReference Page2() {
        return getPage('/apex/aaPage2');
    }
    public PageReference Page3() {
        return getPage('/apex/aaPage3');
    }
    private PageReference getPage(String URL) {
        PageReference page = new PageReference(URL);
        page.setRedirect(false);
        return page;
    }
}

I noticed that if the URL does not change, the state is preserved, but if the URL changes the state is lost. In addition to the "page.setRedirect(false)" (line 23 on the previous code), you must be careful as to the use of controllers and extensions... The only way I was able to not change the URL (and preserve the state) was when all the pages use exactly the same set of controllers and extensions... You may use a standard or custom controller and zero or more extensions, but every page in the wizard must use the same set of classes.

All the pages must have the same heading. Something like this:

<apex:page controller="theData">
Or this:
<apex:page controller="theData" extensions="ext1">
Or this:
<apex:page controller="theData" extensions="ext1,ext2">
Or this:
<apex:page standardController="Account" extensions="theData">
Or this:
<apex:page standardController="Account" extensions="theData,ext1">
The important thing is that all the pages in the wizard must use the same set of controllers and extensions, otherwise the state will be lost! I noticed that if I had something like this: Page 2:
<apex:page controller="theData" extensions="step2,step3">
Page 3:
<apex:page controller="theData" extensions="step3">
I was able to go from page 2 to page 3 preserving the state, but if I went back from page 3 to page 2, the state would be lost. Something simpler that you could try is not using extensions, but rather having an instance of the "extension class" in the controller "theData"... This would probably simplify your life :-)

No comments:

Post a Comment