intra-mart Accel Platform スクリプト開発モデル プログラミングガイド 第15版 2018-08-01

実装例:登録画面を作る

この項では、スマートフォンでTODOを登録する画面の実装例を紹介します。

前提条件

  • intra-mart Accel Platform をインストールし、初期設定までが完了していること。

  • ベースモジュールにスクリプト開発フレームワーク、およびIM-Mobile Frameworkモジュールを含めて環境を作成してください。
    実行環境は単体テスト用で作成してください。

下準備 テーブル作成

以下手順を行う前に、以下のテーブルを作成してください。

  • mfw_sample

    列名 データ型 主キー NOT NULL 説明
    id VARCHAR(20) レコードのID
    user_cd VARCHAR(20)   登録ユーザID
    user_nm VARCHAR(20)   登録ユーザ名
    limit_date VARCHAR(20)     TODOの期限
    title VARCHAR(100)     TODOのタイトル
    comment VARCHAR(1000)     コメント
    progress NUMBER(3)     進捗度
    complete VARCHAR(1)     完了/未完了
    priority VARCHAR(1)     重要度
    timestmp VARCHAR(20)     タイムスタンプ
※その他のデータベースの場合は環境に合わせて調整してください。

画面を表示できるようにする

ソースの準備と配置

まず、以下2点のファイルを作成します。

  • %CONTEXT_PATH%/WEB-INF/jssp/src/sample/mobile_fw/sp_store.js

    function init(request) {
    }
    
  • %CONTEXT_PATH%/WEB-INF/jssp/src/sample/mobile_fw/sp_store.html

    <imart type="head">
      <title>TODO登録</title>
    </imart>
    <div data-role="page" id="main">
      <imart type="spHeaderWithLink" headerText="TODO登録" />
      <div class="ui-content" role="main">
      </div>
      <imart type="spCommonFooter" dataPosition="fixed" />
    </div>
    
  • spHeaderWithLink - ヘッダ部左端に、任意のページに遷移のボタンを備えたヘッダを表示します。

  • spCommonFooter - フッタ部にHOMEボタンとログアウトボタンを表示します。

jqueyMobile1.4.5を読み込むようにする

ライブラリ群の設定を行います。本サンプルでは /sample/sp 以下すべてに jQueryMobile1.4.5 が読み込まれるように設定します。

  • %CONTEXT_PATH%/WEB-INF/conf/theme-full-theme-path-config/im_mobile_sample.xml

    <theme-full-theme-path-config xmlns="http://www.intra-mart.jp/theme/theme-full-theme-path-config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.intra-mart.jp/theme/theme-full-theme-path-config theme-full-theme-path-config.xsd">
      <path client-type="sp" libraries-version="iap-8.0.11">/sample/sp/.*</path>
    </theme-full-theme-path-config>
    

メニューから遷移できるようにする

次にルーティングの設定を行います。

  • %CONTEXT_PATH%/WEB-INF/conf/routing-jssp-config/im_mobile_sample.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <routing-jssp-config xmlns="http://www.intra-mart.jp/router/routing-jssp-config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.intra-mart.jp/router/routing-jssp-config routing-jssp-config.xsd ">
      <authz-default mapper="welcome-all" />
      <file-mapping path="/sample/sp/store" client-type="sp" page="/sample/mobile_fw/sp_store" />
    </routing-jssp-config>
    

コラム

  • ルーティングの詳細については ルーティング を参照してください。
  • file-mapping要素にclient-type=”sp”属性を付加するとクライアントタイプがスマートフォンの場合のみ使用可能なルーティングを設定できます。

メニューの作成

再起動してメニューの設定を行います。

  • PCブラウザからテナント管理者でログインし、「メニュー設定」画面を表示します。

  • グローバルナビ(スマートフォン用)を選択し、新規メニューグループ「テストTODO」を作成します。

    ../../../../../_images/menugroup.PNG

新規メニューアイテムを作成します。

  • メニューアイテム名を「TODO登録」とし、URLを”sample/sp/store”とします。

    ../../../../../_images/menuitem1.PNG

認可の設定

認可を設定します。

  • 「権限設定」ボタンをクリックし権限設定(グローバルナビ(スマートフォン用))を表示します。

  • 「権限設定を開始する」ボタンをクリックします。

  • テストTODOの権限の「参照」権限を認証済みユーザに付与します。

    ../../../../../_images/auth.PNG

    コラム

    • 認可の詳細については 認可 を参照してください。

画面を表示する

作成したプレゼンテーションページをメニューから表示します。

  • クライアントタイプをスマートフォン版へ切り替え、スマートフォン版グローバルナビを表示します。

  • メニューから「テストTODO」を選択すると、先ほど作成されたメニュー「TODO登録」が表示されます。

    スマホ版グローバルナビを表示
  • TODO登録を選択します。目的の画面を表示することができました。

    ../../../../../_images/blank1.PNG

コラム

この項目では、下記のポイントを確認しました。

  • ファイルマッピングにclient-type=”sp”属性を付加することでスマートフォン用のルーティングが設定できる
  • スマートフォン用グローバルナビにメニューを表示するには、メニューカテゴリ「グローバルナビ(スマートフォン用)」に設定する

画面に要素を配置する

<div class=”ui-content” role=”main”>内に要素を配置します。
例としてテキストボックスとラベルを配置してみます。
ラベルを配置するには<imart type=”spFieldContain”>タグを使用します。
  • ファンクションコンテナ(HTML)

    <imart type="spFieldContain" label="TODO名" required="true">
      <imart type="input" style="text" name="title" value=title />
    </imart>
    
  • マークアップ結果。テーマにより最適化されたテキストボックスが表示されました。

    ../../../../../_images/text.PNG

同様に他の要素も配置していきます。

  • spDatePicker–日付文字列を参照入力するためのインタフェースを提供します。

    <imart type="spFieldContain" label="期限" required="true">
      <imart type="spDatePicker" name="limit_date" value=limit_date />
    </imart>
    
  • textarea–テキストエリアを提供します(PC版共通)。

    <imart type="spFieldContain" label="コメント">
      <imart type="textarea" name="comment" value=comment />
    </imart>
    
  • spControlGroup–フォーム要素をグループ化します。

  • spRadioButton–jQuery mobileで最適化されたラジオボタンを提供します。

    <imart type="spControlGroup" label="重要度">
      <imart type="spRadioButton" name="priority" id="radio1" value="0" label="低"></imart>
      <imart type="spRadioButton" name="priority" id="radio2" value="1" label="中"></imart>
      <imart type="spRadioButton" name="priority" id="radio3" value="2" label="高"></imart>
    </imart>
    
  • spSlider–スライダーを提供します。

    <imart type="spFieldContain" label="進捗">
      <imart type="spSlider" name="progress" value=progress min="0" max="100" />
    </imart>
    
  • spToggleSwitch–トグルスイッチを提供します。

    <imart type="spFieldContain" label="完了">
      <imart type="spToggleSwitch" name="complete" selected=complete onLabel="完了" offLabel="未完了" onValue="1" offValue="0" />
    </imart>
    
  • マークアップ結果。各要素が表示されました。

    画面要素配置結果

その他使用可能な要素についてはAPIリストを参照してください。

コラム

この項目では、下記のポイントを確認しました。

  • フォーム要素は<div class=”ui-content” role=”main”>内に配置する
  • ラベルを与える場合は<imart type=”spFieldContain”>タグを使用する

登録処理を実装する

登録処理用のファンクションコンテナを新規作成します。

  • %CONTEXT_PATH%/WEB-INF/jssp/src/sample/mobile_fw/sp_store_do.js

    function init(request) {
       //ユーザプロファイル情報を取得します。
       var userProfile    = Contexts.getUserContext().userProfile;
    
       //登録データを作成します。
       var insertObject = {
          id:Identifier.get(),                //レコードのユニークID
          user_cd:userProfile.userCd,         //登録ユーザCD
          user_nm:userProfile.userName,       //登録ユーザ名
          title:request.title,                //TODOのタイトル
          limit_date:request.limit_date,      //TODOの期日
          comment:request.comment,            //コメント
          progress:parseInt(request.progress),//進捗度
          complete:request.complete,          //完了or未完了
          priority:request.priority,          //重要度
          timestmp:DateTimeFormatter.format("yyyy/MM/dd HH:mm", new Date()) //タイムスタンプ(yyyy/MM/dd HH:mm)
       };
    
       //トランザクションを開始します。
       Transaction.begin(function() {
          // データの保存を行います
          var result = new TenantDatabase().insert("mfw_sample", insertObject);
          if(result.error){
             //エラー時、ロールバックしエラー画面へ遷移します。
             Transaction.rollback();
             Transfer.toErrorPage({
               title: 'エラー',
               message: 'データ登録時にエラーが発生しました。',
               detail: result.errorMessage
             });
          }
       });
    
       //画面を再表示します。
       forward("sample/mobile_fw/sp_store", request);
     }
    

    コラム

    • データベースを使用したプログラミングの詳細については データベース を参照してください。
  • im_mobile_sample.xmlに登録処理用のルーティングを追加します。

    <file-mapping path="/sample/sp/store/insert" client-type="sp" page="/sample/mobile_fw/sp_store_do" />
    
  • 画面にFormタグと「登録」ボタンを配置します。 formタグのaction属性は先ほど設定したルーティングのパスと同様にします。

    <div class="ui-content" role="main">
      <form id="storeForm" name="storeForm" method="POST" action="sample/sp/store/insert" data-ajax="false">
        <imart type="spFieldContain" label="TODO名" required="true">
          <imart type="input" style="text" name="title" value=title />
        </imart>
        ...
        <imart type="spFieldContain" label="完了">
          <imart type="spToggleSwitch" name="complete" selected=complete onLabel="完了" offLabel="未完了" onValue="1" offValue="0" />
        </imart>
        <a class="ui-btn ui-btn-b ui-corner-all" id="storeButton">登録</a>
      </form>
    </div>
    

    コラム

    • リンクにui-btnクラスを指定するとリンクをボタン表示します。
    • テーマを指定するにはクラス属性にui-btn-bのように「ui-btn-」の後にテーマを指定します。
    • ボタンを角丸にするにはui-corner-allクラスを指定します。
    • 「data-role=”button”」を指定してもボタンになりますが、jQuery Mobile1.4以降では非推奨となっています。
    • リンクまたはFORM要素にdata-ajax=”false”属性を付加することで明示的にAjax画面遷移をキャンセルすることができます。
  • 最後に「登録」ボタン押下時のイベントを実装します。
    このとき、記述箇所は<div data-role=”page”>内に実装することに
    気を付けてください。
    <div data-role="page" id="main">
    <script>
      (function($){
        $('#main').on("pagecreate", function() {
          $("#storeButton").tap(function() {
            $("#storeForm").submit();
          });
        });
      })(jQuery);
    </script>
    

    注意

    jQuery Mobileでは、Ajaxを使って画面遷移をする場合
    遷移先画面の<div data-role=”page”>要素のみ取得し、表示中画面に挿入します。
    そのため、<HEAD>タグ内にスクリプト、およびスタイルシートを宣言すると画面遷移時に読み込まれないため、不正動作をする場合があります。
  • サーバを再起動して画面を再表示します。 「登録」ボタン押下時、登録処理を経て画面が再表示されます。

コラム

この項目では、下記のポイントを確認しました。

  • フォームを送信するにはformタグのaction属性にルーティングのパスを与える

入力チェック処理を実装する(サーバサイド)

例としてタイトルと日付の入力チェックを実装します。

コラム

以下のメッセージがある前提として説明します。
CAP.Z.IWP.MFW.SAMPLE.TITLE=タイトル
CAP.Z.IWP.MFW.SAMPLE.LIMIT_DATE=期限

メッセージプロパティは各環境に合わせて設定してください。
メッセージプロパティの詳細については 多言語 を参照してください。

バリデーションルールを定義するため、以下のファイルを作成します。

  • %CONTEXT_PATH%/WEB-INF/jssp/src/sample/mobile_fw/validator.js
var validateRule = {
   "title": {
    caption:"CAP.Z.IWP.MFW.SAMPLE.TITLE",
    required:true,
    maxlength:20
   },
   "limit_date": {
    caption:"CAP.Z.IWP.MFW.SAMPLE.LIMIT_DATE",
    required:true,
    date:true
   }
};
  • sp_store_do.jsのinit関数にアノテーションを追加します。
    @validate には利用するバリデーションルールのパスを``@onerror`` には入力チェックエラー時に実行する関数名を指定します。
/**
 * @validate sample/mobile_fw/validator#validateRule
 * @onerror handleErrors
 */
function init(request) {
  • 入力チェックエラー時に実行する関数「handleErrors」をsp_store_do.jsに追加します。
function handleErrors(request, validationErrors) {
  Transfer.toErrorPage({
       title: 'エラー',
       message: '入力チェックエラーが発生しました。',
       detail: validationErrors.getMessages()
   });
}
  • マークアップ結果。タイトルと日付未入力の状態で「登録」ボタンをクリックすると、
    登録処理が呼出されず入力チェックエラー処理が呼出され、エラー画面へ遷移します。
../../../../../_images/validate-error-ssjs.PNG

コラム

JSSP Validatorの詳細については JSSP Validator を参照してください。

コラム

この項目では、下記のポイントを確認しました。

  • 入力チェックのルールはバリデーションルールで定義する
  • 入力チェックを実装するにはチェック対象の関数にアノテーションを定義する

入力チェック処理を実装する(クライアントサイド)

sp_store.htmlにimuiValidationRuleタグを追加します。
rule属性に 入力チェック処理を実装する(サーバサイド) で作成したvalidate.jsのパスを、
rulesName属性とmessagesName属性にそれぞれクライアントJS内で使用する
オブジェクト名を定義します。
  • プレゼンテーションページ(HTML)

     <div data-role="page" id="main">
       <imart type="imuiValidationRule" rule="sample/mobile_fw/validator#validateRule" rulesName="rules" messagesName="messages" />
       <script>
         (function($){
         ...
    

「登録」ボタン押下時のスクリプト処理を修正します。

  • プレゼンテーションページ(HTML)

     <script>
       (function($){
         $('#main').on("pagecreate", function() {
           //登録ボタン押下時のイベント
           $("#storeButton").tap(function() {
             //入力チェック実行
             if (imspValidate('#storeForm', rules, messages)) {
               //正常時
               imspAlert('入力エラーはありませんでした');
               $("#storeForm").submit();
             } else {
               imspAlert('入力エラーが発生しました', 'エラー');
             }
             return false;
           });
         });
       })(jQuery);
     </script>
    
  • マークアップ結果。タイトルと日付未入力の状態で「登録」ボタンをクリックすると、エラーダイアログが表示され警告が出力されます。
    入力チェックエラー

    コラム

    エラー仕様の詳細は、 エラー処理 を参照してください。

コラム

この項目では、下記のポイントを確認しました。

  • クライアントサイドで入力チェックを実装するには <imart type=”imuiValidationRule”>タグを定義する
  • 入力チェックを実行するにはimspValidate関数を使う

非同期で登録処理を実行する

sp_store_do.jsのinit関数の処理を一部修正します。

  • ファンクションコンテナ(サーバサイドJavaScript)

      var resultObject;
    
      Transaction.begin(function() {
          // データの保存を行います
          var result = new TenantDatabase().insert("mfw_sample", insertObject);
          if(result.error){
              Transaction.rollback();
              //Transfer.toErrorPage({
              //   title: 'エラー',
              //   message: 'データ登録時にエラーが発生しました。',
              //   detail: result.errorMessage
              // });
    
              resultObject = {
                 error:true,
                 errorMessage:"データ登録時にエラーが発生しました。",
                 detailMessages:["管理者にお問い合わせください。"]
              };
          } else {
              resultObject = {
                  error:false,
                  errorMessage:"",
                  successMessage:"登録が完了しました。"
              };
          }
      });
    
      var response = Web.getHTTPResponse();
      response.setContentType('application/json; charset=utf-8');
      response.sendMessageBodyString(ImJson.toJSONString(resultObject));
    
      //画面を再表示します。
      //forward("sample/mobile_fw/sp_store", request);
    

    コラム

    • 非同期時はresponse.sendMessageBodyString関数で返却データを画面に返します。
    • 返却するデータはImJson.toJSONString関数でJSON文字列化する必要があります。

「登録」ボタン押下時のスクリプト処理を修正します。

  • プレゼンテーションページ(HTML)

    <script>
      (function($){
        $('#main').bind("pagecreate create", function() {
          // Formの2度押し防止
          $('#storeForm').imspDisableOnSubmit();
          $("#storeButton").tap(function() {
            if (imspValidate('#storeForm', rules, messages)) {
               //Ajaxでのデータ送信
               imspAjaxSend('#storeForm', 'POST', 'json');
               //バリデーションのリセット
               imspResetForm('#storeForm');
            } else {
               imspAlert('入力エラーが発生しました', 'エラー');
            }
            return false;
          });
        });
      })(jQuery);
    </script>
    
  • マークアップ結果。「登録」ボタン押下後にダイアログが表示され、
    登録処理が正常終了したことが確認できるようになりました。
    ../../../../../_images/ajax-success.PNG

コラム

この項目では、下記のポイントを確認しました。

  • クライアントから非同期でリクエストを送信するにはimspAjaxSend関数を使う
  • サーバから非同期で返信するにはresponse.sendMessageBodyString関数を使う

最終結果

  • %CONTEXT_PATH%/WEB-INF/jssp/src/sample/mobile_fw/sp_store.js

    function init(request) {
    }
    
  • %CONTEXT_PATH%/WEB-INF/jssp/src/sample/mobile_fw/sp_store.html

    <imart type="head">
      <title>TODO登録</title>
    </imart>
    <div data-role="page" id="main">
      <imart type="imuiValidationRule" rule="sample/mobile_fw/validator#validateRule" rulesName="rules" messagesName="messages" />
      <script>
        (function($){
          $('#main').on("pagecreate", function() {
            // Formの2度押し防止
            $('#storeForm').imspDisableOnSubmit();
            //登録ボタン押下時のイベント
            $("#storeButton").tap(function() {
              //入力チェック実行
              if (imspValidate('#storeForm', rules, messages)) {
                //Ajaxでのデータ送信
                imspAjaxSend('#storeForm', 'POST', 'json');
                //バリデーションのリセット
                imspResetForm('#storeForm');
              } else {
                imspAlert('入力エラーが発生しました', 'エラー');
              }
            });
          });
        })(jQuery);
      </script>
      <imart type="spHeaderWithLink" path="home" headerText="TODO登録" />
      <div class="ui-content" role="main">
        <form id="storeForm" name="storeForm" method="POST" action="sample/sp/store/insert" data-ajax="false">
          <imart type="spFieldContain" label="TODO名" required="true">
            <imart type="input" style="text" name="title" value=title />
          </imart>
    
          <imart type="spFieldContain" label="期限" required="true">
            <imart type="spDatePicker" name="limit_date" value=limit_date />
          </imart>
    
          <imart type="spFieldContain" label="コメント">
            <imart type="textarea" name="comment" value=comment />
          </imart>
    
          <imart type="spControlGroup" label="重要度">
            <imart type="spRadioButton" name="priority" id="radio1" value="0" label="低"></imart>
            <imart type="spRadioButton" name="priority" id="radio2" value="1" label="中"></imart>
            <imart type="spRadioButton" name="priority" id="radio3" value="2" label="高"></imart>
          </imart>
    
          <imart type="spFieldContain" label="進捗">
            <imart type="spSlider" name="progress" value=progress min="0" max="100" />
          </imart>
    
          <imart type="spFieldContain" label="完了">
            <imart type="spToggleSwitch" name="complete" selected=complete onLabel="完了" offLabel="未完了" onValue="1" offValue="0" />
          </imart>
          <a class="ui-btn ui-btn-b ui-corner-all" id="storeButton">登録</a>
        </form>
      </div>
      <imart type="spCommonFooter" dataPosition="fixed"/>
    </div>
    
  • %CONTEXT_PATH%/WEB-INF/jssp/src/sample/mobile_fw/sp_store_do.js

    /**
     * @validate sample/mobile_fw/validator#validateRule
     * @onerror handleErrors
     */
    function init(request) {
        //ユーザプロファイル情報を取得します。
        var userProfile    = Contexts.getUserContext().userProfile;
    
        //登録データを作成します。
        var insertObject = {
            id:Identifier.get(),                //レコードのユニークID
            user_cd:userProfile.userCd,         //登録ユーザCD
            user_nm:userProfile.userName,       //登録ユーザ名
            title:request.title,                //TODOのタイトル
            limit_date:request.limit_date,      //TODOの期日
            comment:request.comment,            //コメント
            progress:parseInt(request.progress),//進捗度
            complete:request.complete,          //完了or未完了
            priority:request.priority,          //重要度
            timestmp:DateTimeFormatter.format("yyyy/MM/dd HH:mm", new Date()) //タイムスタンプ(yyyy/MM/dd HH:mm)
        };
    
        var resultObject;
    
        Transaction.begin(function() {
            // データの保存を行います
            var result = new TenantDatabase().insert("mfw_sample", insertObject);
            if(result.error){
                Transaction.rollback();
                resultObject = {
                    error:true,
                    errorMessage:"データ登録時にエラーが発生しました。",
                    detailMessages:["管理者にお問い合わせください。"]
                };
            } else {
                resultObject = {
                    error:false,
                    errorMessage:"",
                    successMessage:"登録が完了しました。"
                };
            }
        });
        var response = Web.getHTTPResponse();
        response.setContentType('application/json; charset=utf-8');
        response.sendMessageBodyString(ImJson.toJSONString(resultObject));
    }
    
    function handleErrors(request, validationErrors) {
      Transfer.toErrorPage({
           title: 'エラー',
           message: '入力チェックエラーが発生しました。',
           detail: validationErrors.getMessages()
       });
    }
    
  • %CONTEXT_PATH%/WEB-INF/jssp/src/sample/mobile_fw/validator.js

    var validateRule = {
        "title": {
            caption:"CAP.Z.IWP.MFW.SAMPLE.TITLE",
            required:true,
            maxlength:20
        },
        "limit_date": {
            caption:"CAP.Z.IWP.MFW.SAMPLE.LIMIT_DATE",
            required:true,
            date:true
        }
    };
    
  • %CONTEXT_PATH%/WEB-INF/conf/routing-jssp-config/im_mobile_sample.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <routing-jssp-config xmlns="http://www.intra-mart.jp/router/routing-jssp-config" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.intra-mart.jp/router/routing-jssp-config routing-jssp-config.xsd ">
      <authz-default mapper="welcome-all" />
      <file-mapping path="/sample/sp/store" client-type="sp" page="/sample/mobile_fw/sp_store" />
      <file-mapping path="/sample/sp/store/insert" client-type="sp" page="/sample/mobile_fw/sp_store_do" />
    </routing-jssp-config>
    
  • %CONTEXT_PATH%/WEB-INF/conf/message/im_mobile_sample.properties

    native2ascii コマンドを利用して、プロパティファイルを変換した結果を示します。

    # CAP.Z.IWP.MFW.SAMPLE.TITLE=タイトル
    CAP.Z.IWP.MFW.SAMPLE.TITLE=\u30bf\u30a4\u30c8\u30eb
    # CAP.Z.IWP.MFW.SAMPLE.LIMIT_DATE=期限
    CAP.Z.IWP.MFW.SAMPLE.LIMIT_DATE=\u671f\u9650