ServiceNow – MRVS(複数行変数セット)の行コピー機能

開発・導入

1申請で複数件の注文をしたい、といった要件をカタログアイテムで叶えるためにMulti-Row Variable Set(複数行変数セット)を作成する機会が多くあります。
※以下、MRVSと記載します。

とても便利な機能ではありますが、MRVS内の変数が多くなればなるほど、ユーザーが入力する時間を奪ってしまうことになります。
特に、1項目だけ値を変えて残りはまるっと同じ値を入力したい場合など、何度も同じ内容を入力するのにストレスを感じてしまうユーザーは少なくありません。

今回は、カタログアイテムの開発者向けにMRVSの行コピー機能の作成の仕方について解説していきます。
※Service PortalやEmpoyee Centerで動作するものを作成します。Now Platformでは動作しませんのでご注意ください。

解説用に画像のようなカタログアイテムを作りました。
MRVSの変数は、各データ型の値をコピーできるかの確認用です。行は非表示に設定しています。
コピーする行番号には、MRVSの何行目をコピーするかをユーザーに入力してもらいます。

周辺機能の作成

行の自動採番機能

ユーザーが何行目をコピーするべきかを視覚的に判断できるように、行が追加されたら自動でMRVS内の変数「行」に数字が割り振られるようにします。
おまけ程度の機能なので、必要ない方は飛ばしてください。

MRVS内のカタログクライアントスクリプトを新規作成します。

  • 名前:適当な名前を付けてください
  • UI タイプ:Service Portalで動作させるためにすべてを選択してください
  • タイプ:onLoadを選択してください
  • 適用:今回はカタログアイテムビューのみに適用します
  • スクリプト:以下のようにコードを記載します
function onLoad() {
    // MRVSを変数に代入
    var mrvs = g_service_catalog.parent.getValue('<MRVSの内部名>');

    // 1行目の場合
    if (!g_form.getValue('<変数「行」の名前>')) {
        if (mrvs.length === 0) {
            g_form.setValue('<変数「行」の名前>', 1);

        // 1行目ではない場合
        // MRVSのデータをJSONに変換し、現在の行+1を変数「行」に入力する
        } else if (g_service_catalog.parent) {
            var max_row = 0;
            var mrvs_list = JSON.parse(mrvs);

            for (var i = 0; i < mrvs_list.length; i++) {
                var current_row = Number(mrvs_list[i].<変数「行」の名前>);

                if (current_row > max_row)
                    max_row = current_row;
            }
            g_form.setValue('<変数「行」の名前>', Number(max_row) + 1);
        }
    }
}

これで行番号が自動で入力されるようになりました。

コピーする行番号のバリデーション

決まったフォーマットで変数「コピーする行番号」に入力するように、新たにバリデーションを作成します。
今回は以下のようなバリデーションを設定します。

  • 数字(例:1 ※1行目をコピーします)
  • カンマ区切りの数字(例:1, 2 ※1行目と2行目をコピーします)
  • ハイフン区切りの数字(例:1 – 3 ※1行目から3行目をコピーします)
Service Catalog > Catalog Variables > Variable Validation Regex

メニュー > Service Catalog > カタログ変数 > 変数の妥当性確認の正規表現 から正規表現を新規作成します。

  • 名前:適当な名前を付けてください
  • 検証メッセージ:フォーマットから外れた値を入力したときに表示されるエラーメッセージです。適切なメッセージを入力してください
  • 正規表現:^\d$|^\d([,]\s?\d)$|^\d\s?[-]\s?\d$
  • 正規表現フラグ:なし

作成したらカタログアイテムに戻ります。
変数「コピーする行番号」 > タイプ仕様タブ > 妥当性確認用の正規表現 に先ほど作成した正規表現を設定します。

これでバリデーションの設定が完了しました。

入力成功パターン
入力失敗パターン

行コピー機能の作成

回り道をしてきましたが、いよいよMRVSの行を扱う部分を作りこんでいきます。

今回の実装方式として、カスタム型変数を使用していきます。
Service Portalで動作させるため、ウィジェットを割り当てる必要があります。
まずはウィジェットから作成していきましょう。

ウィジェット

Service Portal > Widgets

Service Portal > ウィジェット からウィジェットを新規作成します。

  • 名前:適当な名前を付けてください
  • ID:適当なIDを入力してください(小文字英数字推奨)
  • 本文HTMLテンプレート:以下のようにHTML分を記載します
<div>
  <a  class="btn btn-primary" ng-click="c.copyRows()">コピー</a>
</div>
  • CSS:空白で問題ありません
  • サーバースクリプト:デフォルトのままで問題ありません
  • クライアントコントローラー:以下のようにコードを記載します
api.controller = function($scope) {
    var c = this;

    c.copyRows = function() {
        var g_form = $scope.page.g_form;

        if (g_form.getValue('<MRVSの内部名>')) {
       var comma = true;
            var mrvs = JSON.parse(g_form.getValue('<MRVSの内部名>'));

            // 指定行数が1つの場合はカンマ区切りで配列に格納する
            if (g_form.getValue('<変数「コピーする行番号」の名前>').indexOf(',') == -1 && g_form.getValue('<変数「コピーする行番号」の名前>').indexOf('-') == -1)
                var row = g_form.getValue('<変数「コピーする行番号」の名前>').split(',');

            // 指定行数が複数の場合はカンマ区切りで配列に格納する
            if (g_form.getValue('<変数「コピーする行番号」の名前>').indexOf(',') > -1)
                var row = g_form.getValue('<変数「コピーする行番号」の名前>').split(',');

            // 指定行数が範囲の場合はハイフン区切りで配列に格納する
            if (g_form.getValue('<変数「コピーする行番号」の名前>').indexOf('-') > -1) {
                var row = g_form.getValue('<変数「コピーする行番号」の名前>').split('-');
                comma = false;
            }

            // 配列を数値型に変換する
            var rowInt = row.map(Number);
            for (var i = 0; i < rowInt.length; i++)
                rowInt[i] -= 1;


            // 配列がカンマ区切りの場合
            if (comma == true) {
                for (var j = 0; j < mrvs.length; j++) {
                    if (rowInt.indexOf(j) > -1) {
                        mrvs = mrvs.concat(mrvs[j]);
                        g_form.setValue('<MRVSの内部名>', JSON.stringify(mrvs));
                    }
                }
            }

            // 配列がハイフン区切りの場合
            if (comma == false) {
                for (var k = rowInt[0] + 1; k < rowInt[1]; k++) {
                    rowInt.push(k);
                }

                rowInt.sort(function(first, second) {
                    return first - second;
                });

                for (var l = 0; l < mrvs.length; l++) {
                    if (rowInt.indexOf(l) > -1) {
                        mrvs = mrvs.concat(mrvs[l]);
                        g_form.setValue('<MRVSの内部名>', JSON.stringify(mrvs));
                    }
                }
            }

            // MRVSの行番号を整理
            var mrvs2 = JSON.parse(g_form.getValue('<MRVSの内部名>'));
            for (var m = 0; m < mrvs2.length; m++) {
                mrvs2[m].<変数「行」の名前> = String(m + 1);
                g_form.setValue('<MRVSの内部名>', JSON.stringify(mrvs2));
            }
        }
    };
};

ここまで入力したらウィジェットの作成は完了です。

カスタム型変数

カタログアイテムに戻り、カスタム型変数を作成します。

タイプをカスタムに設定し、順序を変数「コピーする行番号」の次に並ぶように設定します。
タイプ仕様 > ウィジェットに先ほど作成したウィジェットを割り当てます。

これでMRVSの行コピー機能が完成しました。

動作確認

コピー元データとして、手動で3行入力します。

コピーする行番号に1と入力してコピーボタンをクリックしてみます。
4行目に、原本:一行目のデータがコピーされました。

コピーする行番号に1, 3と入力してコピーボタンをクリックしてみます。
5行目と6行目に、原本:一行目と原本:三行目のデータがコピーされました。

コピーする行番号に1 – 3と入力してコピーボタンをクリックしてみます。
7行目から9行目に、原本:一行目から原本:三行目のデータがコピーされました。

まとめ

MRVSの行コピー機能を作成する際に最低限必要なものを簡単にまとめると以下のようになります。

  • カタログアイテム
  • MRVS
  • コピー行を指定させる1行テキスト
  • カスタム型変数
  • ウィジェット

コピー行を指定させる1行テキストにバリデーションを設定する場合は、同時にどのフォーマットなら入力可能なのかを注釈に出してあげると親切かもしれませんね。

また何か面白い技術を仕入れてきたらこういった解説記事を書いてみようと思います。

コメント

タイトルとURLをコピーしました