Skip to main content

This is documentation for Caché & Ensemble. See the InterSystems IRIS version of this content.Opens in a new tab

For information on migrating to InterSystems IRISOpens in a new tab, see Why Migrate to InterSystems IRIS?

モデル・ビュー・コントローラ

モデル・ビュー・コントローラ (MVC) は、ユーザ・インタフェース設計に使用される、なじみ深いアーキテクチャです。この章では、Zen で MVC を実装する方法、および MVC 機能を Zen ページに追加する方法について説明します。

データ・ソースから Zen ページへのデータのフローを簡素化するために、Zen には、データ・モデル (モデル) を定義し、それを中間オブジェクト (コントローラ) を介して Zen コンポーネントのセット (ビュー) に接続できるようにする、クラスのセットが用意されています。コントローラに関連付けられたモデルが変更されると、その変更情報はコントローラに接続されたすべてのビューに自動的に送信されます。

MVC の代表的な使用例をいくつか以下に示します。

  • データベース内の永続オブジェクトからロードされる、プロパティのセットを表示するフォームを作成します。このフォームには、各プロパティのデータ型に適したコントロールが自動的に表示されます。

  • フォーム内の値に基づくグラフを表示します。このグラフでは、ユーザがフォームの変更を送信するたびに、表示内容が自動的に更新されます。

  • サーバで計算された値を表すメータを表示します。ページを更新すると、これらのメータはサーバから得られた現在の値を使用して自動的に更新されます。

以下の図は、Zen MVC アーキテクチャの 3 つの構成部分である、モデル、ビュー、およびコントローラと、それらのオブジェクトでコードが実行される場所を示します。コントローラおよびコントローラに関連付けられたビューは Zen コンポーネントであり、Zen ページに配置されます。コントローラ・コンポーネントはビューでは表示されませんが、ビュー・コンポーネントはユーザに表示されます。ビュー・コンポーネントは、コントローラに要求して取得したデータ値を表示します。コントローラはクライアントに存在しますが、サーバ上でコードを実行できます。モデルはすべてサーバに存在します。データ値をサーバ上のソースから取得し、コントローラからのデータ要求に応答できます。

モデル・ビュー・コントローラのアーキテクチャ
generated description: mvc

この章の次の 3 つのセクションでは、前の図の各部分について詳しく説明します。

この章の残りのセクションでは、MVC 機能を使用してフォームを作成する方法について、一連の演習を紹介します。

モデル

データ・モデルは、%ZEN.DataModel.DataModelOpens in a new tab の任意のサブクラスです。データ・モデルは以下のことが可能です。

  1. 以下のようなソースからデータ値を取得できます。

    • 永続 Caché オブジェクト

    • Caché SQL ゲートウェイ経由の外部データベース (ODBC または JDBC)

    • Caché グローバル

    • Ensemble ビジネス・サービス

  2. これらのデータ値を専用のプロパティに配置します。

  3. これらのプロパティをデータ・コントローラで利用できるようにします。

データ・モデル・クラスには 2 つのバリエーションがあります。通常は、新しくデータ・モデルを作成する際に、これらのいずれかをその親クラスとして選択します。2 つのバリエーションは密接に関連していますが、その使用目的は異なります。使用可能なデータ・モデルのサブクラスは、以下の図に示す %ZEN.DataModel.ObjectDataModelOpens in a new tab%ZEN.DataModel.AdaptorOpens in a new tab です。

データ・モデル・クラス
generated description: mvc classes model

%ZEN.DataModel.ObjectDataModel

%ZEN.DataModel.ObjectDataModelOpens in a new tab のサブクラスは、オブジェクト・データ・モデルと呼ばれます。データ・コントローラ・コンポーネントが使用できる 1 つ以上のプロパティを定義します。これらのプロパティはそれぞれ、データ・ソース・クラスの値と相互に関連しています。データ・ソースのすべての値をモデルで使用できるようにする必要はありません。この規約により、目的の値のみをデータ・モデルで使用可能とすることができます。

generated description: mvc object

このような例としては、秘密情報を記録した患者のカルテで、その利用を許可するアプリケーションを限定することが必要なケースがあります。このオプションを使用する場合、ソースからデータ・モデル・プロパティへの値のロード、ソースから返された値の格納、および値の検証を実行するメソッドを、オブジェクト・データ・モデル・クラスの開発段階で実装する必要があります。これは、Zen がこれらの詳細を扱うアダプタ・データ・モデルとは対照的です。ただし、これはそれほど難しい手順ではありません。

%ZEN.DataModel.ObjectDataModelOpens in a new tab の使用方法の詳細は、以下を参照してください。

%ZEN.DataModel.Adaptor

永続オブジェクトをデータ・モデルとして使用すると便利な場合が多くあります。この処理を容易にするために、Zen には %ZEN.DataModel.AdaptorOpens in a new tab インタフェースが用意されています。このクラスを追加スーパークラスとして永続クラスに追加することにより、永続クラスをデータ・モデルとして使用できます。このデータ・モデルを使用することで、その任意のプロパティおよびすべてのプロパティをデータ・コントローラで使用できます。このオプションは、既存のクラスのすべてのプロパティを使用可能にする場合に便利です。

generated description: mvc adaptor

この例としては、在庫や部品の管理アプリケーションを使用していて、そこでは多数のプロパティを持つクラスによって各製品が記述されている場合が考えられます。これらすべてのプロパティを、別のクラスを書き込まずにフォーム上に自動的に配置する場合は、その製品クラスで %ZEN.DataModel.AdaptorOpens in a new tab を拡張するだけですみます。それにより、後述の章で説明するように、Zen によってフォームが生成されます。この方法の短所は、データとフォームが非常に密接にリンクされることです。柔軟性を求める場合は、オブジェクト・データ・モデル・クラスのサブクラスを作成し、その内部インタフェースを実装する必要があります。これは、便利さと柔軟性の間で従来から行われているトレードオフです。

%ZEN.DataModel.AdaptorOpens in a new tab の使用方法の詳細は、以下を参照してください。

コントローラ

データ・コントローラは、データ・モデルとデータ・ビューの間の通信を管理します。データ・コントローラは、%ZEN.Auxiliary.dataControllerOpens in a new tab の任意のサブクラスです。クラス継承はありますが、以下の図に示すように、各データ・コントローラは Zen コンポーネントでもあります。この規約により、Zen ページにデータ・コントローラを配置できます。

データ・コントローラ・クラスとデータ・ビュー・クラス
generated description: mvc classes components

<dataController>

データ・コントローラを Zen ページに提供するには、<dataController> または %ZEN.Auxiliary.dataControllerOpens in a new tab のサブクラスを <page> の中に追加します。<dataController> コンポーネントはその他のコンポーネントと共に XData Contents に記述されますが、画面には表示されません。その役割は、データ・モデルと 1 つ以上のデータ・ビューとの間の仲介です。

次の例では、1 という id 値を使用して MyApp.MyModel クラスのインスタンスを開く <dataController> を定義しています。<dynaForm> の controllerId プロパティを <dataController> の id に設定することで、<dynaForm> は <dataController> と結合されます。これにより、<dynaForm> は Zen コントロールを modelClass 内の各プロパティに提供するフォームを表示できます。

<dataController id="data" modelClass="MyApp.MyModel" modelId="1"/>
<dynaForm id="myForm" controllerId="data"/>

<dataController> 属性

<dataController> には、Zen の <page> に配置することで、以下の属性を割り当てることができます。

属性 説明
Zen コンポーネントの属性

<dataController> は、あらゆる Zen コンポーネントと同じ汎用属性を持ちます。詳細は、以下のセクションを参照してください。

id 属性は、<dataController> では必須です。namecondition も適用される場合があります。<dataController> は表示されないので、外観のスタイル属性は適用されません。

alertOnError

True の場合、保存や削除などのサーバ側機能を呼び出すときにエラーが発生すると、<dataController> は警告ボックスを表示します。既定値は True です。

alertOnError で基本となるデータ型は %ZEN.Datatype.booleanOpens in a new tab です。“Zen 属性のデータ型” を参照してください。

autoRefresh autoRefresh を 0 以外の値に設定すると、このデータ・コントローラの自動更新モードがオンになります。このモードでは、データ・コントローラは autoRefresh で指定された間隔 (ミリ秒) で定期的にサーバからデータを再ロードします。autoRefresh は、メータやグラフの操作に使用するデータ・コントローラでの便宜を考慮して用意されているので、フォームでの使用は制限されています。autoRefresh を 0 に設定すると、自動更新モードは無効になります。
defaultSeries オプション。データ・モデルに複数のデータ系列がある場合、defaultSeries は1 から始まる番号で、1 つのデータ系列 (フォームなど) の値のみ表示できるデータ・ビューに対し、その値を提供するために使用する系列を指定します。既定値は 1 です。
modelClass

この <dataController> にデータを指定する、データ・モデル・クラスのパッケージおよびクラス名。modelClass の値には、リテラル文字列のほか、Zen #()# 実行時式を指定できます。

modelId

データ・モデル・オブジェクトの特定のインスタンスを表す文字列。modelId 文字列のフォームと指定可能な値は、そのデータ・モデル・クラスの開発者が決定します。modelId の値には、リテラル文字列のほか、Zen #()# 実行時式を指定できます。

oncreate

<dataController> の oncreate イベント・ハンドラ。createNewObject メソッドが呼び出されるたびに、Zen によってこのハンドラが呼び出されます。“Zen コンポーネントのイベント・ハンドラ” を参照してください。

ondelete deleteId メソッドが呼び出されるたびに実行される、クライアント側 JavaScript 式。削除が正常に実行されたかどうかにかかわらず、ondelete コールバックが呼び出されます。ondelete コールバックでは次の 2 つの特殊変数を使用できます。id には削除するオブジェクトの modelId が含まれ、deleted は削除が成功したかどうかを表します。
onerror

データ・コントローラがデータ・モデル・オブジェクトのインスタンスを開くときにエラーが発生すると実行される、クライアント側 JavaScript 式。

プログラムで %ZEN.Auxiliary.dataControllerOpens in a new tab を扱う場合、dataController オブジェクトの modelError プロパティを調べることで、このデータ・コントローラに関連付けられたデータ・モデル・オブジェクトによって報告された最近のエラー・メッセージを取得できます。このプロパティは、<dataController> を Zen ページに追加すると、XML 属性として使用できません。このプロパティにクライアント側からアクセスするには、データ・コントローラのクライアント側 JavaScript メソッド getError を使用します。

onnotifyController このデータ・コントローラに接続されたデータ・ビューがイベントをトリガするたびに実行される、クライアント側 JavaScript 式。
onsave save メソッドが呼び出されるたびに実行される、クライアント側 JavaScript 式。パラメータ id はイベント・ハンドラに渡され、現在の modelId を含みます。
readOnly

True の場合、対応するデータ・モデルが読み取り専用であるかどうかにかかわらず、このデータ・コントローラは読み取り専用です。既定値は False です。

readOnly で基本となるデータ型は %ZEN.Datatype.booleanOpens in a new tab です。“Zen 属性のデータ型” を参照してください。

<dataController> メソッド

<dataController> または %ZEN.Auxiliary.dataControllerOpens in a new tab のサブクラスは、データ・コントローラおよびそれが表すデータ・モデルと連携するために、以下のクライアント側 JavaScript メソッドを提供します。オンラインの "%ZEN.Auxiliary.dataControllerOpens in a new tab クラス" のクラス・リファレンス・ドキュメントには、さらに多くのメソッドの説明があります。

クライアント側メソッド 目的
getDataAsArrays() このコントローラのデータを、配列の配列として返します。これは、グラフを使用する場合に便利です。
getDataAsObject(series)

このコントローラのデータを、現在のデータ・モデル・オブジェクトのプロパティに対応する名前および値のプロパティを持つ zenProxy オブジェクトのインスタンスとして返します。

データ・モデルで複数のデータ系列をサポートする場合、series (先頭は 0) は使用する系列を指定します (既定は 0)。

zenProxy の詳細は、"Zen アプリケーションの開発" の “Zen ページ” の章にある "Zen プロキシ・オブジェクト" を参照してください。

getDataByName(prop) このデータ・コントローラ・オブジェクトの指定された prop (プロパティ) のデータを返します。このデータは、生成されたフォームの対応するコントロールの value と同じになります。
getDimensions()

dataModel のディメンジョン数を返します。ディメンジョンは 2 つあります。1 つ目はプロパティのセットで、2 つ目は通常 1 のサイズとなります。

指定されたモデル・インスタンスに対してモデルが複数の系列を提供する場合、2 番目のディメンジョンは 1 より大きくなることがあります(グラフに複数のデータ系列を提供する場合など)。

getDimSize(dim) 指定されたディメンジョン dim 内の項目数を返します。dim は、1、2、または 3 です。
getLabel(n,dim) 指定されたディメンジョン dim 内の位置 n にあるラベルを取得します。n は 0 から始まる番号です。dim は、1、2、または 3 です。
getModelClass() 現在の modelClass 値を返します。
getModelId() 現在の modelId 値を返します。
raiseDataChange() リスナに、このデータ・コントローラに関連付けられているデータが変更されたことを通知します。
setDataByName(p,v,s)

このデータ・コントローラ・オブジェクトの指定されたプロパティ p を値 v に変更します。これにより、生成されたフォーム上の対応するコントロールの value も変更されます。

p が "%id" の場合、このコントローラの id は変更されます。p が "%series" の場合、このコントローラの defaultSeries は変更されます。データ・モデルで複数のデータ系列をサポートする場合、s (先頭は 0) は使用する系列を指定します (既定は 0)。

setDataByName の (1 回または複数の) 呼び出しの後、raiseDataChange を呼び出して、このデータ・コントローラに関連付けられているデータが変更されたことをリスナに通知する必要があります。

setModelId(id) 実行時に modelId 値を変更します。modelId を変更すると、コントローラは新しいレコードをロードし、関連するビューを更新します。
setModelClass(name) 実行時に modelClass 値を変更します。オプションで、setModelClass(name,id) を呼び出して、modelClassmodelId の両方を変更することもできます。modelClass の値を変更すると、コントローラは前のモデルを破棄し、新しいモデルからデータをロードします。

ビュー

データ・ビューは、%ZEN.Component.dataViewOpens in a new tab インタフェースを実装する任意の Zen コンポーネントです。データ・ビュー・コンポーネントのリストには、以下があります。

例として、“データ・コントローラ” の図 “データ・コントローラ・クラスとデータ・ビュー・クラス” を参照してください。

データ・ビュー・コンポーネントは、関連付けられたデータ・コントローラに実行時に接続し、そのデータ・コントローラを使用して、関連するデータ・モデルから値を取得し、そのデータ・モデルに値を設定します。データ・ビューはそのデータ・コントローラを参照します。複数のデータ・ビューから同じデータ・コントローラを参照できます。

データ・ビューの属性

データ・ビュー・コンポーネントは通常の Zen コンポーネント属性をサポートし、さらに特定のタイプのコンポーネントに見られる専用の属性もサポートします。また、すべてのデータ・ビュー・コンポーネントは、以下の属性をサポートします。これらの属性は、具体的にはコンポーネントのロールにデータ・ビューとして関連しています。

Important:

ユーザが Zen ページを操作することでコンポーネントの値を設定した場合は、コントローラとモデルに通知されます。プログラム・コードによってこの値が設定された場合は、コントローラには通知されず、この値は送信時に失われます。プログラム・コードはコントローラに直接書き込む必要があり、その後でコントローラはコントロールを更新します。

属性 説明
controllerId このデータ・ビュー () のデータ・コントローラを表します。controllerId の値は、その <dataController> コンポーネントに指定した id 値と一致している必要があります。
onnotifyView

データ・ビュー・コンポーネントの onnotifyView イベント・ハンドラ。 このデータ・ビューに関連付けられたデータ・コントローラがイベントを発生するたびに、Zen によってこのハンドラが呼び出されます。“Zen コンポーネントのイベント・ハンドラ” を参照してください。

メータとコントロールは以下のプロパティをサポートします。

属性 説明
dataBinding

このコンポーネントに結合されているデータ・モデル・プロパティを表します。このプロパティは、以下のように、そのコンポーネントが表示する値を指定します。

  • dataBinding 値が単純なプロパティ名である場合、このプロパティは、<dataController> の modelClass 属性が表すデータ・モデル・クラス内のプロパティであると見なされます。

  • 別の方法として、dataBinding で完全なパッケージ、クラス、およびプロパティ名を指定することもできます。

dataBinding は、一般的に単一の値を表示するコンポーネント (メータコントロール) に適しています。Zen ページの各メータでは、controllerIddataBinding を指定する必要があります。コントロールはデータ・ビュー・インタフェースをサポートしないので controllerId を指定できませんが、このコントロールを持つフォームにデータ・コントローラが関連付けられていれば、そのフォームにある各コントロールでは、表示するプロパティを特定する dataBinding 属性を指定できます。

コントローラ・オブジェクト

プログラムでデータ・ビュー・コンポーネントを扱う場合、%ZEN.Component.dataViewOpens in a new tab インタフェースが持つ以下のプロパティを使用して、関連する %ZEN.Auxiliary.dataControllerOpens in a new tab オブジェクトへの参照を取得できます。

  • controller — クライアント側で実行する JavaScript コードで使用します。

  • %controller — サーバ側で実行する ObjectScript、Caché Basic、または Caché MVBasic のコードで使用します。

複数のデータ・ビュー

データ・コントローラに結合されたコントロールの値をユーザが変更すると、そのデータ・コントローラに通知されます。複数のデータ・ビューで 1 つのコントローラを共有することは普通です。例えば、以下の図に示すように、同じページにあるさまざまなタイプのグラフで同じデータ・コントローラを共有することで、同じデータを異なる方法で表示できます。複数のデータ・ビュー・コンポーネントが同じデータ・コントローラに接続されている場合、結合されたそのコントロールに変更が発生すると、すべてのコンポーネントに通知されます。

共有データ・コントローラの例として、スタジオを使用して ZENMVC.MVCChartOpens in a new tab クラスと ZENMVC.MVCMetersOpens in a new tab クラスを表示します。ZENMVC.MVCMetersOpens in a new tab クラスは同じデータ・コントローラを使用して、<dynaGrid> およびいくつかのメータに値を提供します。ZENMVC.MVCChartOpens in a new tab クラスは <dynaGrid> と、同じデータ・コントローラを使用する 3 つのグラフを指定します。ブラウザに以下の URI を入力します。

http://localhost:57772/csp/samples/ZENMVC.MVCChart.clsOpens in a new tab

http://localhost:57772/csp/samples/ZENMVC.MVCMeters.clsOpens in a new tab

ここで、57772 は、Caché に割り当てた Web サーバのポート番号です。

これらのページのいずれかの値を <dynaGrid> で編集し、Enter キーを押して変更すると、同じページのすべてのグラフ (またはメータ) の対応する値が変化します。これは、すべてが同じデータ・コントローラを共有しているためです。以下の図では、ユーザが ZENMVC.MVCChartOpens in a new tab の <dynaGrid> の Trucks フィールドを変更しています。

generated description: mvc charts

モデルの作成

このトピックでは、モデル・ビュー・コントローラを使用してフォームを作成する方法について、最初の段階の演習を紹介します。Caché を新規にインストールした場合は、この演習を開始する前に、まず ZENDemo ホーム・ページを実行する必要があります。このページをロードすると、SAMPLES ネームスペースに対するデータ・レコードが暗黙的に生成されます。これは、Caché を 1 回インストールするごとに一度実行するだけですみます。

ブラウザに以下の URI を入力します。

http://localhost:57772/csp/samples/ZENDemo.Home.clsOpens in a new tab

ここで、57772 は、Caché に割り当てた Web サーバのポート番号です。

手順 1 : モデルのタイプ

モデル・ビュー・コントローラを使用してフォームを生成する際は、以下の 2 つの基本的な選択を行います。

  • %ZEN.DataModel.ObjectDataModelOpens in a new tab または %ZEN.DataModel.AdaptorOpens in a new tab

    オブジェクト・データ・モデルでは、多くのコード作成作業が発生します。オブジェクト・データ・モデルでは、どのプロパティをモデル内部で完結させるかを明示的に制御できるので、一定のプロパティを表示しないように保護する場合や、非常に細かいデータ制御を必要とするグラフを表示する場合などに効果的です。アダプタ・データ・モデルを選択すると簡単で便利ですが、元の永続オブジェクトのクラス・コードを変更してアダプタ・データ・モデルにできるようにするという点で、元の永続オブジェクトに対する作業が発生します。

  • <form> または <dynaForm>

    <form> は、アプリケーションが正常に動作するうえで重要なフォームに適した選択肢です。<form> はエンコードの作業が多くなりますが、最適な結果を得るうえで必要なレベルの制御が実現します。<dynaForm> では、基本的なデータ・モデルを変更すると、自動的に結果が生成され、自動的にレイアウトが更新されます。これは、システム管理、在庫管理、メンテナンス要求など、大量の詳細情報を扱うフォームを生成する場合に便利です。

この演習では、オブジェクト・データ・モデルと <form> を選択します。

手順 2 : オブジェクト・データ・モデル

Patient という永続オブジェクトに、患者のカルテが記録されているとします。このオブジェクトは、数百点のプロパティを持つ可能性があります。基本的な統計情報のみを表示する単純なページを作成するとします。この場合は患者の名前と居住する都市を表示します。この場合は、明らかに %ZEN.DataModel.ObjectDataModelOpens in a new tab の使用が適しています。

まず、Patient オブジェクトからプロパティをロードし、格納する方法を指定する、PatientModel というオブジェクト・データ・モデル・クラスを定義します。

  1. スタジオを開始します。

  2. [ファイル]→[ネームスペース変更] を選択するか、F4 キーを押します。

  3. SAMPLES ネームスペースを選択します。

  4. [ファイル]→[新規作成] を選択するか、Ctrl-N を押すか、generated description: studio new アイコンをクリックします。

  5. [一般] タブをクリックします。

  6. [Cache クラス定義] アイコンをクリックします。

  7. [OK] をクリックします。

  8. [パッケージ名] に次の内容を入力します。

    MyApp

  9. [クラス名] フィールドで次のように入力します。

    PatientModel

  10. [次へ] をクリックします。

  11. [Extends] を選択します。

  12. [次へ] をクリックして、次のクラス名を入力 (または参照) します。

    %ZEN.DataModel.ObjectDataModel

  13. [完了] をクリックします。

    スタジオでは、骨格のみのオブジェクト・データ・モデル・クラスが作成され、表示されます。

    Class MyApp.PatientModel Extends %ZEN.DataModel.ObjectDataModel
    {
    
    }
    
  14. 以下のコード例に示すように、プロパティとメソッドを追加して、クラスを完成させます。この例では、データ・モデルの 2 つのプロパティ (Name と City) を定義します。また、%ZEN.DataModel.ObjectDataModelOpens in a new tab インタフェースのいくつかのサーバ側メソッドをオーバーライドします。これらのメソッドのドキュメントは、“オブジェクト・データ・モデルのコールバック・メソッド” を参照してください。

    Class MyApp.PatientModel Extends %ZEN.DataModel.ObjectDataModel
    {
      Property Name As %String;
      Property City As %String;
    
      /// Load an instance of a new (unsaved) source object for this DataModel.
      Method %OnNewSource(Output pSC As %Status = {$$$OK}) As %RegisteredObject
      {
        Quit ##class(ZENDemo.Data.Patient).%New()
      }
    
      /// Save instance of associated source object.
      Method %OnSaveSource(pSource As ZENDemo.Data.Patient) As %Status
      {
        Set tSC=pSource.%Save()
        If $$$ISOK(tSC) Set ..%id=pSource.%Id()
        Quit tSC
      }
    
      /// Load an instance of the source object for this DataModel.
      Method %OnOpenSource(pID As %String, pConcurrency As %Integer = -1,
                           Output pSC As %Status = {$$$OK}) As %RegisteredObject
      {
        Quit ##class(ZENDemo.Data.Patient).%OpenId(pID,pConcurrency,.pSC)
      }
    
      /// Delete instance of associated source object.
      ClassMethod %OnDeleteSource(pID As %String) As %Status
      {
       Quit ##class(ZENDemo.Data.Patient).%DeleteId(pID)
      }
    
    
      /// Do the actual work of loading values from the source object.
      Method %OnLoadModel(pSource As ZENDemo.Data.Patient) As %Status
      {
        Set ..Name = pSource.Name
        Set ..City = pSource.Home.City
        Quit $$$OK
      }
    
      /// Do the actual work of storing values into the source object.
      Method %OnStoreModel(pSource As ZENDemo.Data.Patient) As %Status
      {
        Set pSource.Name = ..Name
        Set pSource.Home.City = ..City
        Quit $$$OK
      }
    }
  15. [ビルド]→[コンパイル] を選択するか、Ctrl-F7 を押すか、generated description: studio compile アイコンをクリックします。

オブジェクト・データ・モデルへの <form> の結合

この演習では、前の演習 “モデルの作成” のオブジェクト・データ・モデルに基づいてデータ・コントローラを作成します。その後、このデータ・コントローラにフォームを結合します。

手順 1 : データ・コントローラ

単純な Zen アプリケーション、および前の演習で得られるページ・クラスを作成していない場合は、"Zen の使用法" の “Zen チュートリアル” の章の以下のセクションにある説明に従って作成してください。

ここで、以下のように <page> コンテナ内で、MyApp.MyNewPage クラスの XData Contents ブロックに <dataController> 要素を追加することにより、Zen ページにデータ・コントローラ・コンポーネントを配置します。

<dataController id="patientData"
                modelClass="MyApp.PatientModel"
                modelId="1" />

以下はその説明です。

  • id は、Zen ページのデータ・コントローラの一意の識別子です。データ・ビュー (フォームなど) は、この id を使用してデータ・コントローラを指定します。

  • modelClass は、データ・モデル・クラスのパッケージおよびクラス名です。前の演習 “モデルの作成” では、ZENDemo.Data.PatientOpens in a new tab クラスのオブジェクトを表すオブジェクト・データ・モデルである MyApp.PatientModel クラスを作成しました。

  • modelId は、データ・ソースから最初にロードするレコードを識別する番号です。この場合は、特定の ZENDemo.Data.PatientOpens in a new tab オブジェクトの識別子となります。

Note:

<dataController> コンポーネントは、ページには表示されません。

手順 2 : データ・ビュー

ここでは、以下のように、フォームを作成してそのフォームをデータ・コントローラに接続します。

  1. <form> コンポーネントを <page> コンテナ内に配置します。

    <form> の controllerId を <dataController> の id と同じ値に設定することで、“手順 1 : データ・コントローラ” で作成したデータ・コントローラにフォームを結合します。以下はその例です。

    <form controllerId="patientData" id="MyForm" >
    </form>

    id 属性は、結合には影響しませんが、今後の手順でフォームを保存する際に役立ちます。

  2. <form> 内に <text> コントロールを 2 つ追加します。

    <dataController> の modelClass 内のプロパティを特定する dataBinding 属性を指定することにより、データ・モデルのプロパティに各コントロールを結合します。また、各コントロールの label も指定します。以下はその例です。

    <form controllerId="patientData" id="MyForm" >
      <text label="Patient Name" dataBinding="Name" />
      <text label="Patient City" dataBinding="City" />
    </form>

    label 属性は、モデル・ビュー・コントローラに関して何かの役に立つわけではありませんが、Zen ページでコントロールにわかりやすいラベルを付ける場合に必要となります。

手順 3 : 最初の結果

以下のように、最初の結果を表示します。

  1. スタジオで Zen ページ・クラスを開きます。

  2. [ビルド]→[コンパイル] を選択するか、Ctrl-F7 を押すか、generated description: studio compile アイコンをクリックします。

  3. [表示]→[ウェブページ] または generated description: studio webpage アイコンを選択します。

    データ・コントローラ・コンポーネントによってサーバに MyApp.PatientModel オブジェクトが作成され、このオブジェクト独自のプロパティに、データ・ソース・レコード番号 1 (<dataController> の modelId 属性で識別されます) のデータがロードされます。データ・コントローラでは、これらのデータ値をフォームにある適切なコントロールに配置します。各コントロールの dataBinding 属性は、そのコントロールの値を指定するプロパティが Name または City のどちらであるかを示します。

    以下の図は、レコード 1 の現在の値を含むフォームを示しています。

    generated description: mvc form text

手順 4 : フォームの保存

ユーザがこのフォームの値を編集し、それを保存できるようにするとします。データ・コントローラに関連付けられたフォームの値を保存するには、アプリケーションからフォームの save メソッドを呼び出す必要があります。通常、これを有効にするには以下のようにします。

  1. 以下のように、クライアント側メソッドをページ・クラスに追加します。

    ClientMethod save() [ Language = javascript ]
    {
      var form = zen('MyForm');
      form.save();
    }
    

    ここで、<form> の id を定義する重要性がわかります。JavaScript 関数 zen は、フォームへのポインタを取得して save メソッドを呼び出し可能にするために、この id を必要とします。

  2. このメソッドを呼び出してフォームを保存する <button> をページに追加します。

    <page> 全体の定義は、以下のようになります。

    <page xmlns="http://www.intersystems.com/zen" title="">
      <dataController id="patientData"
                      modelClass="MyApp.PatientModel"
                      modelId="1" />
      <form controllerId="patientData" id="MyForm">
        <text label="Patient Name" dataBinding="Name" />
        <text label="Patient City" dataBinding="City" />
      </form>
      <button caption="Save" onclick="zenPage.save();"/>
    </page>
  3. データを編集し、[保存] をクリックしてみます。

ユーザが [保存] ボタンをクリックするたびに、フォームの save メソッドがサーバにコールバックし、データ・モデル・クラスに適したメソッドを呼び出してデータを保存します。このプロセスでは、フォームからデータ・コントローラの支援を受けてさまざまなプロパティのデータを検証します。この検証ロジックのソースは、データ・モデル・クラスです。

データ・コントローラにも、使用可能な save メソッドがあります。フォームの保存とコントローラの保存は異なります。フォームの save メソッドを呼び出すと、フォーム検証ロジックがトリガされます。それを受けて、フォームはデータ・モデル・クラスの適切なモデルを呼び出し、コントローラにデータの保存を指示します。コントローラの保存では、フォームの検証が省略されます。

この例の手順 3 の基本的なフォーム検証は、次のように試すことができます。Patient Name フィールドを完全に空白にして [保存] をクリックすると、検証エラーが発生します。これは、データ・ソースの役割をする ZENDemo.Data.PatientOpens in a new tab クラスでは、Patient プロパティが Required (必須) とマークされているためです。ただし、Patient Name または Patient City を空白以外の値に変更すると、フォームは正常に保存されます。フォームを拡張して複数のデータ・レコードを簡単に表示できるようにしてから、これを試してみるとわかりやすくなります。レコードを自由に切り替えて、実際にその変更内容が反映されているかどうかを確認できます。

Note:

検証の詳細例は、SAMPLES ネームスペースの Zen ページ・クラス ZENMVC.MVCFormOpens in a new tab を使用および参照してください。ブラウザに以下の URI を入力します。

http://localhost:57772/csp/samples/ZENMVC.MVCForm.clsOpens in a new tab

ここで、57772 は、Caché に割り当てた Web サーバのポート番号です。ページに表示されたいずれかのフォームの値を編集し、[送信] ボタンをクリックします。スタジオを使用して、クラス・コードを表示できます。

手順 5 : クライアント側検証の実行

プロパティ固有の IsValidJS メソッドを定義することで、クライアント側検証をデータ・モデル・クラスに追加できます。これで、サーバ側検証を待機する必要がなくなります。これは、property の名前付け規約 IsValidJS を使用し、指定された property の値が無効である場合はエラー・メッセージを返し、値が有効である場合は '' (空白の文字列) を返す JavaScript ClientClassMethod です。このメソッドをデータ・モデル・クラス内で定義できるほか、IsValidJS メソッドを定義するデータ型クラスを定義することもできます。

以下のメソッドを MyApp.PatientModel クラスに追加すると、データ・コントローラは、その save メソッドが呼び出されるたびに、この検証をクライアントの City プロパティに自動的に適用します。

ClientClassMethod CityIsValidJS(value) [Language = javascript]
{
  return ('Boston' == value) ? 'Invalid City Name' : '';
}

手順 6 : プログラムを使用した値の設定

データ値をプログラムを使用して設定するには、コントローラで値を設定してから、すべてのビューに変更を通知するようにコントローラに指示します。この値を設定するには、コントローラ・メソッド setDataByName を使用してから、raiseDataChange を使用してビューに通知することができます。raiseDataChange を明示的に呼び出す必要があります。これにより、コントローラで複数の値を変更できるようになるとともに、イベントを 1 回発生させれば済むようになります。

ClientMethod ChangeValue() [ Language = javascript ] {
  var controller = zenPage.getComponentById('patientData');
  controller.setDataByName('Name','Public,John Q');
  controller.raiseDataChange();
}

<form> への動作の追加

%ZEN.Auxiliary.dataControllerOpens in a new tab クラスには、便利なメソッドがいくつかあります。例えば、前の演習 “オブジェクト・データ・モデルへの <form> の結合” のページを拡張して、新しいレコードのオープン、新しいレコードの作成、既存のレコードの削除、または現在のレコードの特定のモデル ID へのリセットを実行できるようにするとします。ここでは、getModelIdsetModelId など、dataController のメソッドを使用してこれらのタスクを実行する方法について説明します。

手順 1 : 新しいレコードのオープン

これまでの演習で作成してきたページをブラウザに表示すると、ブラウザには必ず同じデータ・レコードが表示されます。これは、<dataController> 要素の modelId プロパティが 1 という値に設定されているためです。このプロパティを、2、3、4 など 1000 までの他の値に変更してみると、それぞれどのように動作するかがわかります。

ただし、これらの値が ZENDemo.Data.PatientOpens in a new tab クラスの既存のインスタンスの実際の ID 値を表していることに注意してください。これらのインスタンスが存在するのは、"Zen の使用法" の “Zen の概要” の章の説明にあるように、初めて “Zen デモ” を実行するとき、ZENDemo.Data のすべてのクラスが自動的に生成されるためです。

表示するレコードをユーザ側で選択できるようにするとします。以下のように、選択肢がいくつかあります。

  • ユーザが ID を入力できるテキスト・フィールドの追加

  • 患者名など、いずれかのプロパティでユーザがレコードを選択できる、コンボ・ボックスの追加

  • ユーザが患者名をクリックすると、その下にフォームの詳細を表示できるテーブルの追加

ユーザ入力用に選択するオプションはどれでもかまいません。重要なタスクは、ページからデータ・コントローラに対し、目的のレコードのロードを指示できるようにすることです。これは、以下の操作で実行できます。

  1. スタジオで Zen ページ・クラスを開きます。

  2. <form> に新しいテキスト・フィールドを追加します。

    <form controllerId="patientData" id="MyForm">
      <text label="ID:"
            onblur="zenPage.loadRecord(zenThis.getValue())"
            dataBinding="%id"/>
      <text label="Patient Name" dataBinding="Name" />
      <text label="Patient City" dataBinding="City" />
    </form>
    
  3. 対応するクライアント側メソッドを追加します。

    
    ClientMethod loadRecord(id) [ Language = javascript ]
    {
      var controller = zen('patientData');
      controller.setModelId(id);
    }
    

    onblur イベントは、クライアント側メソッド loadRecord を呼び出します。このメソッドはまず、id 値である "patientData" を使用してデータ・コントローラへのポインタを取得します。次に、ユーザが <text> フィールドに入力した内容を modelId として使用し、目的のレコードをデータ・モデルからロードします。実際にレコードをロードするには、loadRecord はデータ・コントローラのメソッド setModelId を使用します。

    この例では、ID フィールドをデータ・モデルの %id プロパティに結合して、このフィールドに現在のレコードの ID が必ず表示されるようにしています。これは、setModelId の動作には必ずしも必要ありませんが、この演習の “手順 2 : レコードの作成と削除” で役に立ちます。

  4. [ビルド]→[コンパイル] を選択するか、Ctrl-F7 を押すか、generated description: studio compile アイコンをクリックします。

  5. [表示]→[ウェブページ] または generated description: studio webpage アイコンを選択します。

    以下の図は、フォームを示しています。

    generated description: mvc form id save

  6. 以下のように、onblur 機能を試します。

    • ID フィールドに 2 (または他の任意の数) を入力します。

    • Tab キーを押して、ID フィールドの外に移動します。

    • 別のレコードが表示されます。

  7. 演習 “オブジェクト・データ・モデルへの <form> の結合” の “手順 4 : フォームの保存” では、[保存] 機能を追加しましたが、それを簡単にテストすることはできませんでした。ここで、以下のように試します。

    • 現在のレコードの番号を書き留めます。

    • Name フィールドまたは City フィールドの値を大幅に変更します。

    • [保存] をクリックします。

    • ID フィールドに別の番号を入力します。

    • Tab キーを押して、ID フィールドの外に移動します。

    • 別のレコードが表示されます。

    • ID フィールドに保存したレコードの番号を入力します。

    • Tab キーを押して、ID フィールドの外に移動します。

    • 保存した変更内容が、フォームに表示されます。

手順 2 : レコードの作成と削除

レコードの作成と削除も、単純なタスクです。以下のように、ページを変更して必要なボタンとクライアント側コードを追加します。

  1. スタジオで Zen ページ・クラスを開きます。

  2. <form> の後、終了タグ </page> の前にある [保存] ボタンを以下の <hgroup> に置き換えます。

    <hgroup>
      <button caption="Save" onclick="zenPage.save()"/>
      <button caption="New" onclick="zenPage.newRecord()"/>
      <button caption="Update" onclick="zenPage.updateRecord()"/>
      <button caption="Delete" onclick="zenPage.deleteRecord()"/>
    </hgroup>

    これらの文では、ボタンを <hgroup> に追加して、1 行に表示されるようにしています。各ボタンでは、onclick メソッドを別のクライアント側メソッドとして定義します。

  3. 以下の対応する新しいクライアント側メソッドを、ページ・クラスに追加します。

    
    ClientMethod newRecord() [ Language = javascript ]
    {
      var controller = zen('patientData');
      controller.createNewObject();
    }
    

    および、

    
    ClientMethod updateRecord() [ Language = javascript ]
    {
      var controller = zen('patientData');
      controller.update();
    }

    および、

    
    ClientMethod deleteRecord() [ Language = javascript ]
    {
      var controller = zen('patientData');
      controller.deleteId(controller.getModelId());
      controller.createNewObject();
    }

    これらのメソッドはそれぞれ、別の dataController メソッドを使用して目的を達成します。

  4. [ビルド]→[コンパイル] を選択するか、Ctrl-F7 を押すか、generated description: studio compile アイコンをクリックします。

  5. [表示]→[ウェブページ] または generated description: studio webpage アイコンを選択します。

    以下の図は、フォームを示しています。

    generated description: mvc form full

新規作成

ユーザが [新規作成] をクリックするとします。createNewObject を呼び出すと、新しい空白のモデルが直ちに作成されます。ブラウザでは、ユーザが [新規作成] をクリックするたびに、フォームが空になります。その後、ユーザが空白のフォームへの入力を終えて [保存] をクリックすると、フォームの save メソッドが呼び出されます。これは (最終的に)、サーバ上でデータ・モデルの %OnSaveSource メソッドを呼び出すことになります。

演習 “オブジェクト・データ・モデルへの <form> の結合” の “手順 4 : フォームの保存” では、%OnSaveSource メソッドでモデルの %id プロパティを保存済みオブジェクトの ID に設定することにより、[保存] 機能を追加しました。

Method %OnSaveSource(pSource As ZENDemo.Data.Patient) As %Status
  {
    Set tSC=pSource.%Save()
    If $$$ISOK(tSC) Set ..%id=pSource.%Id()
    Quit tSC
  }

その結果、ユーザが [新規作成] をクリックし、値を入力して、[保存] をクリックするたびに、フォームには、ユーザに新しく割り当てられた ID が表示されます (そのフィールドに dataBinding があるためです)。当然、これはフォームに入力されたデータが検証に合格する場合にのみ有効です。Name は必須のフィールドなので、Name への入力がない場合、レコードは保存されません。

削除

ユーザが [削除] をクリックするとします。データ・コントローラの deleteId メソッドには、削除するレコードの ID を含む入力引数が必要になります。したがって、ユーザが [削除] をクリックすると、ページではデータ・コントローラの getModelId メソッドを使用して、ユーザが現在表示しているレコードの ID を特定します。この ID は deleteId に渡されます。これは (最終的に)、サーバ上でデータ・モデルの %OnDeleteSource メソッドを呼び出すことになります。ソース・オブジェクトが削除され、ソース・オブジェクトがなくなるので、ページはデータ・コントローラの createNewObject メソッドを呼び出してフォームを空白にし、新規入力に備えます。

この章のコード例ではこの機能を利用していませんが、deleteId メソッドはブーリアン値 (True または False) を返します。レコードが正常に削除された場合、この値は True です。失敗した場合、またはデータ・コントローラあるいはそのデータ・モデルが読み取り専用である場合は、False です。データ・コントローラは、その readOnly 属性が 1 (True) に設定されている場合は読み取り専用です。データ・モデルは、そのクラス・パラメータが 1 (True) に設定されている場合は読み取り専用です。

Important:

この演習中に、特定の ID のレコードを削除すると、このオブジェクトは存在しなくなります。この ID のレコードは、二度と表示または作成できません。

更新

[更新] をクリックする前に、ユーザは ID フィールドに ID 番号 (1 ~ 1000) を入力する必要があります。ページでは、そのレコードのデータでフォームのフィールドを更新します。これは、その ID のレコードを以前に削除していた場合にのみ失敗します。

エラー

データ・コントローラには、modelError というサーバ側プロパティがあります。これは、そのデータ・コントローラがサーバ側アクションの保存、ロード、削除、または呼び出しをする際に発生した最新のエラー・メッセージを収めた文字列です。データ・コントローラには、modelError の値を取得するためにアプリケーションから呼び出すことができるクライアント側 JavaScript メソッド getError もあります。getError は引数を持たず、modelError 文字列を返します。現時点でエラーがない場合は、空文字列 '' を返します。

<dynaForm> とオブジェクト・データ・モデル

このトピックでは、<dynaForm> をデータ・コントローラで使用する方法を説明します。この場合、データ・モデルはオブジェクト・データ・モデルです。

手順 1 : <dynaForm> は容易

最初の <dynaForm> は、以下のように非常に容易に作成できます。

  1. 新しい Zen ページ・クラスを作成します。"Zen の使用法" の “Zen チュートリアル” の章の演習 “Zen ページの作成” の説明に従います。新しいクラスの名前は自由に付けてかまいませんが、MyApp パッケージに保存してください。以前の作業内容を上書きしないように注意してください。

  2. XData Contents で、<dataController> と <dynaForm> を <page> の中に配置します。

    <page xmlns="http://www.intersystems.com/zen" title="">
      <dataController id="patientData"
                      modelClass="MyApp.PatientModel"
                      modelId="1" />
      <dynaForm controllerId="patientData"/>
    </page>
  3. [ビルド]→[コンパイル] を選択するか、Ctrl-F7 を押すか、generated description: studio compile アイコンをクリックします。

  4. [表示]→[ウェブページ] または generated description: studio webpage アイコンを選択します。

    フォームには 2 つのフィールドが表示され、そこには <dataController> 文に入力した modelId を持つレコードの現在の Name 値と City 値が示されます。おそらく前の演習でこれらの値が変更されているか、このレコードが削除されています。現在その modelId で使用できるデータが表示されます。

    generated description: mvc form text dyna

    各コントロールのラベルは、MyApp.PatientModel で対応するプロパティ名によって決まります。これらのラベルは、<form> コンポーネントおよび <text> コンポーネントを使用してフォームをレイアウトする際に caption 属性に割り当てたテキストとは異なります。その他のすべての表示は、演習 “オブジェクト・データ・モデルへの <form> の結合” の “手順 3 : 最初の結果” で最初に表示されたものと同じです。後述の手順では、<dynaForm> のコントロールの特定のラベルを設定する方法を示します。

手順 2 : <dynaForm> への変換

これで、この章の前の演習の <form> 例を <dynaForm> として再作成する準備ができました。これを実行するには、以下の MyApp.MyNewPage XData Contents ブロックを書き換える必要があります。

<page xmlns="http://www.intersystems.com/zen" title="">

  <dataController id="patientData"
                  modelClass="MyApp.PatientModel" />

  <dynaForm id="MyForm"
            controllerId="patientData"
            defaultGroupId="generatedFields">
    <text label="ID:"
          onblur="zenPage.loadRecord(zenThis.getValue())"
          dataBinding="%id"/>
    <vgroup id="generatedFields"/>
  </dynaForm>

  <hgroup>
    <button caption="Save" onclick="zenPage.save()"/>
    <button caption="New" onclick="zenPage.newRecord()"/>
    <button caption="Update" onclick="zenPage.updateRecord()"/>
    <button caption="Delete" onclick="zenPage.deleteRecord()"/>
  </hgroup>
</page>

つまり、以下のようになります。

  1. スタジオで、既存のサンプル・ページの MyApp.MyNewPage に戻ります。

  2. 各コントロールを個別に指定する、この <form> を削除するかコメントにします。

    <form id="MyForm"
          controllerId="patientData" >
      <text label="ID:"
            onblur="zenPage.loadRecord(zenThis.getValue())"
            dataBinding="%id"/>
      <text label="Patient Name" dataBinding="Name" />
      <text label="Patient City" dataBinding="City" />
    </form>
  3. 以下の <dynaForm> を追加します。この <dynaForm> は、データ・モデルのプロパティに基づいて生成できるコントロール定義を、データ・コントローラに依存して指定します。

    <dynaForm id="MyForm"
              controllerId="patientData"
              defaultGroupId="generatedFields">
      <text label="ID:"
            onblur="zenPage.loadRecord(zenThis.getValue())"
            dataBinding="%id"/>
      <vgroup id="generatedFields"/>
    </dynaForm>

    以下はその説明です。

    • controllerId は、データ・コントローラを表します。

    • defaultGroupId は、データ・コントローラによって生成されるコントロールを含む、フォーム上のグループを表します。

    • defaultGroupId はオプションですが、それを <dynaGroup> に表示し、さらに <dynaForm> 内の任意の場所に置く場合は、defaultGroupId と一致する id (この場合は <vgroup>) を持つグループを指定する必要があります。

    • 値が %id 変数に基づくコントロールなど、データ・コントローラに依存しないコントロールをフォームに表示する場合は、これらを明示的に配置する必要があります。

  4. [ビルド]→[コンパイル] を選択するか、Ctrl-F7 を押すか、generated description: studio compile アイコンをクリックします。

  5. [表示]→[ウェブページ] または generated description: studio webpage アイコンを選択します。

    <dynaForm> は以下のように表示されます。

    generated description: mvc form final

  6. <dynaForm> では、以下のように、生成されたコントロールに特定のラベルを割り当てることができます。

    • スタジオでオブジェクト・データ・モデル・クラス、MyApp.PatientModel を開きます。

    • 以下のようにプロパティを編集して、それぞれに ZENLABEL パラメータを追加します。

      Property Name As %String(ZENLABEL = "Patient Name");
      

      および、

      Property City As %String(ZENLABEL = "Patient City");
      
    • [ビルド]→[コンパイル] を選択するか、Ctrl-F7 を押すか、generated description: studio compile アイコンをクリックして、データ・モデルをコンパイルします。

  7. Zen ページの MyApp.MyNewPage をブラウザで更新します。

    これで、<dynaForm> と <form> で同一の結果が得られるようになりました。

手順 3 : コントロールの自動選択

<form> の代わりに <dynaForm> を使用すると、“オブジェクト・データ・モデルへの <form> の結合” の説明にあるように、データ・ビュー・コンポーネント (コントロール) を項目ごとにフォームに追加する必要がなくなります。<dynaForm> は、コンパイル時にデータ・モデルからこの情報を自動的に抽出します。

以下の演習では、別のデータ・モデルを使用するように前の演習のページ・クラスを変更することにより、<dynaForm> の使用法を示します。演習を終えてページを表示すると、新しいフォームには、前の演習のものとは明らかに異なるコントロールが表示されます。

  1. スタジオの SAMPLES ネームスペースに戻ります。

  2. [ツール]→[クラスをコピー] を選択します。

  3. [クラスをコピー] ダイアログが表示されます。以下を入力します。

    • [クラスをコピー]MyApp.PatientModel

    • [コピー先]MyApp.EmployeeModel

    • [クラス名のインスタンスを置換] チェック・ボックスにチェックを付けます。

    • [OK] をクリックします。

    MyApp.EmployeeModel の新しいクラス定義がスタジオに表示されます。

  4. MyApp.EmployeeModel を、以下の 3 つのプロパティのみを持つように編集します。

    Property Name As %String;
    Property Salary As %Numeric;
    Property Active As %Boolean;
    
    
  5. MyApp.EmployeeModel 内のメソッドを、以下の新しいプロパティを扱うように編集します。

    Method %OnLoadModel(pSource As ZENDemo.Data.Employee) As %Status
    {
      Set ..Name = pSource.Name
      Set ..Salary = pSource.Salary
      Set ..Active = pSource.Active
      Quit $$$OK
    }

    および、

    Method %OnStoreModel(pSource As ZENDemo.Data.Employee) As %Status
    {
      Set pSource.Name = ..Name
      Set pSource.Salary = ..Salary
      Set pSource.Active = ..Active
      Quit $$$OK
    }
  6. [ビルド]→[コンパイル] を選択するか、Ctrl-F7 を押すか、generated description: studio compile アイコンをクリックします。

  7. [ツール]→[クラスをコピー] を選択します。

  8. [クラスをコピー] ダイアログが表示されます。以下を入力します。

    • [クラスをコピー]MyApp.MyNewPage

    • [コピー先]MyApp.MyOtherPage

    • [クラス名のインスタンスを置換] チェック・ボックスにチェックを付けます。

    • [OK] をクリックします。

    MyApp.MyOtherPage の新しいクラス定義がスタジオに表示されます。

  9. <dataController> の id を "patientData" のままにして、操作を簡略化します。

    この簡略化を行わずに、"patientData" をより意味のある id、例えば "employeeData" にグローバルに置き換えることもできます。ただし、クラス内のこの id 文字列のすべてのインスタンスを必ず変更してください。すべて変更しない場合、実行時にエラーが発生します。

  10. <dataController> の modelClass を "MyApp.EmployeeModel" に変更します。

  11. [ビルド]→[コンパイル] を選択するか、Ctrl-F7 を押すか、generated description: studio compile アイコンをクリックします。

  12. [表示]→[ウェブページ] または generated description: studio webpage アイコンを選択します。

    以下の図は得られたフォームを示しています。このフォームには、レコード 5 の値が記述されています。

    generated description: mvc form dyna

    <dynaForm> では、このフォームのコントロールを以下のように選択しました。

    • Name には <text> (%String)

    • Salary には <text> (%Numeric)

    • Active ステータスには <checkbox> (%Boolean)

データ型に基づく <dynaForm> のコントロール

<dynaForm> は、モデルの各プロパティに割り当てるコントロールのタイプを、そのプロパティのデータ型に基づいて判断します。以下のテーブルは、プロパティのデータ型と、それに基づいて生成される <dynaForm> コントロールを示します。

Note:

<dynaForm> による選択結果に不都合がある場合は、<form> に切り替え、演習 “オブジェクト・データ・モデルへの <form> の結合” の “手順 2 : データ・ビュー” の説明にあるように、dataBinding 属性を使用して各コントロールを個別にプロパティに結合します。

データ型に基づく <dynaForm> のコントロール
データ型 詳細 コントロール
%ArrayOfDataTypesOpens in a new tab テーブルの後に示す “配列のデータ型” を参照してください。 <textarea>
%BooleanOpens in a new tab <checkbox>
%DateOpens in a new tab YYYY-MM-DD 形式 <dateSelect>
%DateOpens in a new tab 別の形式 <text>
%Enumerated 4 つ以下の値を持つ VALUELIST を使用 <radioSet>
%Enumerated 5 つ以上の値を持つ VALUELIST を使用 <combobox>
%ListOfDataTypesOpens in a new tab テーブルの後に示す “リストのデータ型” を参照してください。 <textarea>
%NumericOpens in a new tab <text>
オブジェクト参照 <dynaForm> で SQL クエリを生成 <dataCombo>
ストリーム %CharacterStreamOpens in a new tab <textarea>
ストリーム %BinaryStreamOpens in a new tab <image>
%StringOpens in a new tab MAXLEN が 250 を超える場合 <textarea>
%StringOpens in a new tab MAXLEN が 1 ~ 250 の場合 <text>
Public プロパティ 上記で挙げられていないすべてのタイプ <text>
Private プロパティ プライベートとしてマーク付けされているすべてのプロパティ 表示されません

前のテーブルに示したデータ型のほとんどは Caché のクラスとして定義されています。したがって、これらのデータ型では、テーブルで紹介した VALUELIST、DISPLAYLIST、MAXLEN などのクラス・パラメータを定義できます。これらのパラメータはデータ型に関する詳細を指定します。

%Enumerated プロパティでは、VALUELIST パラメータで内部値 (1、2、3) を指定し、DISPLAYLIST パラメータでユーザによる選択肢として表示する名前 (High、Medium、Low) を指定します。%StringOpens in a new tab プロパティでは、MAXLEN パラメータで最大文字数を指定します。

他にも、さまざまなクラス・パラメータがあります。詳細は、"Caché オブジェクトの使用法" の “データ型” の章にある “パラメータ” を参照してください。

リストのデータ型

データ型が %ListOfDataTypesOpens in a new tab のプロパティの場合、そのリスト・コレクションはキャリッジ・リターン文字で区切った 1 つの文字列としてクライアントにストリームされます。結果として得られる <textarea> コントロールでは、テキスト 1 行ごとに 1 つのコレクション項目が表示されます。

<dynaForm> では、データ・モデル・クラスがこのプロパティ・パラメータを以下のように設定する場合にこの規約が機能します。

ZENCONTROL="textarea"

詳細は、この章の “データ・モデル・クラス” の “データ・モデルのプロパティ・パラメータ” を参照してください。

<form> では、dataBinding 属性を使用して <textarea> コントロールを %ListOfDataTypesOpens in a new tab 型のプロパティに結合する場合にこの規約が機能します。

演習 “オブジェクト・データ・モデルへの <form> の結合” の “手順 2 : データ・ビュー” を参照してください。

配列のデータ型

データ型が %ArrayOfDataTypesOpens in a new tab のプロパティの場合、規約は、シリアル化した文字列が以下の形式をとることを除き、%ListOfDataTypesOpens in a new tab と同じです。

key:value[CR]key:value[CR]key:value

ここで : は 1 つのコロン、[CR] は 1 つのキャリッジ・リターン文字を表します。

<dynaForm> とアダプタ・データ・モデル

ここでは、データ・モデルがアダプタ・データ・モデルの場合に <dynaForm> をデータ・コントローラで使用する方法を説明します。この方法は、フォームに表示するプロパティを多数持つ既存のクラスで特に便利です。そのような場合、この章の前の演習に示したようにこれらのプロパティを 1 つずつ %ZEN.DataModel.ObjectDataModelOpens in a new tab のサブクラスに追加すると、非常に時間がかかります。

<dynaForm> を使用すると、特に %ZEN.DataModel.AdaptorOpens in a new tab のサブクラスと組み合わせた場合に、コーディングに要する時間を短縮できます。その場合には、<page> に <dataController> と <dynaForm> を持つ Zen ページ・クラスを作成するだけですみます。%ZEN.DataModel.AdaptorOpens in a new tab のサブクラスは一括して、モデル、ビュー、およびコントローラになります。そのプロパティはすべて、得られた <dynaForm> のコントロールとなります。Zen では、プロパティに対して適切なコントロール・タイプを自動的に生成します。

手順 1 : フォームの生成

アダプタ・データ・モデルでの <dynaForm> の使用の基本的な概要は、以下のとおりです。

Note:

この例は、SAMPLES ネームスペースの Zen クラス、ZENMVC.MVCDynaFormOpens in a new tabZENMVC.PersonOpens in a new tab、および ZENMVC.AddressOpens in a new tab を基にしています。

  1. %ZEN.DataModel.AdaptorOpens in a new tab も拡張するように永続クラスを編集します。例えば、以下のようになります。

    Class ZENMVC.Person Extends (%Persistent, %ZEN.DataModel.Adaptor)
    {
      Property Name As %String [ Required ];
      Property SSN As %String;
      Property DOB As %Date;
      Property Salary As %Numeric;
      Property Active As %Boolean;
      Property Home As Address;
      Property Business As Address;
    }
    

    Person クラスには、別のクラスである Address で定義されたプロパティも存在します。Address クラスも %ZEN.DataModel.AdaptorOpens in a new tab を拡張したものとする必要がありますが、永続である必要はありません。

    Class ZENMVC.Address Extends (%SerialObject, %ZEN.DataModel.Adaptor)
    {
      Property City As %String(MAXLEN = 50);
      Property State As %String(MAXLEN = 2);
      Property Zip As %String(MAXLEN = 15);
    }
  2. 両方のデータ・モデル・クラスをコンパイルします。

  3. 新しい Zen ページ・クラスを作成します。

  4. XData Contents で、<dataController> と <dynaForm> を <page> の中に配置します。

    <page xmlns="http://www.intersystems.com/zen" title="">
      <dataController id="source" modelClass="ZENMVC.Person" modelId=""/>
      <dynaForm id="MyForm" controllerId="source" />
    </page>
  5. Zen ページ・クラスをコンパイルします。

  6. [ビュー]→[ウェブ・ページ] または generated description: studio webpage アイコンを選択します。

    <dynaForm> で適切なコントロールが生成され、フォームが表示されます。例えば、以下のようになります。

    generated description: dynaform 1

データ型とそれに基づいて生成される <dynaForm> コントロールのテーブルは、この章の “データ型に基づく <dynaForm> のコントロール” のテーブルを参照してください。

手順 2 : プロパティ・パラメータ

基本事項を配置しておけば、アダプタ・データ・モデルとして使用している永続クラスのプロパティにデータ・モデル・パラメータを割り当てて、フォームを改良できます。以下はその例です。

  1. この演習の “手順 1 : フォームの生成” のデータ・モデル・クラス Person を開きます。

  2. 以下に示すように、データ・モデル・パラメータをプロパティに追加します。

    Class ZENMVC.Person Extends (%Persistent, %ZEN.DataModel.Adaptor)
    {
      Property Name As %String (ZENLABEL = "Employee Name") [ Required ];
      Property SSN As %String (ZENREADONLY = 1);
      Property DOB As %Date (ZENLABEL = "DateofBirth", ZENATTRS="format:DMY");
      Property Salary As %Numeric (ZENHIDDEN = 1);
      Property Active As %Boolean
        (ZENLABEL = "Is this person working?", ZENATTRS="showLabel:false");
      Property Home As Address;
      Property Business As Address;
    }
    
  3. Person クラスをコンパイルします。

  4. Zen ページ・クラスのビューをブラウザで更新します。

    <dynaForm> で適切なコントロールが生成され、フォームが表示されます。例えば、以下のようになります。

    generated description: dynaform 2

    データ・モデル・パラメータは、以下のように影響します。

    • ZENATTRS は、指定された属性および値を生成されたコントロールに適用します。

    • ZENHIDDEN は、プロパティに関連付けられたコントロールを非表示にします。

    • ZENLABEL は、既定のラベル (プロパティ名) をカスタム文字列に置き換えます。

    • ZENREADONLY は、コントロールを表示しますが、ユーザがその内容を編集できないようにします。

さらに多くのデータ・モデルのパラメータをリストし、<dynaForm> の生成されたコントロールへの影響を示すテーブルは、“データ・モデルのプロパティ・パラメータ” を参照してください。

手順 3 : <dynaForm> への動作の追加

<dynaForm> への動作の追加は <form> の場合と非常によく似た手順です。<dynaForm> での手順は以下のようになります。

  1. 手順 2 : プロパティ・パラメータ” の Zen ページ・クラスを開きます。

  2. 以下のように、現在のモデル ID の値を表示する <text> コントロールを追加し、更新、新規作成、および保存のためのボタンを追加します。

    <page xmlns="http://www.intersystems.com/zen" title="">
    
      <text label="Model ID:" id="idText" dataBinding="%id"
            onblur="zenPage.loadRecord(zenThis.getValue())" />
      <spacer height="10"/>
    
      <dataController id="source" modelClass="ZENMVC.Person" modelId=""/>
      <dynaForm id="MyForm" controllerId="source" />
    
      <hgroup>
        <button caption="Update" onclick="zenPage.showRecord();" />
        <button caption="New" onclick="zenPage.newRecord();" />
        <button caption="Save" onclick="zenPage.save();" />
      </hgroup>
    
    </page>
  3. 以下の対応する新しいクライアント側メソッドを、ページ・クラスに追加します。

    
    ClientMethod loadRecord(id) [ Language = javascript ]
    {
      var controller = zen('source');
      controller.setModelId(id);
    }

    および、

    
    ClientMethod showRecord() [ Language = javascript ]
    {
      var controller = zen('source');
      controller.update();
    }

    および、

    
    ClientMethod newRecord() [ Language = javascript ]
    {
      var text = zen('idText');
      text.setValue("");
      var controller = zen('source');
      controller.createNewObject();
    }

    および、

    
    ClientMethod save() [ Language = javascript ]
    {
      var form = zen('MyForm');
      form.save();
    }
    
  4. Zen ページ・クラスをコンパイルします。

  5. [ビュー]→[ウェブ・ページ] または generated description: studio webpage アイコンを選択します。

  6. [新規作成] ボタンをクリックします。

  7. フィールド (モデル ID および読み取り専用 SSN フィールドを除く) にデータを入力し、[保存] をクリックします。

  8. [新規作成] ボタンを再度クリックし、フィールドをクリアします。

  9. ID フィールドに数字の 1 を入力し、[更新] をクリックします。対応するレコードが表示されます。以下はその例です。

    generated description: dynaform 3

    Person クラスのモデル ID 値には特に形式を指定していないので、既定の設定が優先されます。つまり、新しく追加した各レコードには、1 から始まる連続番号が割り当てられます。手順 6 と 7 を繰り返すと、レコードをさらに追加できます。この番号は自動的にインクリメントされます。

    存在しない ID 番号を入力してレコードを表示しようとすると、<dynaForm> ではすべてのフィールドが無効になって表示されます。[新規作成] をクリックして、新しいレコードのデータを入力する、有効なフォームを再表示できます。

手順 4 : 仮想プロパティ

データ・モデルを使用する前にデータ・モデルに変更を挿入する場合は、そのデータ・モデル・クラスの %OnGetPropertyInfo メソッドをオーバーライドします。これは、モデルで使用する必要があるものの、最初はクラスに存在しない仮想プロパティを追加する方法です。 仮想プロパティを使用するには、データ・モデル・クラス・パラメータ DYNAMICPROPERTIES が 1 (True) に設定されていることを確認する必要があります。%ZEN.DataModel.AdaptorOpens in a new tab クラスでのその既定値は 0 (False) です。%OnGetPropertyInfo はいずれのタイプのデータ・モデルでも使用できますが、この場合は既存のクラスをデータ・モデルとして使用しているので、アダプタ・データ・モデルが最適です。

この演習では、この章の前の演習のアダプタ・データ・モデル・クラス Person に %OnGetPropertyInfo メソッドを追加します。

  1. スタジオで Person クラスを開きます。

  2. [クラス]→[オーバーライド] を選択します。

  3. %OnGetPropertyInfo を選択して、[OK] をクリックします。

    スタジオでは、骨格のみの %OnGetPropertyInfo メソッドがクラスに追加されます。

  4. 次の手順に従って、メソッドを編集します。

    これらの文は、このモデルにより生成された任意の <dynaForm> に <checkbox> と <textarea> を追加します。

    ClassMethod %OnGetPropertyInfo(pIndex As %Integer,
                                   ByRef pInfo As %String,
                                   pExtended As %Boolean = 0) As %Status
    {
      #; Increment past the 3 embedded properties from the last Address object.
      #; This is not necessary when the last property in the Person object
      #; is a simple data type such as %String or %Boolean or %Numeric.
      Set pIndex = pIndex + 3
    
      #; add a field at the end of the form
      Set pInfo("Extra") = pIndex
      Set pInfo("Extra","%type") = "checkbox"
      Set pInfo("Extra","caption") = "Extra!"
      Set pInfo("Extra","label") = "This is an extra checkbox."
      Set pIndex = pIndex + 1
    
      #; add another field at the end of the form
      Set pInfo("Comments") = pIndex
      Set pInfo("Comments","%type") = "textarea"
      Set pInfo("Comments","caption") = "Please enter additional comments:"
       Set pIndex = pIndex + 1
    
      Quit $$$OK
    }
    
  5. Person クラスをリコンパイルします。

  6. Zen ページ・クラスのビューをブラウザで更新します。

    <dynaForm> で適切なコントロールが生成され、フォームが表示されます。例えば、以下のようになります。

    generated description: dynaform 4

データ・モデル・クラス

データ・モデルの基本的な動作は、抽象ベース・クラス %ZEN.DataModel.DataModelOpens in a new tab から得られます。このクラスは基本的なデータ・モデル・インタフェースを定義し、このデータ・モデル・インタフェースはサブクラスによって実装されます。データ・モデル・クラスは、以下のいずれかのタイプになります。

  • データ・モデル・クラスは、独立したデータ・ソースのラッパの役割をします。データ・モデル・オブジェクトはインタフェースを指定し、ソース・オブジェクト (複数可) は実際のデータを指定します。この場合、データ・モデル・クラスはデータ・ソース・オブジェクトに関連する追加のコールバック・メソッドを実装する必要があります。ここのフォームの例では、%ZEN.DataModel.ObjectDataModelOpens in a new tab のサブクラスを作成し、いくつかのサーバ側コールバック・メソッドをオーバーライドすることにより、この規約に従っています。

  • データ・モデル・クラスもデータ・ソース・オブジェクトです。データ・モデル・オブジェクトは、データとインタフェースの両方を指定します。この場合、データ・ソース・オブジェクトに関連する追加のコールバック・メソッドをオーバーライドする必要はありません。データ・ソース・クラスで %ZEN.DataModel.AdaptorOpens in a new tab インタフェースを実装するだけですみます。その後は、得られたクラスをページ・クラスの <dataController> コンポーネントの modelClass として直接使用できます。

データ・モデル・クラスのプロパティ

%ZEN.DataModel.DataModelOpens in a new tab クラスには、以下のプロパティがあります。

  • データ・モデル・オブジェクトの現在アクティブなインスタンスを表す文字列。モデル ID とも呼ばれます。この値を初期設定するには、<dataController> の定義で modelId 属性を指定します。モデル ID の正確なフォームと指定可能な値は、具体的なデータ・モデル・クラスの開発者が決定します。プロパティ名は以下のとおりです。

    • dataModelId — クライアント側で実行する JavaScript コードで使用します。

    • %id — サーバ側で実行する ObjectScript、Caché Basic、または Caché MVBasic のコードで使用します。

    例は、この章の “<form> への動作の追加” を参照してください。

  • モデルのデータ系列の表示名を指定する、文字列の配列。文字列はそれぞれ、1 つのデータ系列のラベルとなります。この配列には、添え字として系列番号 (先頭は 1) が付きます。プロパティ名は以下のとおりです。

    • seriesNames — クライアント側で実行する JavaScript コードで使用します。

    • %seriesNames — サーバ側で実行する ObjectScript、Caché Basic、または Caché MVBasic のコードで使用します。

  • データ・モデルに存在するデータ系列の数。 プロパティ名は以下のとおりです。

    • seriesCount — クライアント側で実行する JavaScript コードで使用します。

    • %seriesCount — サーバ側で実行する ObjectScript、Caché Basic、または Caché MVBasic のコードで使用します。

    詳細は、“データ・モデル系列” を参照してください。

データ・モデル・クラスのパラメータ

データ・モデル・クラスには、データ・モデルのタイプを決めるクラス・パラメータがあります。以下のテーブルはその一覧です。

データ・モデル・クラスのパラメータ
プロパティ・パラメータ 説明
DOMAIN %ZEN.DataModel.ObjectDataModelOpens in a new tab のサブクラスでのみ利用可能。Zen のローカライズを使用する場合は、このパラメータの値を指定する必要があります。
DYNAMICPROPERTIES

1 (True) または 0 (False)。True の場合、このモデルは仮想プロパティをサポートします。背景情報は、“仮想プロパティ” を参照してください。DYNAMICPROPERTIES の既定値は次のとおりです。

READONLYMODEL 1 (True) または 0 (False)。 True の場合、これが読み取り専用モデルであることを示します。データの表示には使用できますが、編集可能なフォームの生成には使用できません。既定値は 0 (False) です。

データ・モデルのプロパティ・パラメータ

%ZEN.DataModel.ObjectDataModelOpens in a new tab クラスには、フォーム上でコントロールとして使用するデータ・モデル・プロパティに適用できるプロパティ・パラメータがあります。これらのパラメータを使用することにより、データ・モデル・クラスのプロパティをよりきめ細かく制御できます。ユーティリティ・クラス %ZEN.DataModel.objectModelParametersOpens in a new tab がこれらのパラメータを定義します。以下のテーブルに、パラメータを一覧にして示します。

Note:

例えば、スタジオを使用して、SAMPLES ネームスペースで ZENMVC.FormDataModelOpens in a new tab クラスと ZENMVC.FormDataModel2Opens in a new tab クラスを表示します。また、この章の演習 “<dynaForm> とアダプタ・データ・モデル” も参照してください。

データ・モデルのプロパティ・パラメータ
プロパティ・パラメータ 説明
ZENATTRS

このプロパティで使用するコントロールに適用する追加属性のリスト。この文字列の形式は以下のようになります。

"attribute:value|attribute:value"

ZENCONTROL

このプロパティをフォームに表示するために使用するコントロールのタイプ。定義しない場合は、プロパティのデータ型に基づいてコントロールのタイプが自動的に選択されます。

ZENCONTROL の値として単純なクラス名を指定した場合、Zen は、このクラスが %ZEN.Component パッケージ内にあると想定します。次の例では、%ZEN.Component.textareaOpens in a new tab クラスを指定しています。

ZENCONTROL = "textarea"

ZENCONTROL の値として、完全なパッケージとクラスの名前を指定することもできます。このパッケージとクラスは、ZENCONTROL パラメータ値を定義しているクラスと同じネームスペースに属している必要があります。次の例では、カスタム・コンポーネント・クラスを指定しています。

ZENCONTROL = "MyPackage.Utils.textAreaAppendable"

ZENDISPLAYCOLUMN 定義すると、このプロパティに対して自動的に生成される SQL 文の表示値を提供する列の名前となります。
ZENGROUP このプロパティに使用するコントロールの追加先とするグループ・コンポーネントの id。これで、レイアウトの制御が可能になります。定義しない場合、コントロールはフォームに直接追加されます。
ZENHIDDEN 1 (True) または 0 (False)。 True の場合、これが非表示のフィールドであることを示します。このフィールドの値はクライアントに送信されるとき、表示されません。既定値は 0 (False) です。
ZENLABEL フォーム内でこのプロパティに使用されるラベル。ラベルのコンマ区切りリストに追加されるため、ラベル・テキストにはコンマ (,) 文字を含めることはできません。
ZENREADONLY 1 (True) または 0 (False)。True の場合、これは読み取り専用のフィールドとなり、ユーザは編集できません。既定値は 0 (False) です。
ZENSIZE コントロールに設定した ZENSIZE パラメータは、そのコントロールのサイズ・プロパティを指定します。サイズの解釈はコントロールによって作成される HTML 要素によって異なります。例えば、テキスト・コントロールでは、サイズは表示する文字数に比例します。この動作は Zen ではなく HTML によって定義されます。
ZENSQL 定義した値は、このプロパティに指定可能な値を検索する SQL 文となります。このパラメータは、さまざまなデータ駆動型 Zen コンポーネントの sql プロパティに対応します。詳細は、“Zen のテーブル” の章の “SQL クエリの指定” のセクションを参照してください。“ ”
ZENSQLLOOKUP 定義した値は、指定した論理値に対する適切な表示値を検索する SQL 文となります。このパラメータは、<dataListBox><dataCombo> などのデータ駆動型 Zen コンポーネントが持つ sqlLookup プロパティに対応します。詳細は、“Zen コントロール” の章の “<dataCombo> の論理値と表示値” を参照してください。
ZENTAB 正の整数。プロパティをフォームに表示するコントロールの既定のタブ順序 (1 から始まる) を、指定した値でオーバーライドします。ZENTAB を指定したすべてのコントロールは、ZENTAB を定義しないコントロールの前に配置されます。
ZENTITLE フォーム内でこのプロパティに表示されるオプションのポップアップ・タイトル文字列。

値リストと表示リスト

データ・モデルに関連付けたフォームには、値リストと表示リストを別々に必要とするフィールドが存在することがあります。両方のリストとも文字列のリストです。値リストはサーバ上のストレージに置く論理値を提供します。表示リストはアプリケーションがクライアント上でユーザに表示する選択肢を指定します。この概念は、以下のようにあらゆる MVC データ・モデルに適用されます。

  • <form> をデータ・モデルで使用する場合、この概念は valueList 属性と displayList 属性を持つすべてのコントロール、つまり <radioSet><select>、および <combobox> に適用されます。詳細は、“Zen コントロール” の章を参照してください。

  • <dynaForm> をデータ・モデルで使用する場合、この概念は、%Enumerated 型のプロパティなど、VALUELIST クラス・パラメータおよび DISPLAYLIST クラス・パラメータを使用するデータ型であるすべてのプロパティに適用されます。データ型とそれに基づいて生成される <dynaForm> コントロールのテーブルは、この章の前半にある “データ型に基づく <dynaForm> のコントロール” のテーブルを参照してください。

これらのコントロール (<dynaForm> の場合は、これらのプロパティを表すために Zen で自動的に生成されるコントロール) はクライアント上に表示リストを表示します。データ・モデルは、値を必ず表示形式に変換してクライアントに送信します。Zen アプリケーションがローカライズされていない場合、クライアント側の値リストと表示リストは同じです。どちらもサーバ側の表示リストと同一の内容を持ちます。このコントロールのクライアント側論理はすべて、これらの値を必要とします。

Zen アプリケーションが複数言語にローカライズされていて、データ・モデル・クラスが DOMAIN クラス・パラメータを正しく定義している場合、規約は若干異なったものになります。クライアント側の値リストはサーバ側の表示リストと同一内容ですが、クライアント側の表示リストはサーバ側の表示値をローカル言語で表した値で構成されます。このコントロールのクライアント側論理はすべて、これらの値を必要とします。

例えば、MVC データ・モデル・クラスのプロパティで以下のように VALUELIST と DISPLAYLIST を使用するとします。

Property Sex As %String(VALUELIST=",1,2", DISPLAYLIST=",Male,Female");

この場合、Sex の論理値は 1 または 2 です。この論理値がデータベースに保存され、サーバ側論理で使用されます。MVC フォームには表示値のみが表示されます。具体的には、以下のように表示されます。

radioSet.valueList = "Male,Female"
radioSet.displayList = $$$Text("Male,Female")  
Note:

ローカライズ、DOMAIN パラメータ、および $$$Text マクロの詳細は、"Zen アプリケーションの開発" の “Zen のローカライズ” の章を参照してください。

オブジェクト・データ・モデルのコールバック・メソッド

オブジェクト・データ・モデルを作成する際、%ZEN.DataModel.ObjectDataModelOpens in a new tab のサブクラスを作成し、そのサーバ側コールバック・メソッドを実装します。以下のテーブルでは、これらのメソッドについて詳しく説明します。これらのメソッドのいくつかは、演習 “モデルの作成” の “手順 2 : オブジェクト・データ・モデル” で扱っています。これらのメソッドについては、[例] の列に “あり” と表示されています。

オブジェクト・データ・モデルのコールバック・メソッド
メソッド このコールバック・メソッドの呼び出しタイミング
%OnDeleteModel データ・モデルが削除されたとき。このメソッドは、データ・モデル・クラスが存在する場合、そのサブクラスによって実装されます。
%OnDeleteSource あり データ・モデルが削除されたとき。実装すると、指定した ID を持つオブジェクトが削除され、その操作の結果得られる状態コードが返されます。
%OnGetPropertyInfo %GetPropertyInfo メソッドにより呼び出されたとき。このテーブルの後に示す説明を参照してください。
%OnInvokeAction このモデル・オブジェクトでユーザ定義の名前付きアクションが呼び出されたとき。 このテーブルの後に示す説明を参照してください。このメソッドは、データ・モデル・クラスが存在する場合、そのサブクラスによって実装されます。
%OnLoadModel あり Zen は実際は、データ・ソースからデータ・モデル・オブジェクトに値をロードする作業を行います。ロードするデータは、ユーザに実際に表示されるデータのみです。これは、格納する前にデータ上で集約やその他の操作を実行する場所となります。
%OnNewSource あり データ・モデルに新しいインスタンスが必要になったとき。実装すると、データ・モデルで使用するデータ・ソース・オブジェクトの新しい (未保存の) インスタンスが開き、その参照が返されます。
%OnOpenSource あり データ・モデルが開かれたとき。実装すると、データ・モデルで使用するデータ・ソース・オブジェクトのインスタンスが開き、その参照が返されます。
%OnSaveSource あり データ・モデルが保存されたとき。実装すると、データ・ソースに対する変更が保存されます。指定したソース・オブジェクトが保存され、その操作の結果得られる状態コードが返されます。状態コードが返される前に、データ・モデルの %id プロパティはソース・オブジェクトの識別子に設定されます。
%OnStoreModel あり Zen は実際は、データ・モデルからデータ・ソースに値をコピーする作業を行います。このメソッドは、モデル (フォームでユーザが変更したものなど) からデータをロードして、ソース・オブジェクトに戻します。
%OnSubmit このデータ・モデルに接続されたフォームが送信されたとき。このデータ・モデルの内容には、このコールバックが呼び出される前に送信された値が指定されます。このコールバックの実装はオプションです。

仮想プロパティ

データ・コントローラは、データ・モデル内のプロパティに関する情報を検索する必要がある場合、そのデータ・モデルの %GetPropertyInfo メソッドを呼び出します。これにより、データ・モデルのプロパティに関する詳細情報を収めた多次元配列が返されます。この情報をまとめるコードは、データ・モデル・クラスのプロパティ、プロパティ・タイプ、およびプロパティ・パラメータに基づいて自動的に生成されます。

データ・モデル・クラスは、%OnGetPropertyInfo コールバック・メソッドをオーバーライドすることにより、%GetPropertyInfo によって返されるプロパティ情報を変更できます。%GetPropertyInfo は、プロパティ情報を返す直前に %OnGetPropertyInfo を呼び出します。%OnGetPropertyInfo は、プロパティ情報を収めた多次元配列を参照により受け取ります。%OnGetPropertyInfo では、この配列の内容を希望どおりに変更できます。プロパティは、追加、削除、または属性の変更が可能です。このメソッドを使用して追加する属性は、仮想プロパティと呼ばれます。仮想プロパティを使用するには、データ・モデル・クラス・パラメータ DYNAMICPROPERTIES が 1 (True) に設定されていることを確認する必要があります。

Note:

例は、演習 “<dynaForm> とアダプタ・データ・モデル” の “手順 4 : 仮想プロパティ” を参照してください。

%OnGetPropertyInfo シグニチャは以下のようになります。

ClassMethod %OnGetPropertyInfo(pIndex As %Integer,
                               ByRef pInfo As %String,
                               pExtended As %Boolean = 0,
                               pModelId As %String = "",
                               pContainer As %String = "") As %Status

以下はその説明です。

  • pIndex は、次の順番にあるプロパティをリストに追加するために使用するインデックス番号です。

  • pInfo は、このデータ・モデルのプロパティに関する情報を収めた多次元配列です。

  • pExtended が True の場合、プロパティに関するすべての情報が返されます。False の場合、返される必要があるものはプロパティ名のみです (アプリケーションではこれを単に無視できます)。

  • pModelId は、データ・モデル・オブジェクトの現在アクティブなインスタンスを表す文字列です。モデル ID とも呼ばれます。これは、動的なフォームの内容がデータ・モデル・オブジェクトのインスタンスごとに異なる可能性がある場合のために用意されています。モデル ID の正確なフォームと指定可能な値は、具体的なデータ・モデル・クラスの開発者が決定します。

  • これが埋め込みプロパティである場合、pContainer はこの埋め込みプロパティを含むプロパティの名前です。

%OnGetPropertyInfo メソッド内では、プロパティ情報の配列 pInfo に添え字としてプロパティ名が付きます。各プロパティの最上位ノードには、動的に生成されるフォーム内でプロパティの通常の位置を指定する、整数のインデックス番号があります。

%OnGetPropertyInfo で新しいプロパティをデータ・モデルに追加する場合は、該当するノードをプロパティ情報の配列に追加します。新しいプロパティは、“仮想” プロパティとして扱われます。つまり、正式にその名前で定義したプロパティが存在しなくても、その名前で値を設定し、取得できます。%OnGetPropertyInfo に新しいプロパティを追加する際は、最上位ノードを現在のインデックス番号に設定し、次のようにインデックスを 1 つインクリメントします。

  pInfo("Property") = pIndex
  Set pIndex = pIndex + 1

プロパティ情報の配列にはサブノードの番号があり、この番号を定義して、他のプロパティ属性の値を指定できます。組み込み属性は % で始まり、次のものがあります。

  • pInfo("Property","%type") = "control"

    %type サブノードは、動的なフォームを使用する際にこのタイプのプロパティに対して使用する Zen コントロールの名前を表します。この名前はコンポーネントのクラス名であることに注意してください。パッケージ名を指定しない場合、%ZEN.Component パッケージのコンポーネントであると見なされます。別のパッケージのコンポーネントを指定する場合は、完全なクラス名を使用してください。

  • pInfo("Property","%group") = "groupId"

    %group サブノードは、動的なフォームが持つグループ・コンポーネントの id を示します。%group を指定した場合、この id を持つグループが存在すれば、このプロパティのコントロールは、そのグループ内に作成されます。これにより、動的なフォーム内のコントロールのレイアウトを制御できます。

先頭に % が付かない属性は、同じ名前のコントロールのプロパティに適用する値を指定します。 例えば、以下の文では、MyProp プロパティに使用するコントロールの label プロパティが、動的なフォームによって "My Label" に設定されます。

Set pInfo("MyProp","label") = "This is an extra field!"

データ・モデルとデータ・コントローラは、それぞれ %OnGetPropertyInfo メソッドをサポートします。実行時に、Zen が生成されたフォームにコントロールを追加する順序は以下のようになります。

  1. データ・モデル・プロパティとそのパラメータに基づいて、コントロールの初期リストを作成します。

  2. データ・モデルの %OnGetPropertyInfo メソッドが存在する場合、そのメソッドを呼び出して、このコントロールのリストを変更します。

  3. データ・コントローラの %OnGetPropertyInfo メソッドが存在する場合、そのメソッドを呼び出して、このコントロールのリストをさらに変更します。

コントローラ・アクション

%OnInvokeAction コールバックを使用すると、データ・コントローラを介してデータ・モデルで呼び出せる “アクション” を定義できます。 クライアントは、以下のように dataController の invokeAction メソッドを呼び出すことにより、アクションを呼び出せます。

controller.invokeAction('MyAction',data);

今度は、これによりサーバ側のデータ・モデルの %OnInvokeAction コールバックが呼び出され、アクション名とデータ値が渡されます。アクション名とデータの解釈は、アプリケーション開発者が決定します。

データ・モデル系列

基本的なデータ・モデル・オブジェクトは、名前と値の組み合わせの系列で構成されています。

名前と値の組み合わせを持つデータ・モデル
generated description: mvc data model

データ・モデル内の名前と値の組み合わせは、データ・モデル・クラスのすべてのプロパティから、ZENHIDDEN のマークが付いたプロパティを除外し、%OnGetPropertyInfo で追加したすべてのプロパティを加え、%OnGetProperty で削除したすべてのプロパティを除外して構成されます。既定では系列の番号は 1 ですが、それより大きくすることもできます。モデルに複数の系列がある場合、概念的には、そのモデルはマトリックスになります。

データ系列を含むデータ・モデル
generated description: mvc data series

以下に示す SAMPLES クラスの ZENMVC.ChartDataModel2Opens in a new tab のように、独自に %OnLoadModel メソッドを記述する場合、モデルに複数の系列を追加できます。この例では、モデルに 3 つの系列を作成し、各系列のデータ・モデル・プロパティに値を割り当てます。

Class ZENMVC.ChartDataModel2 Extends %ZEN.DataModel.ObjectDataModel
{
Property Cars As %Integer;
Property Trucks As %Integer;
Property Trains As %Integer;
Property Airplanes As %Integer;
Property Ships As %Integer;

Method %OnLoadModel(pSource As %RegisteredObject) As %Status
{
  Set scale = 100

  #; This model has multiple data series. We set up the data series here.
  Set ..%seriesCount = 3
  Set ..%seriesNames(1) = "USA"
  Set ..%seriesNames(2) = "Europe"
  Set ..%seriesNames(3) = "Asia"

  #; Now we provide data for each property within each series.
  #; We use the %data array so that we can address multiple series.
  For n = 1:1:..%seriesCount {
    Set ..%data(n,"Cars") = $RANDOM(100) * scale
    Set ..%data(n,"Trucks") = $RANDOM(100) * scale
    Set ..%data(n,"Trains") = $RANDOM(100) * scale
    Set ..%data(n,"Airplanes") = $RANDOM(100) * scale
    Set ..%data(n,"Ships") = $RANDOM(100) * scale
    }
  Quit $$$OK
  }
}

データ・モデルに複数の系列があるとき、データ・モデルをグラフに結合すると、そのグラフはさまざまな系列を自動的に選択します。ただし、円グラフでは注意が必要です。系列は、グリッドと同様に作用します。フォームに一度に表示できる系列は 1 つのみなので、現在のビューに表示する系列を判断するには、データ・コントローラ属性 defaultSeries を使用する必要があります。

カスタム・データ・モデル・クラス

ほとんどの場合、%ZEN.DataModel.ObjectDataModelOpens in a new tab または %ZEN.DataModel.AdaptorOpens in a new tab で十分事足ります。ただし、例えばグローバルを表す目的などで、開発者が特殊なカテゴリのデータ・モデルを作成することが必要になる場合があります。その場合、開発者は %ZEN.DataModel.DataModelOpens in a new tab のサブクラスを作成し、そのサブクラスの詳細を実装する必要があります。

以下のテーブルは、データ・モデル・オブジェクトを扱うためにアプリケーションから呼び出せるメソッドを示しています。これらのメソッドの動作は、そのオブジェクトを実装する具体的な %ZEN.DataModel.DataModelOpens in a new tab サブクラスによって決まります。

カスタム・データ・モデル・クラスのメソッド
メソッド 説明
%DeleteModel 識別子の値で指定されたデータ・モデル・オブジェクトのインスタンスを削除します。これにより、指定された識別子の値が取得され、それを使用してソース・オブジェクトのインスタンスが削除されます (使用できる場合)データ・モデル・オブジェクトがインタフェースとデータ・ソースの両方として機能している場合は、データ・モデル・オブジェクト自体が削除されます。
%OpenModel 識別子の値で指定されたデータ・モデル・オブジェクトのインスタンスを開きます。これにより、指定された識別子の値が取得され、それを使用してソース・オブジェクトのインスタンスが検索され (使用できる場合)、さらにソース・オブジェクトの適切な値がデータ・モデル・オブジェクトのプロパティにコピーされます (データ・モデル・オブジェクトがインタフェースとデータ・ソースの両方として機能している場合、このコピーは実行されません)。
%SaveModel データ・モデル・オブジェクトのインスタンスを保存します。これにより、データ・モデル・オブジェクトのプロパティが適切なソース・オブジェクトにコピーされて戻り (使用できる場合)、ソース・オブジェクトが自身を保存します (データ・モデル・オブジェクトがインタフェースとデータ・ソースの両方として機能している場合、データ・モデル自体が保存されます)。
FeedbackOpens in a new tab