Apache Wikcet 7.x ユーザガイド7章邦訳+訳注

[原文](https://ci.apache.org/projects/wicket/guide/7.x/guide/single.html)

原文著者: Authors: Andrea Del Bene, Martin Grigorov, Carsten Hufe, Christian Kroemer, Daniel Bartl, Paul Borș, Tobias Soloschenko, Igor Vaynberg , Joachim Rohde

訳注は :sos: で示します。
訳語集

<dl>
  <dt>ページ部品</dt>
  <dd>org.apache.wicket.Component 原文では単に component<dd>
</dl>

# 7. ページ部品のライフサイクル

アプレットやサーブレットと同様にWicketのページ部品はその存在期間中に幾つかの段階が存在します。この章ではその段階それぞれについで分析し、ページ部品の段階が切り替わった時に実行される(ほとんど全ての)フックメソッドの作り方を学びます。

## 7.1. ページ部品のライフサイクル段階

ページ部品の生涯には以下の３つの基本的段階があります:

<dl>
<dt>初期化(initialization)</dt>
<dd>Wicketによりページ部品が生成され、描写前の準備が行われます。</dd>
<dt>描写中(Rendering)</dt>
<dd>この段階でWicketはページ部品のマークアップを生成します。(例えばMarkupContainerのような)子となるページ部品が含まれていた場合は、自身の描写に先立ってにそれら(:sos:子となるページ部品)の描写が完了するのを待たなくてはなりません。</dd>
<dt>除去中(Removing)</dt>
<dd>この段階はページ部品が明示的にページ部品の木構造から除去された時、例えば親となるページ部品がそのページ部品に対して`remove(component)`を呼び出したとかいった時に発生します。この段階は随意のもので(:sos:開発者がremoveしなきゃ呼ばれないよという意味です)、ページそのものについては発生することはありません。</dd>
</dl>

以下がページ部品のライフサイクル段階の状態遷移図になります。

![原文ページ転載](https://ci.apache.org/projects/wicket/guide/7.x/img/component-lifecycle.png)

ページ部品は一度除去された後、再びコンテナ(:sos:親あるいは他のページ部品)に追加することができますが、初期化段階が再び発生することはありません。

:warning: ページ部品のライフサイクルに関するより詳細な説明は`Component`クラスのJavaDocにあります。JavaDocの説明はまだ説明していない高度な内容を含んでいるため、この章では混乱を避けるために、後の章で説明する詳細な部分については省略しています。今は上に説明した簡略化されたライフサイクルについてだけ考えてください。

## 7.2. 各ライフサイクル段階に対応するフックメソッド

`Component`クラスにはそれぞれのライフサイクル段階における振る舞いをカスタマイズするための、オーバライド可能なたくさんのフックメソッドがあります。以下の表ではそれらのメソッドを実行されるライフサイクル段階別にグループ化したものになります(同じグループのものは実行順に並べられています):

| 段階 | 実行されるメソッド |
|:-:|:-:|
| __初期化__ |onInitialize |
| __描写中__ |onConfigure, onBeforeRender, onRender, onComponentTag, onComponentTagBody, onAfterRenderChildren, onAfterRender |
| __除去中__ |onRemove |

ではそれぞれの段階と実行されるフックメソッドについて詳しく見ていきましょう。

## 7.3. 初期化(Initialization stage)

この段階はページ部品のライフサイクルの一番最初に実行されます。初期化段階のページ部品はページ全体の木構造に既に組み込まれているため、安全に`getParent()`や`getPage()`のようなメソッドによって親となるページ部品やページそのものにアクセスすることが可能です。この段階で呼び出されるメソッドは`onInitialize()`のみです。このメソッドは任意の初期化処理を実行することができる特殊化されたコンストラクタとでも呼ぶべきものです。

また`onInitialize`は一般的なコンストラクタと同様に、オーバーライドした場合には親クラスのsuper.onInitializeをそのメソッド内で呼び出さなくてはなりません。(普通は場合は一番最初に呼び出ます)

## 7.4. 描写中(Rendering stage)

この段階はページ部品がWicketによって描写されるたび、典型的にはページがリクエストされた時やAJAXで更新された時に発生します。

### onConfigure メソッド

`onConfigure()` メソッドはページ部品の可視性(:sos:可視・不可視)や有効性(:sos:活性・非活性)を制御する最適の場所として導入されました。このメソッドは描写が始まる前に呼び出されます。6.1.章で言及したように`isVisible`と`isEnabled`はページ部品の描写中に何度となく呼び出されるため、これらのメソッドを直接オーバーライドすることは強く__推奨されず__、それよりも`onConfigure`を用いて状態を変更するべきです。(次の節で説明する)onBeforeRener メソッドはこれとはまったく逆に、ページ部品を不可視にした場合にはそもそも呼び出されないため、この手の処理を意図したものではありません。

### onBeforeRender メソッド

この段階でも最も重要なフックメソッドはおそらく`onBeforeRender()です。このメソッドはページ部品が描写処理に入る前に呼び出され、その子となるページ部品の構造を変更する最後の機会になります。

もしも子となるページ部品を追加したり除去したりしたいのであれば、ここが最適の場所です。次の例(LifeCycleStagesプロジェクト)では描写されるたびに異なる２つのラベルを交互に表示するページを作っています:

```java::
public class HomePage extends WebPage
{
	private Label firstLabel;
	private Label secondLabel;
	public HomePage(){
		firstLabel = new Label("label", "First label");
		secondLabel = new Label("label", "Second label");

		add(firstLabel);
		add(new Link("reload"){
			@Override
			public void onClick() {
			}
		});
	}

	@Override
	protected void onBeforeRender() {
		if(contains(firstLabel, true))
			replace(secondLabel);
		else
			replace(firstLabel);

		super.onBeforeRender();
	}
}
```

`onBeforeRender()`内のコードはささいなもので、ページ部品の木構造に`firstLabel`と`secondLabel`のどちらが存在するかを調べ、存在するものをもう一方のものと交換しています。
このメソッドは子となるページ部品の`onBeforeRender()`の呼び出しも行っているため、オーバーライドするときには`super.onBeforeRender()`を呼びださなくてはなりません。ただし`onInitialize()`とは異なり、カスタマイズコードでの変更を子となるページ部品の描写に反映するため、親クラスのメソッドの呼び出しは一番最後でなくてはなりません。

上の例ではF5キーを押すか、リロードボタンを押すかして描写中段階を発生させなくてはならない点には注意してください。

:no_entry: もしも親クラスの`onInitialize()`や`onBeforeRender()`の呼び出しを忘れた場合、Wicketは以下のメッセージでIllegalStateExceptionを発行します。

> java.lang.IllegalStateException: org.apache.wicket.Component has not been properly initialized. Something in the hierarchy of <page class name> has not called super.onInitialize()/onBeforeRender() in the override of onInitialize()/ onBeforeRender() method

### onComponentTag メソッド

`onBeforeRender(ComponentTag)`メソッドはページ部品のタグ引数の`org.apache.wicket.markup.ComponentTag`を通して自由に処理してもらうために呼び出されます。例えば`put(String key, String value)`メソッドや`remove(String key)`メソッドでタグ属性の追加/除去ができ、あるいはタグの名前そのものを`setName(String)で変更することもできます。(次のコードはonComponentTagExampleプロジェクトのものです):

Markup code:

```html::
<head>
  <meta charset="utf-8" />
  <title></title>
</head>
<body>		
  <h1 wicket:id="helloMessage"></h1>		
</body>
```

Java code:

```java::
public class HomePage extends WebPage {
   public HomePage() {
      add(new Label("helloMessage", "Hello World"){
         @Override
         protected void onComponentTag(ComponentTag tag) {            
            super.onComponentTag(tag);
            //Turn the h1 tag to a span
            tag.setName("span");
            //Add formatting style
            tag.put("style", "font-weight:bold");
         }
      });
    }
}
```

Generated markup:

```html::
<head>
  <meta charset="utf-8" />
  <title></title>
</head>
<body>		
  <span wicket:id="helloMessage" style="font-weight:bold">Hello World</span>		
</body>
```

`onInitialize`と同様に`onComponentTag `をオーバーライドするとなったなら、親クラスでもタグのカスタマイズをしているかもしれないので、親クラスの同じメソッドを呼び出すのを忘れてはなりません。`onConponentTag`のオーバーライドは特定のページ部品のタグをカスタマイズしなくてはならない場合には完璧ですが、同じコードを別のページ部品でも使いまわしたい場合にはこのフックメソッドの代わりにビヘイビアを使ったほうが良いでしょう。

タグ属性を操作する場合の`AttributeModifier`ビヘイビアの使い方については既に6.2.章で見ています。19.1章ではそのベースクラスである`Behavior`クラスがフックメソッドの` onComponentTag(ComponentTag)`の代替となる`onComponentTag(ComponentTag, Component)`というメソッドを持っているのを見ることになります。

### onComponentTagBody メソッド

`onComponentTagBody(MarkupStream, ComponentTag)`メソッドはタグの中身を処理するために呼ばれます。入力として`onComponentTag`と同様にページ部品のタグを表す`ComponentTag`を受け取ります。それに加えてクライアントに返却されるページのマークアップストリームを表す`MarkupStream`もあります。

`onComponentTagBody`では`Component`クラスの`replaceComponentTagBody`との組み合わせで特定状況にカスタマイズされたタグ内容を描写することができます。例えば(プロジェクトOnComponentTagExampleからのものです)ラベル部品が無効化されている場合にその内容の代わりに簡潔な説明文を表示することができます:

```java::
public class HomePage extends WebPage {
   public HomePage() {
      add(new Label("helloMessage", "Hello World"){
         @Override
         protected void onComponentTagBody(MarkupStream markupStream, ComponentTag tag) {

           if(!isEnabled())
               replaceComponentTagBody(markupStream, tag, "(the component is disabled)"); 
          else    
               super.onComponentTagBody(markupStream, tag);
         }
      });   
    }
}
```

親クラスの`onComponentTag`は標準のタグ内容描写処理を維持したい場合にだけ呼び出せば良い点に注意してください。（上の例ではページ部品(:sos:ラベル)が有効な場合のみになります）

## 7.5. 除去中(Removing stage)

この段階はページ部品がコンテナ(:sos:親あるいは他のページ部品)の木構造から取り除かれた時に発生します。この段階のフックメソッドは`onRemove()`のみです。もしもページ部品が描写中に必要だった何かしらのリソースをまだ保持していたりする場合、このメソッドをオーバーライドしてそれを開放することができます。

一度ページ部品が除去された後は、それを同じコンテナに追加するのも別のものに追加するのも自由です。バージョン6.18.0以降ではさらに、一度除去されたページ部品が再度別のコンテナに追加されるたびに呼び出される`onReAdd()`というフックメソッドが追加されています。`onInitialize`は__ページ部品がコンテナに初めて追加された時のみ__なのに対して、`onReAddd`は__一度除去された後、追加される度__である点には注意してください。

## 7.6. まとめ

この章ではWicketページ部品のライフサイクル各段階とそれぞれに提供されているフックメソッドについて見てきました。これらのメソッドをオーバーライドすることでページ部品の構造を動的に変更したり、カスタムページ部品の振る舞いを強化したりすることができます。