Thursday, December 22, 2011

Wicket 1.4 modal window not hiding contents in hierarchy on close! :o

Looks like plenty of the posts are for myself not to forget about 'specific' issues found in the frameworks during the development. Well, wicket 1.4.(18?) this time.

The problem
So, we have a modal window. There is a form (call it form2) in this modal window. For the last, modal window itself is within a form (call it form1). Hierarchy looks like form1 <- modal window <- form2.

Both form1 and form2 have validators for their components, and both do have submitting components.

When the MW is shown and visible, everything works just fine according to the Nested form rules of the wicket. However, as you close the modal window by any means, the form2 will still be validated on the submit of form1! However, as it is _not_ visible on client (modal window is closed) we can not show to user what's wrong, they actually don't have access to the components failing.

Wicket modal windows do close by means of Ajax. E.g. when the actual DOM element is hidden by clicking cross, or outside modal window, or close button, wicket issues an ajax-call to server to notify it that window is no more visible.

This call is handled by WindowClose behavior, the subclass of AbstractAjaxDefaultBehavior.For greater detail you may take a look at it in the ModalWindow class. What this behavior does in the original implementation:
  1. Reset shown flag to false, so window will be opened again when next time show(target) is invoked.
  2. In case of the modal window showing pages, not components, do cleanup of the pagemap.
  3. Invoke WindowCloseListener instance (if any has been set to this ModalWindow).
That's it. It means that closing modal window does not hide the components inside it from the wicket hierarchy, and they are treated as visible however they are not really!

The solution
General solution for me was to hide content on the modal window close action.
2 approaches:
  1. Local.
    Just implement the windowClosedCallback, pass it to the instance of modal window, and in it, do hide your content on the close action.
    Bad things are that if you are developing a reusable component, you may want clients of this component to use windowClosedCallback for their own stuff.

  2. General.
    Subclass a ModalWindow. Say, MyModalWindow extends ModalWindow

  3. Override newWindowClosedBehavior() of it

  4. In this method, create your own ajax behavior doing same thing as wicket does, plus hide component if it is there. Pass it on to the return. Voila, we are done.
Now the components will be really hidden, and you won't be getting nested forms edgecase validation issues (or whatever else hiding there - I don't really know).

That's the code snippet:

//... inside our ModalWindow subclass, MyModalWindow
private class MyModalWindowClosedBehavior extends AbstractDefaultAjaxBehavior
implements
IWindowClosedBehavior {
private static final long serialVersionUID = 1L;

@Override
protected void respond(AjaxRequestTarget target) {
respondOnWindowClosed(target); // that'd be a call to the wicket's MW behavior.
if (getContent() != null) {
getContent().setVisible(false);
}
}

@Override
public CharSequence getCallbackScript() {
return super.getCallbackScript();
}
}

@Override
protected IWindowClosedBehavior newWindowClosedBehavior() {
return new PoseidonWindowClosedBehavior();
}

No comments: