(Taken from an e-mail on the mailing list by Johan Compagner - July 2005)

Wicket back button support is based on the same principles as the Undo/Redo support in swing.

If you have page versioning enabled (that's the default) then everything you do on the component level is recorded. So setting the visibility flag is recorded as an Undoable Change for a specific version of that page, The same goes for replacing components in a page. We record the "Remove" of the old component and the "Add" of a new component. When you do something like this then the version number of the page is bumped up one. You can see that in the links that are generated to that page.

Then when an action comes in to that page with a smaller version number. Then everything that was in recorded from the largest version to the version you want are Undone.

So for exampe a visibility recorded on a component is stored in this class:

protected class VisibilityChange extends Change
{
{panel}
  private final Component component;
  private final boolean isVisible;
{panel}

{panel}
  VisibilityChange(final Component component)
  {
    this.component = component;
    this.isVisible = component.isVisible();
  }
      
  /**
   * @see wicket.version.undo.Change#undo()
   */
  public void undo()
  {
    component.setVisible(!isVisible);
  }
{panel}
}

And that undo is called when an older version is encountered and the undo method will invert the visibility.

You as a developer must record the change if you want model changes to be recorded. That is something we can't really do as a framework. (because models can be anything, implemented by everybody) See Component.modelChanging (should be called before the model is changed) and the Component.modelChanged (should be called after the model is changed)

For back button support the first (modelChanging) is the important one.

Design rules for writing applications with back button support

In summary, if you want to write an application that fully supports the back button you must know a number of rules:

  • Before you change a model of a component, call modelChanging() on the component, after making the change call modelChanged()
  • Pages are versioned by default but Forms are not. If you want to make a form versioned call form.setVersioned(true) on the form right after all components have been added (not before). This holds at least for wicket version 1.2.2
  • If you are keeping state in your component apart from the component models (e.g. as instance variable or in data providers) make sure that the changes are versioned. You can implement versioning by adding a Change object that can undo the change using Component.addStateChange(Change) as described above.

Component models and versioning

Component models are automatically versioned by creating a copy using Java Serialization (triggered by modelChanging()). Nevertheless, if you construct a model (say CompoundModel) with a model and save the same object as a private instance variable, you may have a problem with versioning. This is because for instance a CompoundModel uses only getters and setters to manipulate its model. As a result, if you apply operations other than getters and setters on your model you have to version it explicitly.

If you want to be completely safe, consider retrieving the model again from the component using
getModelObject().getObject(null) (wicket 1.2.2).

I hope this makes it a bit clearer how the Back button based on Undoable changes is working.

  • No labels