【ServiceNow】UIアクション-Client sideで条件をチェックし、Server sideで処理を実行してみる

# 概要
　ServiceNowには、フォームやリスト上にボタン、リンク、コンテキストメニューの項目といったUI要素を追加する【UIアクション】と呼ばれる機能が存在する。UIアクションでは、スクリプトを記述し、NowPlatform上でさまざまな処理を実行できるが、基本的にレコードに対して更新を行うため、サーバサイドで実行するのがデフォルトになっている。クライアントサイドで実行する場合は、「クライアント」チェックボックスにチェックを入れることで、クライアントサイドで実行できる。  

では、両サイドの実行を行いたい場合はどうすればよいのか？
本記事では、それを行うのにどのような実装が必要か説明する。

## 事例
　例えば、インシデントテーブルのレコードのステータスを"処理中"から"解決済み"に変更するのにUIアクションを用いるとする。しかし、”解決済み”にするには何かしらの解決情報を「コメント」フィールドに入れる必要があるとする。この場合、「コメント」は必須設定にしなければならないが、常時必須にする必要はない。つまりUIアクションを実行したタイミングで「コメント」を検証する必要がある。この検証はクライアントサイドで行う必要があるが、レコードの値の更新はサーバサイドで行う必要がある。
そこで、UIボタンをクリック時に「コメント」フィールドに記入があるかを検証し、無ければエラーメッセージを出し、記入がある場合は、ステータスを遷移させる。

# この記事でできること
 - 同一のUIアクションでクライアント、サーバサイドの両方の処理を実行できる。
 
# 環境、使用する物
 - Version : Orland
 - 使用言語 : 日本語
 - UIアクション

# 解説
## UIアクションについて
 - アクセス方法  
1. UIアクションを作成したいフォームにを開く
2. フォームコンテキストメニューから[構成]>[UIアクション]を選択
3. [新規]ボタンを押下する。
<br><br>


 - 使用するフィールド
 今回は事例同様のボタンをフォーム上に作成する。
 使用するフィールドは主に以下の６つ。  

| フィールド名 | 説明 |  
|------------|------------|
|アクション名|現在作成しているUIアクションをスクリプト等で使用する場合の名称を定義する。|
|クライアント|チェックすることで、クライアントサイドで動作する。|
|フォームボタン|フォーム上に現在作成しているUIアクションが動作するボタンを配置する。|
|クリック時|"クライアント"をチェックすると表示される。ボタン、リンク等のクリック時に動作する関数を定義する。|
|条件|UIアクションが表示される条件を定義する。|
|スクリプト|JavaScriptで動作するスクリプトを記述する。|
<br>

 - 使用するAPI・構文の解説  
 今回使用する重要なAPIと構文は２つである。

```javascript
gsftSubmit(null, g_form.getFormElement(), '<action_name>');
```
gsftSubmit()は、UIアクション内で使用され、クライアントサイドの処理が正常に終了した場合に、サーバサイドの処理を実行するAPI。
第２引数で現在のフォーム要素の全てを取得し、第３引数で取得した要素の中から定義したアクション名と同一なUIアクションをサーバサイドで実行するといったニュアンスである。
というのも、ServiceNow内ではデフォルトで使用されているAPIでありながら、残念ながらこれを説明する公式ドキュメンは存在していないため正確な情報が今のところ存在していない、しかしこのAPIに言及するコミュニティの回答は多く見られる[^1]  [^2]  [^3]。  
 



```javascript
if(typeof window == 'undefined')
   server_Side_function();
```
こちらはサーバサイドの処理を実行する前に、スクリプトがWebブラウザ内のWebページで実行されているかどうかを確認する構文。
window型が未定義ならば処理を実行することで、サーバサイドの処理が正常に行える状態かをチェックしている。
 

# 実践！
それでは、実際にUIアクションを作成する。
今回はインシデントテーブル[incident]を継承したテーブルにUIアクションを作成する。また、「コメント」フィールドは事前に作成してある。

##仕様
 - ボタンの表示条件は、ステータスが"処理中"の時。
 - ボタン押下時にステータスを”処理中”から”解決済み”に遷移する。
 - ステータスを遷移する前に”コメント”フィールドに記入があるか検証する。
 - ”コメント”に記入がない場合、エラーメッセージを表示、ステータスの遷移は行わない。

##実装
スクリプト以外のUIアクションは以下の画像のように設定した。
![作成したUIアクション.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425100/01505094-e5fd-64a5-0b1d-3333184c2116.png)

”条件”フィールドには ```current.state==2```を記述している。
インシデントテーブルのステータス"処理中"の値は**2**であるため、**現在のステータスが処理中のとき**という条件文になる。



スクリプトに関しては以下のように記述

```javascript
// ----- クライアントサイド -----
//フィールドの入力をチェックします。"クリック時"フィールドはこの関数から実行される。
function checkField(){
	//コメントフィールドの列名を格納
    var fieldName = ['comment'];
	//格納したフィールド文ループ
    for(var i=0; i<fieldName.length; i++){
        popMsg(fieldName[i]);
    }
	//UIアクションを呼び出して、クリック時の関数をスキップする。
	gsftSubmit(null,g_form.getFormElement(),'change_state_resolved');
}

//エラーメッセージに関しての関数
function popMsg(field){
    //説明フィールドに何も入力されていない場合
    if(g_form.getValue(field)==''){
    //既に何かしらのメッセージがある時のためにメッセージを非表示にする。
        g_form.hideFieldMsg(field);
    //コメントフィールドを必須にする。
        g_form.setMandatory(field, true);
    //メッセージを表示
        g_form.showFieldMsg(field,'コメントに何か入力してください。','error');
        return false;
    }
}

//----- サーバサイド -----
//以下のif文でスクリプトがブラウザ内で実行されないかチェック。
//windowが"undefined"なら関数を実行。
if (typeof window == 'undefined')
    serverStateToResolved();

function serverStateToResolved(){
	//ステータスを解決済み[6]にする
    current.state = 6;
	//レコードの更新
    current.update();
}
```

## 動作チェック
- ステータス”処理中”のみボタンが表示されるか確認。
![ボタン表示.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425100/9e5a8bf0-2bea-cb3b-c8cd-4d2f1b71cf5e.gif)
<br><br><br><br><br><br>

- ”コメント”を検証し、何も書かれていない場合、エラーメッセージを表示するか確認。
![エラーメッセージっ表示.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425100/a19a0423-8b18-2fdf-addc-398a334aa40c.gif)
<br><br><br><br><br><br>

- ステータスが”解決済み”に遷移するか確認する
![ステータス遷移.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/425100/3b8c8595-2401-0ace-7555-9de6072f7624.gif)



# 結論
UIアクションはクライアント、サーバサイドの処理を実行できるが、今回紹介したAPIを使用することにより、両サイドの処理が実行できるようになった。これにより、アクション実行前にフィールドの必須チェックを行うことができるため、UIアクションの処理の幅を増やすことができると思われる。
今回の記事がお役に立てば幸いである。


# 参考文献
 - [【SNGURU】Client & Server Code in One UI Action](https://servicenowguru.com/system-ui/ui-actions-system-ui/client-server-code-ui-action/)
 - [【stackoverflow】What's the purpose of if (typeof window !== 'undefined')](https://stackoverflow.com/questions/32598971/whats-the-purpose-of-if-typeof-window-undefined)

[^1]: [【コミュニティ】gsftSubmit(null,form element,ui action name);](https://community.servicenow.com/community?id=community_question&sys_id=d2d58b21db1cdbc01dcaf3231f961921)

[^2]: [【コミュニティ】what is the Use of gsftSubmit , why should we use it, when use it?](https://community.servicenow.com/community?id=community_question&sys_id=3c730538db43db44f7fca851ca961939)

[^3]: [【コミュニティ】gsft submit()](https://community.servicenow.com/community?id=community_question&sys_id=cefe7229db58dbc01dcaf3231f961981)