当社Webサイト

カテゴリー

Amazonサーチ

  • 09/04
    Java, Wicket post by 武輪 恭代 @ 2009 年 9 月 4 日 11:35

    ども、武輪です。

    Webアプリケーションでは、データを登録したり編集したり削除したりといった操作が日常茶飯事です。
    そして「削除ボタンを押した時にいきなり削除するのではなく、confirmメッセージを出す」というのはよくあるパターンですね。

    さてWicket小話その2では、「Wicketで確認メッセージを出すにはどうすればよいのか?」について、実際のコードを踏まえて試してみようと思います。

    非Ajaxボタンの場合

    例えばこんなボタンがあるとします。
    confirmサンプル

    これはcountボタンを押すと画面上に何回クリックしたのかを表示する簡単なサンプルです。

    Confirm.html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    <?xml version="1.0" encoding="UTF-8"?>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    	<title>Wicket Examples - confirm</title>
    </head>
    <body>
    	<span wicket:id="result" /> 回目
    	<form wicket:id="form">
    		<input wicket:id="count" type="submit" value="count" />
    	</form>
    </body>
    </html>

    Confirm.java(非AjaxButton編)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    
    public class Confirm extends WebPage {
     
    	private int count = 0;
     
    	/**
    	 * コンストラクタ
    	 */
    	public Confirm() {
    		add(new Label("result", new PropertyModel<Integer>(this, "count")));
    		Form<?> form = new Form<Confirm>("form");
    		Button countButton;
    		form.add(countButton = new Button("count") {
    			private static final long serialVersionUID = 1L;
     
    			@Override
    			public void onSubmit() {
    				super.onSubmit();
    				count++;
    			}
     
    		});
    		add(form);
    	}
     
    	/**
    	 * countを取得します。
    	 * @return count
    	 */
    	public int getCount() {
    		return count;
    	}
    }

    html側で対応

    html側に直接JavaScriptを埋め込むことで対応。
    Javaのコードには一切変更点はありません。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    
    <?xml version="1.0" encoding="UTF-8"?>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    	<title>Wicket Examples - confirm</title>
    </head>
    <body>
    	<span wicket:id="result" /> 回目
    	<form wicket:id="form">
    		<input wicket:id="count" type="submit" value="count" onclick="return confirm('カウントしますか?')" />
    	</form>
    </body>
    </html>

    【メモ】
    9行目:
    onclickイベントを追加しただけです。

    Java側で対応

    SimpleAttributeModifierを使うことで、任意のアトリビュートを付加することができます。
    htmlには一切変更点はありません。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    
    public class Confirm extends WebPage {
     
    	private int count = 0;
     
    	/**
    	 * コンストラクタ
    	 */
    	public Confirm() {
    		add(new Label("result", new PropertyModel<Integer>(this, "count")));
    		Form<?> form = new Form<Confirm>("form");
    		Button countButton;
    		form.add(countButton = new Button("count") {
    			private static final long serialVersionUID = 1L;
     
    			@Override
    			public void onSubmit() {
    				super.onSubmit();
    				count++;
    			}
     
    		});
    		countButton.add(new SimpleAttributeModifier("onclick", "return confirm('カウントしますか?')"));
    		add(form);
    	}
     
    	/**
    	 * countを取得します。
    	 * @return count
    	 */
    	public int getCount() {
    		return count;
    	}
    }

    【メモ】
    22行目:
    countButtonにSimpleAttributeModifierをadd。

    html側で対応した場合も、Java側で対応した場合も、動作的には下図のように、countボタンをクリックすると確認ダイアログが表示されます。
    確認ダイアログのOKをクリックすれば、数字がインクリメントされ、キャンセルをクリックすれば数字は元のままです。
    Confirmサンプル


    Ajaxボタンの場合

    続きましてAjaxボタンの場合。
    動作的には変わらないのですが、先ほどのサンプルコードを下記のように、ButtonではなくAjaxButtonを使用するように変更します。
    html側には変更はありません。(Wicketってこういうとこ便利ですよね)

    Confirm.java(AjaxButton編)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    
    public class Confirm extends WebPage {
     
    	private int count = 0;
     
    	/**
    	 * コンストラクタ
    	 */
    	public Confirm() {
    		final Label result;
    		add(result = new Label("result", new PropertyModel<Integer>(this, "count")));
    		result.setOutputMarkupId(true);
    		Form<?> form = new Form<Confirm>("form");
    		AjaxButton countButton;
    		form.add(countButton = new AjaxButton("count") {
    			private static final long serialVersionUID = 1L;
     
    			@Override
    			protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
    				count++;
    				target.addComponent(result);
    			}
     
    		});
    		add(form);
    	}
     
    	/**
    	 * countを取得します。
    	 * @return count
    	 */
    	public int getCount() {
    		return count;
    	}
    }

    【メモ】
    11行目:
    Ajaxを用いてコンポーネントを更新する場合は、setOutputMarkupIdをtrueにすることを忘れずに!

    こんな失敗

    非Ajaxボタンの時と同じように、onclickアトリビュート追加してやればいいんじゃないの?
     ↓
    html側に「onclick=”return confirm(’カウントしますか?’)”」をつけたしてみる。
     ↓
    カウントはされるが、確認ダイアログは出てこない。あれ?
     ↓
    生成されたhtmlソースを見てみる
     ↓

    <input type="submit" value="count" onclick="var wcall=wicketSubmitFormById('ida', '?wicket:interface=:5:form:count::IActivePageBehaviorListener:0:&amp;wicket:ignoreIfNotActive=true', 'count' ,null,null, function() {return Wicket.$$(this)&amp;&amp;Wicket.$$('ida')}.bind(this));;; return false;" name="count" id="idb"/>

     ↓
    Ajax用のonclickになってるよーorz
     ↓
    じゃあ、SimpleAttributeModifierを使うパターンは?
     ↓
    確認ダイアログは出るけど、OKクリックしてもカウントアップされない
     ↓
    生成されたhtmlソースを見てみる
     ↓

    <input type="submit" value="count" name="count" id="id3" onclick="return confirm('カウントしますか?')"/>

     ↓
    見事Ajax用のonclickイベントはconfirmに置き換わってる
     ↓
    そりゃそうだorz

    力技で解決

    Ajaxで中身を入れ替える以上、onclickアトリビュートをいじるのはややこしそうなので、とっても強引に解決してみました。

    もう独自にConfirmウィンドウ実装しちゃえばいいんじゃね?
    というわけで、できたのがこんな画面。

    Confirmサンプル

    Wicket-extensionにはいくつかの便利なAjaxコンポーネントが備わっています。
    そのうちの一つであるModalWindowを使って、強引に確認ダイアログのようなものを表示させてみました。

    追加したのは下記3ファイル(ConfirmModalWindowPage.html、ConfirmModalWindowPage.java、IConfirmPage.java)。

    ConfirmModalWondowPage.html

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
    <html>
    <head>
    	<meta http-equiv="content-type" content="application/xhtml+xml;charset=UTF-8" />
    	<title>ModalWindow</title>
    </head>
    <body>
    	<div wicket:id="info">メッセージ</div>
    	<form wicket:id="confirmForm">
    		<div style="padding:1em; text-align:center">
    			<input type="submit" wicket:id="ok" value="OK" />
    			<input type="submit" wicket:id="cancel" value="キャンセル" />
    		</div>
    	</form>
    </body>
    </html>

    ConfirmModalWondowPage.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    
    public class ConfirmModalWindowPage extends WebPage {
     
    	@SuppressWarnings("unchecked")
    	public ConfirmModalWindowPage(final IConfirmPage page, final ModalWindow confirmModalWindow, String info) {
    		add(new Label("info", new Model<String>(info)));
     
    		Form confirmForm;
    		add(confirmForm = new Form("confirmForm"));
     
    		// OKボタン
    		confirmForm.add(new AjaxButton("ok") {
    			private static final long serialVersionUID = 1L;
    			@Override
    			public void onSubmit(AjaxRequestTarget target, Form<?> form) {
    				boolean result = page.ok(target);
    				if(result){
    					// 処理に成功した時の動作
    					confirmModalWindow.close(target);
    				} else {
    					// 処理に失敗した時の動作
    				}
    			}
    		});
     
    		// キャンセルボタン
    		confirmForm.add(new AjaxButton("cancel") {
    			private static final long serialVersionUID = 1L;
    			@Override
    			public void onSubmit(AjaxRequestTarget target, Form<?> form) {
    				// キャンセルボタンが押されたときの処理
    				confirmModalWindow.close(target);
    			}
    		});
    	}
     
     
    	/**
    	 * 確認用ModalWindowを取得する.
    	 * @param id
    	 * @param page
    	 * @return
    	 */
    	public static ModalWindow getConfirmModalWindow(String id, final IConfirmPage page, final String info){
    		final ModalWindow modal = new ModalWindow(id);
    		modal.setCssClassName(ModalWindow.CSS_CLASS_GRAY);
    		modal.setInitialHeight(150);
    		modal.setInitialWidth(350);
    		modal.setResizable(false);
    		modal.setTitle("確認メッセージ");
    		modal.setCookieName("confirm_window");
    		modal.setPageCreator(new ModalWindow.PageCreator() {
    			private static final long serialVersionUID = 1L;
    			public Page createPage() {
    				return new ConfirmModalWindowPage(page, modal, info);
    			}
    		});
     
    		modal.setWindowClosedCallback(new ModalWindow.WindowClosedCallback() {
    			private static final long serialVersionUID = 1L;
    			public void onClose(AjaxRequestTarget target) {
    				page.refleshComponent(target);
    			}
    		});
     
    		modal.setCloseButtonCallback(new ModalWindow.CloseButtonCallback() {
    			private static final long serialVersionUID = 1L;
    			public boolean onCloseButtonClicked(AjaxRequestTarget target) {
    				return true;
    			}
    		});
     
    		return modal;
    	}
    }

    【メモ】
    11~23行目:
    ConfirmウィンドウのOKボタンの定義です。実際の処理は、元のPageのokメソッドに記述しています。
    処理の成功/失敗を返すようにしておくと、処理に失敗した場合はConfirmウィンドウ内にエラーメッセージを表示するなんて使い方もできそうです。

    26~33行目:
    Confirmウィンドウのキャンセルボタンの定義です。Confirmウィンドウを閉じているだけです。

    43~73行目:
    Confirmウィンドウ用のModalWindowを取得するメソッドです。元のPageから取得するためstaticメソッドにしています。

    45~51行目:
    ModalWindowの設定です。
     setCssClassName → CSS_CLASS_GRAYかCSS_CLASS_BLUEを選択。デフォルトでは青色。
     setInitialHeight、setInitialWidth → ModalWindowのサイズ指定。確認ダイアログなので小さめにしておきます。
     setResizable → ModalWindowのリサイズを可能にするかしないか。
     setTitle → ModalWindowのタイトル文字列。
     setCookieName → クッキーの名前。このクッキーはModalWindowの位置などを記憶しています。

    58~63行目:
    ModalWindowが閉じられたときの処理を記述します。
    ここでは元画面のコンポーネントの更新を行っています。

    65~70行目:
    ModalWindowの×ボタンで閉じられたときの処理を記述します。
    ここでは特に何も行っていません。

    IConfirmPage.java

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    
    public interface IConfirmPage {
    	/**
    	 * 画面の更新
    	 * @param target
    	 */
    	public void refleshComponent(AjaxRequestTarget target);
     
    	/**
    	 * OKボタンを押した時の処理
    	 * @param target
    	 * @return 処理が成功したかどうか true:成功 false:失敗
    	 */
    	public boolean ok(AjaxRequestTarget target);
     
    	/**
    	 * キャンセルボタンを押した時の処理
    	 * @param target
    	 */
    	public void cancel(AjaxRequestTarget target);
    }

    【メモ】
    Confirmウィンドウを表示するPageでこのインターフェースを実装します。

    このインターフェースを実装するようにConfirm.javaを以下のように修正しました。
    Confirm.java(修正後)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    
    public class Confirm extends WebPage implements IConfirmPage{
     
    	private int count = 0;
    	private Label result;
     
    	/**
    	 * コンストラクタ
    	 */
    	public Confirm() {
    		add(result = new Label("result", new PropertyModel<Integer>(this, "count")));
    		result.setOutputMarkupId(true);
    		Form<?> form = new Form<Confirm>("form");
     
    		final ModalWindow confirmModalWindow = ConfirmModalWindowPage.getConfirmModalWindow("confirmModalWindow",
    				Confirm.this, "カウントしますか?");
    		form.add(confirmModalWindow);
     
    		AjaxButton countButton;
    		form.add(countButton = new AjaxButton("count") {
    			private static final long serialVersionUID = 1L;
     
    			@Override
    			protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
    				confirmModalWindow.show(target);
    			}
     
    		});
     
    		add(form);
    	}
     
    	/**
    	 * countを取得します。
    	 * @return count
    	 */
    	public int getCount() {
    		return count;
    	}
     
    	/**
    	 * Confirmウィンドウでキャンセルが押されたときの処理
    	 */
    	public void cancel(AjaxRequestTarget target) {
    		// キャンセル時の処理
    	}
     
    	/**
    	 * ConfirmウィンドウでOKが押されたときの処理
    	 */
    	public boolean ok(AjaxRequestTarget target) {
    		count++;
    		return true;
    	}
     
    	/**
    	 * 画面上のコンポーネントの更新
    	 */
    	public void refleshComponent(AjaxRequestTarget target) {
    		target.addComponent(result);
    	}
    }

    【メモ】
    14~16行目:
    Confirmウィンドウ用のModalWindowを取得します。

    23~25行目:
    countボタンがクリックされたときの処理。ModalWindowを表示します。

    40~45行目:
    Confirmウィンドウでキャンセルボタンを押されたときの実処理を記述します。ここでは特に何もしていません。

    50~53行目:
    ConfirmウィンドでOKボタンが押されたときの実処理を記述します。ここではcountをインクリメントしています。

    58~60行目:
    Ajaxで画面の更新を行います。ここではresultラベルの表示を更新しています。

    Confirm.html(変更後)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    
    <?xml version="1.0" encoding="UTF-8"?>
    <html xmlns="http://www.w3.org/1999/xhtml">
    <head>
    	<title>Wicket Examples - confirm</title>
    </head>
    <body>
    	<span wicket:id="result" /> 回目
    	<form wicket:id="form">
    		<input wicket:id="count" type="submit" value="count"/>
    		<div wicket:id="confirmModalWindow">削除ConfirmModalWindow</div>
    	</form>
    </body>
    </html>

    【メモ】
    10行目:
    Confirmウィンドウ表示領域の確保。

    まとめ

    とりあえずこんな感じの実装で、AjaxButtonの場合でも確認ダイアログっぽいものは出すことができました。

    もっともこのままだと、1つの画面でOK、キャンセルが押されたときの処理が1つしか記述できないので、実際汎用的に使おうと思えば、さらなる改造が必要ですけどね:)

    動作環境

    今回のサンプルソースは以下の環境で動作させています。

    バージョン
    JDK 1.6.0_13
    Apache Tomcat 6.0.16
    Apache Wicket 1.4.0

    あわせて読みたい

    Tags:

2 Responses

WP_Cloudy