intra-mart Accel Platform IM-共通マスタ 拡張プログラミングガイド 第2版 2015-08-01

4.1. 詳細画面(登録・編集画面)の拡張

詳細画面のタブ拡張について、まず動作の概要を説明し、実装の詳細について説明します。

4.1.1. 詳細タブの動作概要

IM-共通マスタメンテナンス画面はプラグインをコントロールする基盤部分と、画面(タブ内)にデータを表示したり、実際にテーブルへのデータ入出力を行ったりするプラグインという構成で成り立っています。
詳細画面の構成の概要を【図:詳細タブの動作概要】に示します。
../../_images/detail_tab_actions_summary.png

【図:詳細タブの動作概要】

  • クライアント側へ情報を表示する際、まずサーバサイドで画面基盤が各プラグインを呼び出します。
    プラグインはそれぞれデータを取得し、画面基盤は各プラグインからデータを受け取り、受け取ったデータをまとめてクライアントサイドへ転送します。
    クライアントサイドでは転送したデータをそれぞれ対応するプラグイン毎に受渡します。
    クライアントサイドのプラグイン処理は受け取ったデータをタブに描画するなどの処理を行い、ユーザがデータを編集できるようにします。
  • ユーザがデータの編集を行い、画面の更新ボタンが押されると、画面基盤はクライアントサイドの各プラグインに永続化する情報を収集するよう依頼します。
    クライアントサイドの各プラグイン処理はそれぞれのタブに入力された情報を収集して画面基盤に返します。
    画面処理基盤は全てのタブのデータを集めて一括してサーバサイドへ転送しクライアントサイドから受け取ったデータをサーバサイドの各プラグイン処理に渡します。
    サーバサイドのプラグイン処理は受け取ったデータをもとにテーブルの更新処理などを行います。

以上から、プラグインとしてタブを追加するために、以下のような実装が必要となります。

  • タブを描画するための HTML,またはJSSP
  • クライアントサイドでタブと画面処理基盤とのデータの受け渡しを行うjavascript
  • サーバサイドで永続化されたデータを入出力する javascript

他にpluginの設定ファイル(plugin.xml)が必要になりますが、詳細は「詳細タブの実装 」で解説します。

4.1.1.1. 画面起動時のシーケンス

画面起動時の動作シーケンスを【図: タブ画面起動時のシーケンス】に示します。

../../_images/tab_screen_sequence.png

【図:タブ画面起動時のシーケンス】

上記はタブが二つ(基本タブと拡張したタブ)ある場合の例です。処理の順番に以下に概要を説明します。

  1. init (画面起動時の初期処理/サーバサイド処理)
    クライアントサイドからの画面を表示するリクエストです。ユーザによって画面の表示が行われた場合に、まずこの処理から実行されます。
    画面処理基盤が初期化やプラグイン情報の収集処理を行います。
  2. getDetail (表示情報の取得処理/サーバサイド処理) :
    詳細情報取得のリクエスト。画面起動時には自動的に発生します。ほかにも画面で最新の情報が必要になった場合等に発生します。
    このタイミングで各プラグインのサーバサイドの処理が呼び出されます。引数に前提条件となる情報が渡されますので、各プラグインは画面に表示するための情報を取得して返します。
  3. init (画面の初期化処理。クライアントサイド処理)
    初期化処理を実行するために、各プラグインのクライアント処理が呼び出されます。
    特に画面処理基盤側から期待する処理や戻り値はありません。
  4. renderer(画面の内容表示処理クライアントサイド処理)
    各タブの情報を表示するために各プラグインのクライアント処理が呼び出されます。
    上記 2 で述べたサーバサイドでの処理が返したデータがそのまま引数に渡されます。

4.1.1.2. 更新時のシーケンス

ユーザが更新のアクションを起こした際に発生する、更新処理のシーケンスを【図:更新処理実行時のシーケンス】に示します。

../../_images/update_processing_sequence.png

【図:更新処理実行時のシーケンス】

  1. validate(入力値検証処理/クライアントサイド処理)
    ユーザの操作により更新処理が実行された場合に、まずクライアントサイドで各タブの入力チェック処理の契機としてvalidate関数が実行されます。プラグインで各タブの内容をチェックし、問題がある場合はエラーを返します。
    validateは全てのタブに対して実行され、一つでもエラーが返された場合、画面処理基盤はエラーメッセージを表示して更新処理を中断します。
  2. modeler(入力値収集処理/クライアントサイド処理)
    各タブのユーザ入力値を収集するために、画面処理基盤が各プラグイン処理のmodeler を実行します。この処理は各タブでユーザが入力した値をJSON 文字列で表現可能なObject として返す必要があります。これはサーバサイドのプラグイン処理にそのまま渡されます。
    画面処理基盤は全てのタブに対して modeler を呼び出し、全ての情報を一括してサーバサイドへ送信します。
  3. validate(入力値検証処理/サーバサイド処理)
    データベースに保管されているデータとの関連チェックなどのために、再度plugin に対して入力値の検証を行う機会があります。
    サーバサイドでもクライアントサイド同様にvalidate 関数(実際の関数名はplugin.xml ファイルによってマッピングされます)が呼び出されます。
  4. setDetail(更新処理/サーバサイド処理)
    クライアントサイドから収集してきたデータを更新するために各プラグインのsetDetail 関数(これも実際の関数名はplugin.xml ファイルによりマッピングされます)が呼び出されます。
    引数には全てのプラグインのデータが渡されます。処理が成功した場合は最新の情報を取得し直して返します。

4.1.2. 詳細タブの実装

これまでに説明した通り、タブを追加するためには次のものを準備する必要があります。

  • プラグインの構成情報を記述する plugin.xml
  • タブ内の UI を構築するJSSP(js+html)
  • クライアントサイドでデータの入出力やイベントをハンドリングするjs ファイル
  • サーバサイドでデータにアクセスするロジック(js ファイル)

以下では、この他の実装の詳細を順を追って説明します。

4.1.2.1. プラグインの構成情報を記述する plugin.xmlの作成

ここでは品目詳細画面の所属タブを例にとって説明します。
品目詳細画面の所属タブの例を【リスト:品目詳細画面所属タブのplugin.xml】に示します。
<?xml version="1.0" encoding="utf-8"?>
<plugin>
   <extension point="jp.co.intra_mart.foundation.master.setting.item.detail.contents">
      <detail_item_attach_config
         name="standard"
         id="jp.co.intra_mart.master.item.detail.attach"
         version="8.0.0"
         rank="3">
         <content
            title="%tabname"
            csjs="im.app.master.plugins.item.detail.tabs.attach.ItemAttach"
            page="master/plugins/maintenance/item/detail/tabs/attach/item_attach"
            min_height="280"
            />
         <operations page="master/plugins/maintenance/item/detail/tabs/attach/item_attach" object="">
            <operation id="get_detail" method="getDetail"></operation>
            <operation id="set_detail" method="setDetail"></operation>
            <operation id="validate" method="validate"></operation>
            <operation id="remove" method="remove"></operation>
         </operations>
      </detail_item_attach_config>
   </extension>
</plugin>

【リスト:品目詳細画面所属タブのplugin.xml】

plugin.xml の書き方や構成についての詳細はPlugin Manager のマニュアルを参照してください。
ここではリスナーの追加に当たって必要な範囲で説明します。
  • extension タグ
  • point属性:タブを拡張したい対象の拡張ポイントを指定します。
    IM-共通マスタのメンテナンス画面で用意している拡張ポイントについては「マスタメンテナンス画面の拡張に関する情報 」で説明していますので参照してください。
  • detail_item_attach_config タグ
  • このタグ名は任意の名称が使用可能です。
  • name、id、version、rank などの属性はPlugin Manager によって依存関係の管理などに使用されます。詳細はPlugin Manager のドキュメントを参照してください
  • content タグ
  • title 属性:追加したタブに表示されるタイトルを指定します。%表記は国際化メッセージキーを表します。国際化に関してはPlugin Manager のドキュメントを参照してください。
  • csjs 属性:クライアントサイドでタブの情報をハンドリングする実装を指定します。この処理はim-jspackman で管理可能なクラス形式である必要があります。ここにはそのクラスのFQDN を指定します。
  • page 属性:タブ内のUI を構築するJSSP のパスを指定します(拡張子無し)
  • min_height 属性:最低限必要な表示高さをpixel で指定します。特に指定しなければ他のTab の要求サイズや実際の表示領域の大きさを勘案して自動的に決定します。
  • operations タグ
  • page 属性:サーバサイドでデータにアクセスするロジックのjs ファイルのパスを指定します(拡張子無し)
  • operation タグ : 処理と実装をマッピングする情報です。画面処理基盤側が必要とするid に対してmethodのマッピングを定義します。
  • id属性:詳細・更新タブで定義する必要のあるidは 【表:サーバサイドスクリプトで実装する必要のある処理】で挙げているget_detail、set_detail、validate、removeの4 つです。operationタグが4 つ必要になります。
  • method 属性:operations タグのpage 属性で指定したファンクションコンテナ内のメソッド名を指定します。上記のID に対応する形で記述してください。

この xml ファイルをPlugin Manager の管理するディレクトリに配置します。具体的には以下のパスになります。

<(展開した war)/WEB-INF/plugin/%plugin_id%/plugin.xml>

plugin.xml の変更を行った場合には再起動が必要になります。

4.1.2.2. タブ内のUIを構築するJSSP(js+html) の作成

JSSP内の処理に関してはタブの初期表示に必要な処理以外には特別な記述を行う必要はありません。初回のinitの引数が通常と異なりますので注意が必要です。
initの引数には詳細画面が受け取っている引数がObjectでとして渡されます。内容は機能によって異なりますが、概ね以下の内容が渡されます。
  • 表示中の基準日やロケールなど、画面表示に使用する基本情報(basicInfo)
  • タブ内を描画するにあたり渡されるパラメータ(parameters)
  • 画面が新規登録として開かれているか、編集として開かれているかを表すフラグ(isEdit)
  • 現在選択中の期間情報(term)
  • 編集として開いている場合、編集する対の情報のビジネスキー(recordInfo)
  • 所属構造を持っており、画面操作の流れ上所属先が選択されている場合であればその所属先のビジネスキー(parentInfo)

詳細な内容は機能によって異なります。詳細はIM-共通マスタで提供する各拡張ポイントについて、詳細を記載したドキュメント「IM-共通マスタ 拡張インタフェース定義一覧」をご覧確認下さい。

ここで作成したファイルは 「プラグインの構成情報を記述する plugin.xmlの作成 」でcontentタグのpageプロパティと一致する場所に配置します。

4.1.2.3. クライアントサイドでデータの入出力やイベントをハンドリングするjsファイルの作成

データの出し入れに関しては画面基盤プログラムがサーバサイドとクライアントサイドでデータの通信を一括して取り扱います。このため、クライアントサイドではデータの表示や、ユーザの入力したデータの収集を各Pluginのクライアントサイドスクリプトに依頼します。
画面基盤プログラムは画面表示の更新が必要になったタイミングや、更新ボタンが押された等、特定のタイミングでクライアントサイドスクリプトの特定のメソッドを呼び出します。
クライアントサイドスクリプトはim-JSPackmanの実装方式にしたがって定義したクラスである必要があります(plugin.xmlに指定したクラス名から、画面基盤が動的にロードしてインスタンス化します)。
クライアントサイド処理で必要な処理を 【表:クライアントサイドスクリプトに必要なメソッドの一覧】 に示します。
  メソッド名 戻り値 説明
1 init(window) void
タブを初期表示した時点で呼び出されます。引数にはタブ自身を表すwindowオブジェクトが渡されます。
戻り値は必要ありません。
2 renderer(window,model) void
画面の表示を更新する必要がある場合(ユーザ操作による画面表示更新操作など)に呼び出されます。引数にはタブ自身を表すwindowオブジェクトと、サーバサイド処理の取得処理(plugin.xmlでid:get_detailに指定された処理)を実行して返された内容(model)をそのまま渡します。
戻り値は必要ありません。
3 validate Object
次で説明するmodelerが呼び出される直前に呼び出されます。
クライアントサイドで可能な範囲で入力値検証処理を実装できます。戻り値の形式は以下のとおりです。
../../_images/validate_return_value_table.png
とくに問題がなければerror をtrue に設定して返して下さい。
4 modeler(window) Object
画面のデータを収集する必要がある場合(更新時など)に呼び出されます。この関数の戻り値の内容を各タブのplugin idをキーとした連想配列に収めた物がサーバサイドの更新処理(plugin.xmlでid:set_detailに指定された処理)に渡されます。
特に形式はありませんがJSONで表せる内容である必要があります。
5 isUpdated(window) boolean
画面基盤側がタブの内容がユーザによって変更されているかチェックしたい場合に呼び出されます。(画面遷移時などに編集中データが残っていないかの確認に使用)。画面の内容が変更されているか否かをtrue/falseで返してください。
画面基盤側はtrueを受け取ると編集中の情報を破棄して良いかをユーザに問い合わせるダイアログを表示します。

【表:クライアントサイドスクリプトに必要なメソッドの一覧】

これらの処理を、「プラグインの構成情報を記述する plugin.xmlの作成 」でcontentタグのcsjs属性に指定するクラスに実装します。
以下に品目詳細画面 所属タブの例を示します。
Package("im.app.master.plugins.item.detail.tabs.attach");

/**
* 品目詳細画面 所属タブのクライアントサイドスクリプト
*
*/

Class("im.app.master.plugins.item.detail.tabs.attach.ItemAttach").define(

   im.app.master.plugins.item.detail.tabs.attach.ItemAttach = function () {

/*・・・ 略 ・・・ */

      /**
      * init
      * クライアントサイドの初期化処理
      * タブのロードが完了したタイミングで呼び出されます。
      **/
   this.init = function(tabwindow,basicInfo,param) {

   };

   /**
    * renderer
    * クライアントサイドの描画処理
    * 初期表示、及び、タブ内の再描画が必要なタイミングで呼び出されます。
    **/
   this.renderer = function(tabwindow,model,parameters) {

      tabwindow.attach_term_data = model.data ? model.data : new Array();
    /*・・・ 略 ・・・ */

   };

   /**
    * modeler
    * クライアントサイドの情報取得処理
    * クライアントサイドの情報をサーバサイドに転送する際に呼び出されます。
    * このメソッドはタブ内で編集された情報をオブジェクトに纏めて返します。
    **/
   this.modeler = function(tabwindow) {
      var o = new Object();
      o.termsInfo = tabwindow.attach_term_data;
      o.removedParents = tabwindow.removedParents;
      return o;
   };

   /**
    * validate
    * クライアントサイドの検証処理
    * modelerの直前に呼ばれ、クライアントサイドで可能な範囲で値の検証を行います。
    * エラーがあればエラーフラグ・エラーメッセージを設定したオブジェクトを返します。
    **/
   this.validate = function(contentWindow){
      return {};
   };

   /**
    * isUpdated
    * 画面処理基盤が画面を閉じようとしたり遷移しようとした場合に
    * タブ内に編集中のデータが無いか確認するために呼び出します。
    * trueを返すと、ユーザに対し編集中のデータがあるが続行して良いか
    * 問い合わせるダイアログを表示します。
    **/
   this.isUpdated = function(tabwindow){
      var propertyCount = 0;

      for( var i=0; i<tabwindow.attach_term_data.length; i++  ){
         if(tabwindow.attach_term_data[i].modified || tabwindow.attach_term_data[i].created) {
            return true;
            }
         }
         if(tabwindow.removedParents && tabwindow.removedParents.length > 0){
            return true;
         }

         return false;
      };

      /* constructor */
      {
         this.superclass();
      }
   }

);

【リスト:品目詳細画面所属タブのクライアントサイドjs の例】

クライアントサイドスクリプトの配置場所は <(展開したwar)/csjs> 配下に、パッケージ名にあわせてディレクトリを作成して配置して下さい。
上記の品目詳細画面 所属タブの例では、パッケージがim.app.master.plugins.item.detail.tabs.attach、クラス名がItemAttachなので、実際には以下のファイルになります。
<(展開したwar)/csjs/im/app/master/plugins/item/detail/tabs/attach/ItemAttach.js>

4.1.2.4. サーバサイドでデータにアクセスするロジック(jsファイル) の作成

クライアントサイドでmodeler()が収集した情報をデータベースに反映したり、要求された情報を検索して返す処理を実装します。
実装する処理のファイル名や関数名は「プラグインの構成情報を記述する plugin.xmlの作成 」で定義している値と一致していなければなりません。
operationsタグのpage属性と一致する場所にjsファイルを作成し、operationタグのmethod属性に指定した名前と同じ名前で関数を実装して下さい。
詳細画面で実装する必要のあるメソッドは以下の通りですが、引数や期待される戻り値の詳細な定義は機能ごとに異なります。別途公開されている「IM-共通マスタ 拡張インタフェース定義一覧」をご確認下さい。
  処理(plugin.xml上のid) 説明
1 取得処理(id:get_detail)
編集対象の詳細情報を取得して画面に表示する必要がある場合にこの取得処理が呼び出されます。
この処理の結果がクライアントサイドのrenderer()にそのまま渡されますので、連携の取れるようにデータを返してください。
また、エラーが発生した場合は戻り値のエラーフラグをtrueに設定しておくことで、処理を中断させることが出来ます。
2 検証処理(id:validate)
編集画面で編集された情報をデータベースに更新する直前に、内容を検証するために呼び出されます。引数にはクライアントサイドの全てのタブについて、modeler()が集めた形式のオブジェクトがplugin-idをキーとした連想配列として渡されます。Plugin側ではこの連想配列から自身のPlugin-idに一致するデータを取り出して処理する必要があります。
エラーが発生した場合は戻り値のエラーフラグをtrueに設定しておくことで、処理を中断させることが出来ます。
※値検証は複数のエラーメッセージを同時に返せるようにmessageプロパティが配列型になっています。
3 更新処理(id:set_detail)
編集画面で編集された情報をデータベースに更新するなど、永続化を行う処理です。引数の形式は検証処理と同じ内容が渡されます。処理が完了した場合は更新後のデータを戻してください。(取得処理と同じ形式)
エラーが発生した場合は戻り値のエラーフラグをtrueに設定しておくことで、画面基盤処理がロールバックをし、エラーメッセージ表示を行います。
4 削除処理(id:remove)
対象のエンティティを削除する処理です。引数の形式は更新処理と同じ内容が渡されます。
エラーが発生した場合は戻り値のエラーフラグをtrueに設定しておくことで、画面基盤側では処理をロールバックし、エラーメッセージを表示させることが出来ます。

以上で詳細画面のタブ拡張の実装は完了です。