Friday, January 11, 2008

A Wicket 1.3 Ajax button with a confirmation dialog

This example is exactly the kind of thing I like to post: a small example that solves a general problem. In this case it's how to add a confirmation dialog to a Wicket Ajax button. This is harder than it needs to be because it's one of the few areas in Wicket that I feel is not adequately documented (the area in question being marrying Javascript with Wicket). My use case is the usual deletion warning: "Are you sure you want to delete?". This might work for Wicket 1.2.6, but I've only tested it on 1.3.


The solution is to add an AjaxCallDecorator to your button, as follows:


MyAjaxDeleteButton.java


public class MyAjaxDeleteButton extends IndicatingAjaxButton
{
private static final long serialVersionUID = -8294444630452310166L;

private final IModel confirm;

public MyAjaxDeleteButton(String id, IModel confirm, IModel label)
{
super(id, null);
this.setModel(label);
this.confirm = confirm;
}

@Override
protected IAjaxCallDecorator getAjaxCallDecorator()
{
return new AjaxPreprocessingCallDecorator(super.getAjaxCallDecorator())
{
private static final long serialVersionUID = 7495281332320552876L;

@Override
public CharSequence preDecorateScript(CharSequence script)
{
return "if(!confirm('" + confirm.getObject()
+ "')) return false;" + script;
}
};
}
}

Note that the confirmation message is passed in as a model. This is because, if you use a localized string, and you look it up using Localizer.getString() in the constructor of your page, Wicket will complain because it can't properly navigate the component hierarchy to resolve localized properties until after all components have been constructed. If you use a model, you won't have this problem.


Example of how to use it:


MyDataPanel.java


...
private class DeleteButton extends MyAjaxDeleteButton
{
private static final long serialVersionUID = 4966354164332401574L;

private DeleteButton()
{
// MyDataPanel.properties contains:
// confirmdelete=Are you sure you want to delete?
// delete=Delete
super("delete", new StringResourceModel("confirmdelete", MyDataPanel.this,
null), new StringResourceModel("delete", MyDataPanel.this,
null));
}
@Override
protected void onSubmit(AjaxRequestTarget target, Form form)
{
// Update the feedback panel
final MyBasePage page = (MyBasePage) this.getWebPage();
target.addComponent(page.getFeedback());

// Do the actual deletion here...

// Update the data panel
target.addComponent(MyDataPanel.this);
}
}
...

Note that I'm adding my feedback panel to the Ajax target. This way, any errors will be shown as well. This does depend on calling feedback.setOutputMarkupId(true) in your base page.

5 comments:

bhaskar said...

If you really wan't a consistent look and feel, use a ModalWindow, instead of a jvascript.alert box.
For my case, I created a ConfirmActionButton, which when clicked opens a ModalWindow that ask's for the confirmation.
Another advantage of this, is that you can make this work even if javascript is disabled.
The javascript.alert will not work if javascript is disabled on the client.

Julian Sinai said...

Bhaskar, thanks for the tip. Do you have the code for your ConfirmActionButton posted anywhere?

vmoreau said...

Hi, I did a test of this with wicket 1.4m3 and I have some trouble with IE6 SP2.It works fine in Firefox or IE7. (The initial javascript also works properly if I dont add the confirm call).

Is someone having the same problem ?

vmoreau said...

Little précision this is with a AjaxLink, not an AjaxButton

will824 said...

Thanks a lot for this great example on how to use an Ajax Button with a confirmation.

I think all over the web this is the most ilustrated and clear example.

Cheers!