先日開催されました技術者交流会にて、クライアントサイドスクリプトを利用したFormaDesignerの画面開発が難しいという声を多くいただきました。
そこで、今回のブログ記事では、交流会の場で挙がった課題を解決させるためのTipsと新機能をご紹介します。
目次
1. スクリプトの履歴管理・共通化を実現したい
■ FormaDesignerの開発における履歴管理・共通化の課題
従来のスクラッチ開発をベースとしたシステム開発では、作成したソースファイルを統合開発環境のプロジェクトの構成にて管理します。
プロジェクト形式に整理されたソースファイルは、バージョン管理システムに保存され、各リリースごとの差分が簡単に把握できます。
また、各画面にて共通となる処理については、同じ内容を各画面ごとに管理するのではなく、共通のソースファイルとして切り出します。
一方、FormaDesignerを利用した開発にてスクリプトを組み込む場合、スクリプト記述用の画面アイテムのプロパティにソースコードを直接記述します。
プロパティに記述されたソースコードの実体は、各フォームごとの定義情報が格納されたjsonファイルに出力されます。
jsonファイルにはスクリプトだけでなく、デザイナーで設定したあらゆるプロパティが格納されるため、開発したスクリプトのソースファイルとして扱うことはできません。
そのため、FormaDesignerの開発では、従来のスクラッチ開発の手法の利用が難しい点があります。
- ソースファイルとして切り出せないため、バージョン管理ができない。
- 個々のフォームのにスクリプトが格納されるため、ソースファイルの共通化が行えない。
次の章では、これらの課題を解決するための「テンプレートHTML」の活用方法を説明します。
■ テンプレートHTMLによるスクリプトの外出し・共通化
FormaDesignerのアプリケーションが実行される画面は、テンプレートHTMLというベースとなるHTMLファイル上に、各画面アイテムのHTMLが配置されて生成される仕組みになっています。
各画面アイテムのインスタンスにて共通した処理となるスクリプトについてはHTMLサイズ削減のため外部ファイルとして外出しで管理され、ベースとなるテンプレートHTMLからscriptタグを通して読み込まれます。
1 |
パブリックストレージ/forma/html_template/template.html |
上記のテンプレートHTMLはパブリックストレージ上に配置されておりますが、こちらをカスタマイズし、scriptタグを記述することが可能です。
scriptタグを記述することで、アプリケーションの実行画面にて任意のスクリプトファイルを読み込ませることができます。
上記の仕組みを利用して、スクリプト記述用の画面アイテムのプロパティから、外部ファイルに外出しすることで、スクリプトのソースファイルについてもバージョン管理の対象とすることが可能です。
また、テンプレートHTMLで読み込まれるスクリプトファイルは全アプリケーションに適用されるため、ソースファイルの共通化も実現できます。
【注意事項1】
IM-FormaDesigner 2014 Winterアップデートより、
テンプレートHTMLファイルは初回読み込み時にキャッシュされる動作となっておりますので、テンプレートHTMLをカスタマイズされた際は再起動もしくは「HTMLテンプレートキャッシュ削除」ジョブを実行していただく必要がございます。
【注意事項2】
テンプレートHTMLのファイルは製品モジュールのソースファイルに該当しますので、今後のアップデートにて修正される可能性があります。
アップデート時には製品側で修正された内容をカスタマイズされたテンプレートHTMLファイルにマージしていただく必要がございます。
テンプレートHTMLの共有範囲の限定
テンプレートHTMLは通常1テナント単位にて管理されており、テナント上の全てのアプリケーションで共有されますが、共有範囲を限定することも可能です。
各アプリケーション、各フォームごとに固有のテンプレートHTMLを指定可能です。
下記のディレクトリに配置することで、固有のテンプレートHTMLが読み込まれます。
- アプリケーション固有のテンプレート
1パブリックストレージ/forma/html_template/%アプリケーションID%/%アプリケーションID%.html - フォーム固有のテンプレート
1パブリックストレージ/forma/html_template/%アプリケーションID%/%フォームID%/%フォームID%.html
各テンプレートの読み込みの優先順位は、下記の通りです。
フォーム固有のテンプレート > アプリケーション固有のテンプレート > テナント固有のテンプレート
【注意事項1】
IM-FormaDesigner 2014 Winterアップデートより、
テンプレートHTMLファイルは初回読み込み時にキャッシュされる動作となっておりますので、テンプレートHTMLをカスタマイズされた際は再起動もしくは「HTMLテンプレートキャッシュ削除」ジョブを実行していただく必要がございます。
【注意事項2】
テンプレートHTMLのファイルは製品モジュールのソースファイルに該当しますので、今後のアップデートにて修正される可能性があります。
アップデート時には製品側で修正された内容をカスタマイズされたテンプレートHTMLファイルにマージしていただく必要がございます。
チュートリアル
ここでは、実際にアプリケーション固有のテンプレートを使って、外部ファイルで定義したスクリプトをアプリケーションに組み込む手順をご紹介します。
今回は例として、サンプルアプリケーション「稟議書」の稟議件名フィールドに入力された値がIM-Workflowの案件名に連動されるスクリプトを組み込みます。
1. eBuilderのモジュールプロジェクトを作成します。
スクリプトファイルとテンプレートHTMLを管理するためのモジュールプロジェクトを作成します。
モジュールの依存関係には、formaモジュール(モジュールID : jp.co.intra_mart.forma)を指定します。
詳しくは、
「intra-mart e Builder for Accel Platform / ユーザ操作ガイド モジュール・プロジェクト作成」
をご参照ください。
2. アプリケーションに組み込むためのスクリプトを作成します。
モジュールプロジェクト上にテンプレートに組み込むjsファイルを作成し、任意の処理を記述します。
3. アプリケーション固有のテンプレートを作成します。
(1)デバッグサーバのパブリックストレージからテナント固有のテンプレートHTMLを取得します。
(2)取得したテンプレートHTMLをリネームし、モジュールプロジェクトのストレージ上にアプリケーション固有のテンプレートとして配置します。
4. テンプレートHTMLをカスタマイズし、2で作成したJsファイルが読み込まれるようにscriptタグで指定します。
5. モジュールプロジェクトをビルドし、デバッグサーバへデプロイします。
6. アプリ実行画面を表示し、スクリプトが正しく動作することを確認します。
(1) 稟議書アプリケーションの画面にて、稟議件名を入力します。
(2) IM-Workflowの申請画面を表示し、稟議件名が案件名に連動されていることを確認します。
2. 画面アイテムを対象にした処理を作成したい
各アイテムの仕様が公開されていないため、各アイテムを対象にした処理を記述する際にブラウザの開発者ツールなどで各アイテムのDOM構造を調査する必要があります。
APIを利用して処理を記述することで、各アイテムごとの仕様を調査せずに、対象のアイテムを操作することが可能です。
入力アイテムから値を取得するAPI
入力アイテムから値を取得するAPI
アイテム名 | API |
---|---|
文字列 | formaItems.product_72_textbox.getItemData.%フィールド識別ID% |
複数行文字列 | formaItems.product_72_textarea.getItemData.%フィールド識別ID% |
数値 | formaItems.product_72_number.getItemData.%フィールド識別ID% |
隠しパラメータ | formaItems.product_72_hidden.getItemData.%フィールド識別ID% |
日付 | formaItems.product_72_calendar.getItemData.%フィールド識別ID% |
期間 | formaItems.product_72_terms.getItemData.%フィールド識別ID% |
一覧選択(v72) | formaItems.product_72_itemSelect.getItemData.%フィールド識別ID% |
チェックボックス(v72) | formaItems.product_72_checkbox.getItemData.%フィールド識別ID% |
セレクトボックス(v72) | formaItems.product_72_selectbox.getItemData.%フィールド識別ID% |
リストボックス(v72) | formaItems.product_72_listbox.getItemData.%フィールド識別ID% |
ユーザ選択 | formaItems.product_72_userSelect.getItemData.%フィールド識別ID% |
組織選択 | formaItems.product_72_departmentSelect.getItemData.%フィールド識別ID% |
組織・役職選択 | formaItems.product_72_departmentPostSelect.getItemData.%フィールド識別ID% |
所属組織選択 | formaItems.product_72_affiliationSelect.getItemData.%フィールド識別ID% |
テーブルアイテムから値を取得するAPI
アイテム名 | API |
---|---|
グリッドテーブル | formaItems.product_80_gridtable.getItemData.%テーブル識別ID%(String rowId,Array inputIdList) |
(サンプルコード) 入力アイテムから値を取得する
文字列 入力値の取得
1 2 3 4 |
(function($){ var result = formaItems.product_72_textbox.getItemData.%フィールド識別ID%(); alert(result); })(jQuery); |
リストボックス(v72) 入力値の取得
1 2 3 4 5 6 7 8 |
(function($){ var result = formaItems.product_72_listbox.getItemData.%フィールド識別ID%(); var valueArray = result.split( ',' ); for(var i=0; i<valueArray.length; i++){ alert(valueArray[i]); } })(jQuery); |
グリッドテーブル 全行取得
1 2 3 4 5 6 7 8 9 10 |
(function($){ var rowId = ""; var inputIdList = []; inputIdList[0] = "%フィールド識別ID%"; var result = formaItems.product_80_gridtable.getItemData.%テーブル識別ID%(rowId, inputIdList); for(var i=0; i<result.length; i++){ alert(result[i].%フィールド識別ID%); } })(jQuery); |
グリッドテーブル 特定行取得
1 2 3 4 5 6 7 |
(function($){ var rowId = 行数; var inputIdList = []; inputIdList[0] = "%フィールド識別ID"; var result = formaItems.product_80_gridtable.getItemData.%テーブル識別ID%(rowId, inputIdList); alert(result[0].%フィールド識別ID%); })(jQuery); |
入力アイテムに値を反映するAPI
入力アイテムに値を反映するAPI
アイテム名 | API | 備考 |
---|---|---|
文字列 | formaItems.product_72_textbox.setItemData.%フィールド識別ID%(Object arg) | |
複数行文字列 | formaItems.product_72_textarea.setItemData.%フィールド識別ID%(Object arg) | |
数値 | formaItems.product_72_number.setItemData.%フィールド識別ID%(Object arg) | |
日付 | formaItems.product_72_calendar.setItemData.%フィールド識別ID%(Object arg) | |
期間 | formaItems.product_72_terms.setItemData.%フィールド識別ID%(Object arg) | |
隠しパラメータ | formaItems.product_72_hidden.setItemData.%フィールド識別ID%(Object arg) | |
一覧選択(v72) | formaItems.product_72_itemSelect.setItemData.%フィールド識別ID%(Object arg) | |
チェックボックス(v72) | formaItems.product_72_checkbox.setItemData.%フィールド識別ID%(Object arg) | 複数要素指定はカンマ区切りの文字列。送信値に一致する行が選択される。 |
セレクトボックス(v72) | formaItems.product_72_selectbox.setItemData.%フィールド識別ID%(Object arg) | 送信値に一致する行が選択される。 |
リストボックス(v72) | formaItems.product_72_listbox.setItemData.%フィールド識別ID%(Object arg) | 複数要素指定はカンマ区切りの文字列。送信値に一致する行が選択される。 |
ユーザ選択 | formaItems.product_72_userSelect.setItemData.%フィールド識別ID%(Object arg) | |
組織選択 | formaItems.product_72_departmentSelect.setItemData.%フィールド識別ID%(Object arg) | |
組織・役職選択 | formaItems.product_72_departmentPostSelect.setItemData.%フィールド識別ID%(Object arg) |
引数のargは、下記の構造のオブジェクトで指定してください。
入力値は、アイテムのデータ型に合わせてセットしてください。文字列型(String)・数値型(Number型)・日付型(Date型)・タイムスタンプ型(Date型)。
1 2 3 |
var args = {}; args.data = {}; args.data.%フィールド識別ID% = "入力値"; |
テーブルアイテムに値を反映するAPI
アイテム名 | API |
---|---|
グリッドテーブル | formaItems.product_80_gridtable.setItemData.%テーブル識別ID%(Object args, Object option); |
全行更新の場合、引数のarg, optionは、それぞれ下記の構造のオブジェクトで指定してください。
1 2 3 4 5 6 7 8 9 |
var args = {}; args.data = {}; args.data.%テーブル識別ID% = []; for(var i=0; i<valueArray.length; i++){ args.data.%テーブル識別ID%[i] = {}; args.data.%テーブル識別ID%[i].%フィールド識別ID% = valueArray[i]; } var option = {}; |
特定行更新の場合、引数のarg, optionは、それぞれ下記の構造のオブジェクトで指定してください。
グリッドテーブルの場合は、削除された行数についても行数の計算にて考慮する必要があります。
1 2 3 4 5 6 7 |
var args = {}; args.data = {}; args.data.%テーブル識別ID% = []; args.data.%テーブル識別ID%[0] = {}; args.data.%テーブル識別ID%[0].%フィールド識別ID% = "入力値"; var option = {}; option.indexKey = 行数; |
(サンプルコード) 入力アイテムに値を反映する
文字列 入力値の反映
1 2 3 4 5 6 |
(function($){ var args = {}; args.data = {}; args.data.%フィールド識別ID% = "入力値"; formaItems.product_72_textbox.setItemData.%フィールド識別ID%(args); })(jQuery); |
日付 入力値の反映
1 2 3 4 5 6 |
(function($){ var args = {}; args.data = {}; args.data.%フィールド識別ID% = new Date(); formaItems.product_72_calendar.setItemData.%フィールド識別ID%(args); })(jQuery); |
リストボックス(v72) 入力値の反映
1 2 3 4 5 6 |
(function($){ var args = {}; args.data = {}; args.data.%フィールド識別ID% = "入力値1,入力値2,入力値3"; formaItems.product_72_listbox.setItemData.%フィールド識別ID%(args); })(jQuery); |
グリッドテーブル 全行反映
1 2 3 4 5 6 7 8 9 10 11 |
(function($){ var args = {}; args.data = {}; args.data.%テーブル識別ID% = []; for(var i=0; i<valueArray.length; i++){ args.data.%テーブル識別ID%.%フィールド識別ID% = valueArray[i]; } var option = {}; formaItems.product_80_gridtable.setItemData.%テーブル識別ID%(args, option); })(jQuery); |
グリッドテーブル 特定行反映
1 2 3 4 5 6 7 8 9 10 |
(function($){ var args = {}; args.data = {}; args.data.%テーブル識別ID% = []; args.data.%テーブル識別ID%[0] = {}; args.data.%テーブル識別ID%[0].%フィールド識別ID% = "入力値"; var option = {}; option.indexKey = 行数; formaItems.product_80_gridtable.setItemData.%テーブル識別ID%(args, option); })(jQuery); |
入力アイテムの入力不可・入力可状態を切り替えるためのAPI
入力アイテムの入力不可・入力可状態を切り替えるためのAPI
アイテム名 | API |
---|---|
文字列 | formaItems.product_72_textbox.changeInputMode(Object controlSetting) |
複数行文字列 | formaItems.product_72_textarea.changeInputMode(Object controlSetting) |
数値 | formaItems.product_72_number.changeInputMode(Object controlSetting) |
日付 | formaItems.product_72_calendar.changeInputMode(Object controlSetting) |
期間 | formaItems.product_72_terms.changeInputMode(Object controlSetting) |
一覧選択(v72) | formaItems.product_72_itemSelect.changeInputMode(Object controlSetting) |
チェックボックス(v72) | formaItems.product_72_checkbox.changeInputMode(Object controlSetting) |
セレクトボックス(v72) | formaItems.product_72_selectbox.changeInputMode(Object controlSetting) |
リストボックス(v72) | formaItems.product_72_listbox.changeInputMode(Object controlSetting) |
ユーザ選択 | formaItems.product_72_userSelect.changeInputMode(Object controlSetting) |
組織選択 | formaItems.product_72_departmentSelect.changeInputMode(Object controlSetting) |
組織・役職選択 | formaItems.product_72_departmentPostSelect.changeInputMode(Object controlSetting) |
所属組織選択 | formaItems.product_72_affiliationSelect.changeInputMode(Object controlSetting) |
引数のcontrolSettingは、下記の構造のオブジェクトで指定してください。
1 2 3 |
var controlSetting = {}; controlSetting.mode = "valid"; // 入力可 valid・入力不可 invalid controlSetting.inputId = "%フィールド識別ID%"; |
テーブルアイテムの入力不可・入力可状態を切り替えるためのAPI
アイテム名 | API |
---|---|
グリッドテーブル | formaItems.product_80_gridtable.changeInputMode(Object controlSetting) |
テーブル全体の状態を切り替える場合、引数のcontrolSettingは、下記の構造のオブジェクトで指定してください。
1 2 3 4 |
var controlSetting = {}; controlSetting.tableId = "%テーブル識別ID%"; controlSetting.inputId = ""; // 空文字を指定 controlSetting.mode = "valid"; // 入力可 valid・入力不可 invalid |
特定列の状態を切り替える場合、引数のcontrolSettingは、下記の構造のオブジェクトで指定してください。
列タイプについては、文字列(textbox), 数値(number),日付(calendar),一覧選択(itemSelect),セレクトボックス(selectbox)を指定してください。
1 2 3 4 5 |
var controlSetting = {}; controlSetting.tableId = "%テーブル識別ID%"; controlSetting.inputId = "%フィールド識別ID%"; controlSetting.inputItemType = "textbox"; // 列タイプを指定 controlSetting.mode = "valid"; // 入力可 valid・入力不可 invalid |
(サンプルコード) 入力アイテムの入力不可・入力可状態を切り替える
文字列 入力不可
1 2 3 4 5 6 |
(function($){ var controlSetting = {}; controlSetting.mode = "invalid"; controlSetting.inputId = "%フィールド識別ID%"; formaItems.product_72_textbox.changeInputMode(controlSetting); })(jQuery); |
グリッドテーブル テーブル全体入力不可
1 2 3 4 5 6 7 |
(function($){ var controlSetting = {}; controlSetting.tableId = "%テーブル識別ID%"; controlSetting.mode = "invalid"; controlSetting.inputId = ''; window.formaItems.product_80_gridtable.changeInputMode(controlSetting); })(jQuery); |
グリッドテーブル 特定列の入力不可
1 2 3 4 5 6 |
var controlSetting = {}; controlSetting.tableId = "%テーブル識別ID%"; controlSetting.mode = "invalid"; controlSetting.inputId = "%フィールド識別ID%"; controlSetting.inputItemType = "textbox"; formaItems.product_80_gridtable.changeInputMode(controlSetting); |
3. スクリプトの実行順を制御したい
各画面アイテム自身のスクリプト処理とアドオンで追加したスクリプトの間で実行順を制御するのが、難しいという声をいくつかいただきました。
実行順はスクリプトの読み込み、すなわちアイテムの読み込み順にて定義されます。アイテムの読み込み順は、デザイナーのコンテキストメニューにて「前面へ移動」「背面へ移動」を行うことで変更できます。
アイテムの固有処理後に任意のスクリプトを実行
テンプレートHTML上で、form_areaのdivタグより後方にてscriptタグを配置していただき、スクリプトファイル内に$(document).ready(function(){});を定義し、その関数の内部に処理を記述していただくことで、スクリプトの内容が各アイテム固有の処理後に実行されます。
4. よく利用するアクションはノンプログラミングで実現したい
複雑なプログラム処理でなく、業務で利用頻度の高いアクションについては設定だけで実現したいという声をいくつかいただきました。
アクション設定機能を利用すると、アイテムの表示・非表示,入力可・入力不可の制御,クエリの実行については設定だけで実現できます。
詳しくは、
「IM-BIS for Accel Platform / 業務管理者 操作ガイド 7.10.アクションを設定する」
をご参照ください。
【注意事項】
アクション設定機能は、IM-BISがインストールされた環境でのみ利用可能です。
また、IM-BIS 2014 Winter Patch01以降で利用できる機能となります。
大変分かりやすくまとめていただき、ありがとうございます。
IM-FormaDesignerでアンケートのような入力フォームを作成するとします。
複数のロールが付与されているユーザがいる中で、ロールごとに、IM-FormaDesignerのある入力フォームの項目の表示/非表示を変える場合の設定方法はどのように行えばよろしいでしょうか。
データマッパーでは、複数のロールを取得することができないため、concatでロールをつなげて取得し、表示非表示の条件式に部分一致の式を書くことで、実現しようとしました。
concatし、”admin,messenger_manager”となったロールの値を持つユーザが、adminしか見れない入力フォームを見られる状態にするために、部分一致の条件式で、role=”admin“と書いても、うまくいきませんでした。
複数のロールを持つユーザがいる場合の、入力フォームの表示非表示の設定の仕方についてご教示いただけますと幸いに存じます。
すみません。最後から二文目の文字がイタリックになっている下記ですが、アスタリスクを書きたかったです。
訂正前
role=”admin“
訂正後
role=”アスタリスクadminアスタリスク“
モジュールプロジェクト上のpublicディレクトリからの相対パスを指定してください。
例えば、静的ファイルがモジュールプロジェクト上のパス %PROJECT_ROOT%/src/main/public/forma_script_tutorial/csjs/sample.jsに配置されている場合は、template.htmlのscriptタグのsrc属性には、forma_script_tutorial/csjs/sample.js を指定してください。
Formaでのスクリプト開発生産性向上を読み、template.htmlと簡単なjsの作成をしました。本書ではeBuilderを使用し、その後サーバーへデプロイを行っていますが、作成したファイルを手動で置く、ということは可能でしょうか。また、ファイルを置くとすればどの位置が適切なのかを教えていただけないでしょうか。
【template.html】
開発環境へのデプロイに関しては、手動で実施していただいても問題ありません。本番環境へのデプロイに関しては、ファイル操作機能(Storage)をご利用ください。
http://www.intra-mart.jp/download/product/iap/operation/system_administrator_guide/texts/apply_guide/apply_guide_4.html
本記事の記述の通り、配置場所はそれぞれ以下のディレクトリです。
テンプレート
パブリックストレージ/forma/html_template/template.html
アプリケーション固有のテンプレート
パブリックストレージ/forma/html_template/%アプリケーションID%/%アプリケーションID%.html
フォーム固有のテンプレート
パブリックストレージ/forma/html_template/%アプリケーションID%/%フォームID%/%フォームID%.html
また、IM-FormaDesigner 2014 Winterアップデートより、テンプレートHTMLファイルは初回読み込み時にキャッシュされる動作となっておりますので、配置後に再起動もしくは「HTMLテンプレートキャッシュ削除」ジョブを実行していただく必要がございます。
【csjs】
開発環境へのデプロイに関しては、手動で実施していただいても問題ありません。本番環境へのデプロイに関しては、ユーザモジュールを作成し、Jugglingプロジェクトに組み込んだ上で、静的ファイル出力を利用して実施してください。
http://www.intra-mart.jp/document/library/iap/public/setup/iap_setup_guide/texts/create_war/create_static_file.html
csjsファイルを配置するディレクトリに関して指定はありません。Web Server の仮想ディレクトリで設定したディレクトリに静的ファイルとして配置されていれば、問題ありません。
テンプレートHTMLのsrcを指定する際に、何に従ってsrc元を書いているのかがうまく理解できませんでした。
もし書き方があれば教えていただけないでしょうか。