こんにちは、この講義では Seaside でのコンポーネントの
合成のパワーを見ようと思います。
再利用可能で状態を持つコンポーネントを 定義しました。
そこで それらのコンポーネントを組み合わせて
複雑なアプリケーションを構築します。 前にも言った通り
アプリケーションとはルートコンポーネントです。
アプリケーションのライブなデバッグを 楽しんでみましょう。
Seaside でのコンポーネント合成の
3 つの重要なメカニズムを復習します。
1つ目は、コンポーネントの集約です。
覚えているでしょうか 以前 2 つのカウンターを定義しました。
1 つは通常の、こんな感じのカウンターと
もう 1 つは、ツイッターのカウンターでした。
よろしいですか？
カウンターのコンポーネンントを 1 つのビューに集約して
マルチカウンターアプリケーションを 構築するには、どうしたらよいでしょう？
1 つのページ内に沢山のカウンターを 表示したいのです。
例えば、この + をクリックすると
このカウンターの値のみが増加します。 いいですね？
沢山のコンポーネントを同じページに 集約するのは簡単です。
これを Seaside ではどうするのでしょう？ とても単純です。
いつもの通り コンポーネントのサブクラスを定義します。
WAMultiCounter です。
インスタンス変数として、counters を定義します。 カウンターのコレクションです。
initialize メソッドでは ここでは 5 つのカウンターのコレクションを作ります。
いいですか？
そして renderContentOn: メソッドを定義します。
counters コレクションを do: で列挙します。
そして各カウンターにそれぞれ
html オブジェクトで描画するようにします。 render: という特別なメソッドを使います。
render: メソッドは html オブジェクトのメソッドで
コンポーネントを与えて それを自分自身に描画します。
1 つ重要なメソッドがあります。 children です。
コンポーネントが他のコンポーネントを持つ時には
それらを renderContentOn: で描画するために
内部のコンポーネントを宣言しなければなりません。 そして children メソッドが
それらのコンポーネントのコレクションを 構築しなければなりません。
そのコンポーネントが描画すべき 全てのコンポーネントです。
ここでは counters コレクションを使います。
このメソッドを定義することは重要です。
というわけで、コンポーネントの集約
による合成の基本ルールは簡単です。
あるコンポーネントがそのサブコンポーネントを
インスタンス変数に持ちます。 今回の例では counters です。いいですね？
そして renderContentOn: メソッドでは
html オブジェクトに
render: メッセージを送り 各サブコンポーネントを描画します。
そして children メソッドを再定義して
全てのサブコンポーネントを宣言します。
この 3 つは必須です。
異なる種類のサブコンポーネントを 集約することもできます。
同じ種類である必要はありません。
これまでは単純にカウンターを 組み合わせただけですが
次はアプリケーションを作ります。 MyApp コンポーネントです。
initialize メソッドでは コレクションを作りますが
今回はコレクションに
Greeter（挨拶）アプリケーションを入れます。 前の講義で使ったものです。
そしてツイッターカウンターとカウンターの 3 つのコンポーネントです。
children メソッドは
全てのサブコンポーネントを作り
renderContextOn: では
children を列挙して 自分を描画するようにします。
つまり html render: each をします。
つまり、Greeter、ツイッターカウンター そしてカウンターについて
html render: します。
どうなるか見てみましょう。
ここに Greeter コンポーネントがあります。 ウェブページ上に表示されています。
ここに、ツイッターカウンターが表示されて
通常のカンターはここに表示されています。
これでコンポーネントを合成して それぞれが独立して動作します。
+ をクリックしたり Say Hello をクリックしたりなど
コンポーネントは完璧に動きます。
では、もう 1 つ次のステージに進みましょう。
Webページ上に 一度に全部のコンポーネントが欲しいのではなく
毎回 1 つずつ表示したいとします。 例えば、メニューでどのサブコンポーネントを
表示するか選択できるようにします。
通常は、グリーターだけがあればよくて
ここのツイッターカウンターをクリックした時だけ
ツイッターカウンターを表示します。
どうやって作ったらよいのでしょうか？ 簡単です。
MyApp アプリケーションに
インスタンス変数 selectedChild を追加します。
デフォルトでは最初のサブコンポーネントで
初期化します。
renderContentOn: メソッドを修正して
メニューを表示します。
どのサブコンポーネントを表示するかを 選択することができるようにします。
そして、 html render: をします。 ただし、表示したいサブコンポーネントだけです。
とても簡単です。
メニューでは順序なしリストを生成して
全てのサブコンポーネントを
インデックス付きで列挙して
サブコンポーネントごとにリスト要素を生成します。
ここでアンカーを生成します。
アンカーはクリック可能なリンクで
クリックするとコールバックが呼ばれます。 このブロックです。
つまり、仕組みは簡単で ユーザーに選択されたコンポーネントは
self children はコレクションなので
self children at: i
となります。
いいですか？ ここで i は
self children の中での
インデックスです。
全てのサブコンポーネントを列挙して
どのサブコンポーネントを表示するか あるいは表示しないのかを
制御することはとても簡単なのです。
2 つ目の合成のメカニズムは コールアンサー と呼ばれるものです。
これまでサブコンポーネントの集約を 見てきましたが
これからコールアンサーメカニズムを 見ていきます。
コンポーネント A のコードで
こんなコードを書くことを
コンポーネント A が コンポーネント B をコールする、と言います。
何が起こるかというと、ウェブページ上で コンポーネントB がコンポーネントAを覆い隠して
処理をします。
しばらくするとコンポーネント B が 「もう自分の処理は終わりです。」
「アンサーをして結果を返します。」 と言います。
ここで、結果は☆印です。
そしてこの結果が X に保存されます。
call component: B の結果として。
ここで、コンポーネント A が ウェブページ上にまた表示されて
コンポーネント A はこの結果を使って 何かをすることができます。
例をお見せしましょう。
ここにコンポーネントがあります。 カウンターです。
ここに Set Value ボタンを追加してあります。
Set Value をクリックするとユーザーは
カウンターの新しい値を入力できるとします。
Set Value をクリックすると 新しいコンポーネントが表示されて
Greeter コンポーネントのように 何かを入力することができて
OK をクリックすると それをカウンターの新しい値とします。
そしてカウンターコンポーネントに戻ります。
ここでカウンターの値はさきほどユーザーが 入力した値になっています。
いいですか？
これらの一連のコンポーネントを
どう実装するのでしょう？
まず、ツイッターカウンターです。 renderContentOn: メソッドに
ボタンを追加します。
Set Value ボタンです。
このボタンをクリックすると このコールバックが実行されます。
setCountToUserValue メソッドです。
SetCountToUserValue は ここで定義されています。
そこでは ダイアログボックスを用意します。
Seaside のダイアログボックス コンポーネントを使います。
ここでダイアログボックスを構成します。
「Enter a new value for the counter」 （カウンターの新しい値を入力してください）
デフォルト値は 0 で OK ボタンが必要です。
次にやることは
この行です。 この行が興味深い要素です。
ここです。
ここで、このコンポーネント、つまり self は
（覚えていますか？ self はツイッターカウンターです）
さきほど構築したダイアログボックスをコールします。
そして結果を受け取ります。
結果はカウンターの新しいカウント値に使います。
そして最後に
count つまりこのカウンタの値は
ユーザーから入力された値を数値として 新しい値とします。
とても単純です。
内部的には この WAInputDialog の動作を
確認することができます。
Seaside の再利用可能なコンポーネントで とても古典的なものですが
1 つ特別な機能があります。
それは、answer: メソッドで 結果を送り返すことです。
このコンポーネントの
renderContentOn: メソッドを見ると
OK ボタンをクリックすると
それは実際には submit ボタンで
コールバックとなるブロックを持っています。 そのコールバックでは
self answer: value をします。
つまり、このコンポーネントは結果を
自分をコールしたコンポーネントに 送るということです。
実際、コールされた場所に制御を戻し
結果を送ります。
コール/アンサーメカニズムを見てきました。
最後のコンポーネント合成のメカニズムを 見ようと思います。
タスクメカニズムです。
本当に、とても簡単です。
タスクはコンポーネントに似ていますが UI 部品ではありません。
したがって、renderContentOn: も HTML の生成もありません。
複数のコンポーネントを指揮して
時間の経過に従って どう実行していくかを記述します。
まず最初にこのコンポーネントが実行して 次にあのコンポーネントで、という具合に。
スクリーンの裏側のコールアンサー のようなものです。
Adder というタスクを定義します。
全てのタスクは go メソッドを持っています。
ここで、例えばこのタスクでは
ユーザーに数値を入力するよう訊きます。 self request: firstNumber です。
そして2番目の数値を入力するよう訊いて value2 とします。
そして、それら 2 つの数の合計を
ここで伝えます。いいですね？
最後の行では、このコンポーネントを アプリケーションとして登録します。
ブラウザからアクセスするための 古典的なウェブアプリケーションです。
内部では
request: メソッドはどのように 実装されているのでしょう？
request: メソッドは
内部的にはコールアンサーを使っています。
前のスライドでの
ここで request: をすると、内部的に
もう 1 つのコンポーネンントをコールします。
元からあるものではなくて WAInputDialog コンポーネントをコールします。
そのコンポーネントが文字列を表示して
このコンポーネントがリクエストした側に 結果を返します。
この例では、誰がリクエストをしたでしょう？ Adder です。
なので、Adder が値を value1 に受け取ります。
同じことが value2 にもあって そして inform: します。
inform: がどう実装されているかを見ると
inform: もコール/アンサーを使っていますが 別のコンポーネントで行なっています。
FormDialog です。
self call: FormDialog すると
文字列を OK ボタン付きで表示して
「はい、これで彼はこの文字列を見ました」 と言います。
これらの合成の全ての形態において
知っておくべきことは
HTTP リクエストや URL や
パースや設定などについて一切話さなかった ということです。
リクエストのルーティングについても 全く話していません。
次のページへのリンクなどは 一切何もしていません。
お話したことは、コンポーネントのことです。
状態を持つコンポーネントを使って 合成できること
3 つの形態の合成があること
コンポーネントは他のコンポーネントを カプセル化すること
コンポーネントは他のコンポーネントを コールすることができること
そして Seaside でワークフローを定義して コンポーネントを 1 本の線として繋げることです。
これはとても強力で
ライブなデバッグが可能になっています。