Wicket: how to write a reusable modal window popup

Wicket is a Java framework for creating web frontends. It allows you to create a webpage in Java, combining all kinds of components into one page.

In many projects, a subset of user interface elements are repeated over the site. Depending on your project, you can have different selection panels or popups that are used multiple times.

In the following tutorial I describe a method to write your own application specific reusable modal window popup.

Start!

For this example, we use the ModalWindow component from Wicket Extensions.If you don’t know what a ModalWindow looks like, you can see a demo. (hint: try to drag and drop it or resize it)

The example from Wicket extensions

The default example from the Wicket Extensions site contains no real reusable components, besides the ModalWindow itself. The Page inside the ModalWindow is tightly coupled to the ModalWindow. It closes its containing ModalWindow and sets a result parameter of the parent page. The components can only be reused in exactly the same way.

How can we make this more reusable?

We can make this more reusable by making the content of the ModalWindow not aware of the ModalWindow itself. The content does not know what will happen when the containing Form (or other data) is submitted. But how can we do that? The magic words are: abstract methods.

Before we start, you can download the code here, so you can get the complete picture.

The image on the right contains the structure of the page and the components we are creating. The page contains a modal window, which in turn contains a content panel.

The Content Panel

Lets start with the content panel inside the ModalWindow popup. This panel contains a selection link, a selection button and a cancel button, created in the constructor:

public SelectContentPanel(String id) {
  super(id);
 
  // Create the form, to use later for the buttons
  Form form = new Form("form");
  add(form);
 
  // Add some example 'selection' methods, to show as example
  // You can also use a full fledged form, I left that as an
  // exercise for the reader :-)
  add(new AjaxLink("selectionLink"){
    public void onClick(AjaxRequestTarget target) {
      onSelect(target, new String("Selection using the link."));
    }
  });
 
  form.add(new AjaxButton("selectionButton"){
    protected void onSubmit(AjaxRequestTarget target, Form form) {
      onSelect(target, new String("Selection using the button."));
    }
  });
 
  // Add a cancel / close button.
  form.add(new AjaxButton("close") {
    public void onSubmit(AjaxRequestTarget target, Form form) {
      onCancel(target);
    }
  });
[..]

HTML:

  <a>Select something</a> 
<form>
<input type="button" value="Select something else" />
<input type="button" value="Close" />
  </form>

You probably noticed the calls to onSelect() and onCancel(). These are the abstract methods we are declaring:

abstract void onCancel(AjaxRequestTarget target);
abstract void onSelect(AjaxRequestTarget target, String selection);

These methods are abstract, that way the code that is calling this panel must implement these methods. Because we are calling these methods, this effectively gives the result to the consuming code, without ever knowing what will happen with it.

To use abstract methods, the class itself must be abstract:
public abstract class SelectContentPanel extends Panel

The Modal Window

The Modal Window initializes itself with some values. You can just as easily set these values from the calling class, but for now we keep it local.

public SelectModalWindow(String id) {
    super(id);
 
    // Set sizes of this ModalWindow. You can also do this from the HomePage
    // but its not a bad idea to set some good default values.
    setInitialWidth(450);
    setInitialHeight(300);
 
    setTitle("Select something");
 
    // Set the content panel, implementing the abstract methods
    setContent(new SelectContentPanel(this.getContentId()){
        void onCancel(AjaxRequestTarget target) {
            SelectModalWindow.this.onCancel(target);
        }
 
        void onSelect(AjaxRequestTarget target, String selection) {
            SelectModalWindow.this.onSelect(target, selection);
        }
    });
}

HTML:

  <a>Select something</a> 
<form>
<input type="button" value="Select something else" />
<input type="button" value="Close" />
  </form>

The setContent() function call is interesting. Here we create a new SelectContentPanel and implement the methods onCancel() and onSelect(). It is possible to do some extra actions here, but here it is passed one-on-one to the abstract methods of the ModalWindow itself:

abstract void onCancel(AjaxRequestTarget target);
abstract void onSelect(AjaxRequestTarget target, String selection);

The Home Page

The Home Page itself can now instantiate the ModalWindow, override the methods and do something with the data. One big advantage is that you can see from the HomePage class directly what is going to happen after the user selects something or cancels the action:

final ModalWindow selectModalWindow = new SelectModalWindow("modalwindow"){
 
  void onSelect(AjaxRequestTarget target, String selection) {
    // Handle Select action
    resultLabel.setModelObject(selection);
    target.addComponent(resultLabel);
    close(target);
  }
 
  void onCancel(AjaxRequestTarget target) {
    // Handle Cancel action
    resultLabel.setModelObject("ModalWindow cancelled.");
    target.addComponent(resultLabel);
    close(target);
  }
 
};
 
add(selectModalWindow);

Further customization

This example is just passing a String around, you can change it to a more specific class. You can change the onSelect to something more appropriate for your use case. You can even put a Form inside the panel and return the resulting object of the form.

Download

Download the complete example project and start experimenting!
(run with mvn jetty:run and connect to http://localhost:8080/demo)

If you think this is useful, please let me know in the comments.

28 Responses to “Wicket: how to write a reusable modal window popup”

  1. Daan June 6, 2008 at 14:55 #

    Thanks!

    So basically you give a String in the ModalWindow constructor, that you display together with a confirm button. Nice :-)

  2. bhaskar June 6, 2008 at 13:34 #

    Great post.
    I did something very similar in my code.
    I reuse the modal window for Confirming actions, and then displaying the corresponding results.
    Also based on the outcome of the action, I can trigger reloading of certain parts of the webpage, after the window is closed.

  3. uwe schaefer August 30, 2008 at 14:05 #

    Nice! would you mind sharing this?

  4. Daan August 30, 2008 at 21:40 #

    This is directed at me? :-)
    I have no problem with this appearing on the Wicket wiki.

  5. Steve Hiller September 19, 2008 at 15:35 #

    Hi Daan,

    In my Wicket project, I’m trying to use your modal window technique since I think it is pretty cool. It seems to work fine except for one issue. In the implemented onSelect() method, I am trying to use a RequestTarget via the call RequestCycle.get().setRequestTarget(). The implementation of the IRequestTarget writes an Adobe PDF to the WebResponse output stream. Unfortunately, the PDF does not appear in the browser, and the modal window stays. The system out displays the following:

    [9/19/08 9:31:35:205 EDT] 00000032 SystemOut O 09:31:35,205 DEBUG RequestCycle:589 – replacing request target [AjaxRequestTarget@6739162 markupIdToComponent [{}], prependJavascript [[]], appendJavascript [[var win;
    try {
    win = window.parent.Wicket.Window;
    } catch (ignore) {
    }
    if (typeof(win) == "undefined" || typeof(win.current) == "undefined") {
    try {
    win = window.Wicket.Window;
    } catch (ignore) {
    }
    }
    if (typeof(win) != "undefined" && typeof(win.current) != "undefined") {
    window.parent.setTimeout(function() {
    win.current.close();
    }, 0);
    }]]

    Any idea on what I did wrong?

    Thanks,
    Steve

  6. Daan September 19, 2008 at 15:45 #

    Hi Steve,

    I think your RequestTarget clashes with the AjaxRequestTarget of the ModalWindow. Thus the AjaxRequestTarget is never called. To really find the issue, some more code is needed.

    - Daan

  7. Steve Hiller September 23, 2008 at 14:56 #

    Hi Daan,

    Sorry I haven’t replied sooner but I had my first demo of our Wicket-based project for our bosses yesterday (BTW, it went well). What would be the best way to get you some code? Create a quickstart project like the ones used at the Wicket JIRA?

    Steve

  8. Steve Hiller September 23, 2008 at 20:35 #

    Ok, I think I came up with a solution (kludge?) while putting together the quickstart example.

    1) I create a WindowClosedCallback for the modal window.
    2) In the onClose() event of the callback, I use the setResponsePage() method to go
    to a new WebPage.
    3) In the constructor of the new WebPage, I put the call to
    RequestCycle.get().setRequestTarget(), which then uses an implementation of the
    IRequestTarget to write an Adobe PDF to the WebResponse output stream.

    This all seems to work fine though I am hearing a double click from the browser as the PDF is displayed. Do you think this is a good solution, Daan?

  9. Daan September 24, 2008 at 10:29 #

    Hi Steve,

    Great that you got it working! I have looked at your code. It seems not to be a far-fetched ‘hack’, but there may be other solutions which are cleaner.

    There are some related posts on the Wicket mailinglist:

    For educational purposes, you can ask your question on the mailing list. I’m very curious if someone can come up with a better solution.

  10. Keith Mcneill January 30, 2009 at 09:08 #

    Hi Daan, thanks for the demo. I’m currently putting together a small Wicket App and this is exactly what I need. I find that most Wicket books are quite poor on explaining non standard components (i.e. anything that’s not a regular HTML form component).

    Have you ever considered writing a book? I for one would buy it! ;-)

  11. Daan January 30, 2009 at 10:01 #

    Hi Keith,

    Maybe I will write a book in the future ;-)
    I have no idea what the subject should be…

  12. dg May 14, 2009 at 14:04 #

    Awesome. Thanks!!! I enjoyed it thoroughly … and will try to put into action!

  13. CK June 8, 2009 at 13:50 #

    I’m trying to open a pop-up window similar to the post except that I do not want to use Ajax. The pop-up window is actually contain another form like add an item. How should I do this without using Ajax ? Thanks.

  14. Raphael August 25, 2009 at 19:35 #

    Hi Daan,
    Your example is pretty cool, but I need some modifications in that and I do not know how to get it.

    How can I create a reusable content? I mean, you did your example with “SelectContentPanel” as a “default” content. I wanna make this reusable too.

    What I need is create something like a method in which I can set what class(extending Panel) I’ll put in “setcontent”.

    Is there any possibility to achieve this?

    Thanks in advance.

  15. Daan August 26, 2009 at 11:22 #

    Hi Raphael,

    You could create a panel with abstract methods and pass that to the Modal Window constructor. That way, you wouldn’t need the abstract methods in the Modal Window itself.

    - Daan

  16. Phil September 15, 2009 at 12:04 #

    Hi Daan,

    I got some strange behavior.. I have a page displaying a panel where an hibernate Entity A is shown for editing. This entity also got a OneToMany relation to another Entity B (load this into an ArrayList), so i have to put this in an ListView. In this ListView i put Links to open a ModalWindow (each link for one List entry). In this ModalWindow you can edit Entity B which got an compound PrimaryKey. To search for one of its parts i use a second ModalWindow. This works fine the Window opens i do some searching, chose one, the window is closed an the field is set in the first Modal. Back on the First modal i may do some other entries to Entity A… so now i hit ok and the onOk(AjaxRequestTarget target, AEntity aEntity); is called, which adds the entity to the ArrayList… now it gets strange when i syser.out this ArrayList at this moment everthing is fine the entity is added, but then it calls close(target) which lead to the setWindowClosedCallback… where i repaint the ListView.. and here the new Entry in the ArrayList is missing….

    I dont know whats wrong here an where the entry is gone.. Any idea? Is it some Wicket problem or my fault???

    Thanks

  17. Phil September 16, 2009 at 08:44 #

    Ohhh yaeee I found it….

    I added .setPageMapName(“zzz”) to the ModalWindows now it works!!!
    But I dont know what setPageMapName really does, only it got somthin to do when u got more then one Modal. Anyone knows and can explain???

  18. buddyprav October 21, 2009 at 15:04 #

    Hi Daan,

    This demo is pretty good.But while working with different pages.Giving browser specific message “Are you sure you want to navigate away from this page?”. Could you please gimme hint how to avoid this alert message.

    regards,
    Buddyprav

  19. Daan October 23, 2009 at 20:14 #

    Hi Buddyprav,

    Please see comment #23 for a method to redirect to another page. Let me know if it works for you.

    - Daan

  20. Chris Colman January 22, 2010 at 09:15 #

    Let’s say we had a need for another modal window. Does this mean that we need to create a new MyModalWindow.java and new MyContentPanel and the corresponding .html for each.

    i.e. 4 new artifacts for one new single ModalWindow.

    Is it possible to somehow create a framework whereby we only need 2 new wicket artifacts to implement a new modal window i.e. MyContentPanel.java and MyContentPanel.html?

  21. Daan January 22, 2010 at 11:20 #

    Hi Chris,

    Good question!
    You can easily create a simple framework with a ‘generic’ Modal Window. You probably do not even need a custom Modal Window, as you can add your MyContentPanel with abstract methods directly to the Modal Window.

    – Daan

  22. Chris Colman January 22, 2010 at 11:52 #

    Hmmm, I’m actually overriding the style of the modal form by injecting some different, but standard across the application, CSS to be used by the form. I should be able to override ModalWindow once and create a StyledModalWindow which handles the styling and ALL modals in the app use that instead of ModalWindow.

    Then I could possibly make StyledModalWindow’s constructor take a ‘ContentPanel’ base class as the content panel. All content panels then simply extend from ContentPanel (directly or indirectly). Maybe the ContentPanel base class could have some default onSubmit onCancel behaviour.

    Hmmm, nice.

  23. sumit March 17, 2010 at 15:02 #

    can u give me the implementation for TabContainer and Collapsible box

  24. DaHe March 31, 2010 at 17:52 #

    Does this example run with wicket 1.4.6 ?

    I’m trying to run it but it throws me a Exeption:

    MarkupNotFoundException: Markup of type ‘html’ for component ‘com.consisint.frontend.ajax.SelectModalWindow$1′ not found.
    ….. then sais me
    [MarkupContainer [Component id = content]]

    Do you know wich one is the reason of this? Is by the version of wicket?

  25. DaHe March 31, 2010 at 18:15 #

    I tried to run it on wicket 1.4.6 and 1.4.7 and does not work.

    The exeption it throws me is becouse We implement the class SelectContentPanel on SelectModalWindow, and wicket search its view, that should be SelectModalWindow$someid.html.

    I am trying to find a solution for this problem, but I didn’t found yet

  26. Daan March 31, 2010 at 23:43 #

    Hi DaHe,

    Do you have an inner class? Wicket tries to load the HTML of an anonymous class.
    Is the HTML next to the Java class or in a separate resources directory? Is the inner class anonymous or named?
    If you can’t solve it on your own, the mailing list is really helpful. You can search for previous solutions, and if you find nothing you can ask new questions.

    Daan

  27. sumit April 7, 2010 at 12:16 #

    i need to pass to form object from modal window to parent how can i do this .thanks

Trackbacks/Pingbacks

  1. Confluence: Altes wiki - November 5, 2009

    Wissen.wicket.linkListe…

    Blogs A Wicket Diary…

Leave a Reply