プリザンターのスクリプトとサーバスクリプト

# 概要

同じ動作をスクリプトとサーバスクリプトでそれぞれ作ってみて違いを確認します。

# サーバスクリプト使ってますか？

2020年11月にリリースしたVer1.1.9から追加されたサーバスクリプト機能は、レコードの操作、通知、アクセス制御など様々な制御を行うことができる機能です。
公開されて約4年経ちますので、多くの皆様がご利用ご活用されていると思います。
サーバスクリプト機能リリース前はスクリプトで苦労してアプリケーションを作成したことを思い出すと懐かしくもあり、作り直したい気持ちもあります。

今回はこの「作り直してみる」に着目して、スクリプトとサーバスクリプトでの実現方法の違いを見ていきたいと思います。

# 事例１．ユーザの入力操作結果によって〇〇したい
## 1. 値を転記
[FAQ：ある項目の値を特定の値に変更したときに別の項目の値を変更する \| Pleasanter](https://pleasanter.org/ja/manual/faq-lookup)
上記FAQに記載してあるスクリプトで以下のような分類Aを「終了」にした場合、数値Aに「10」をセットするサンプルです。
##### スクリプト
```javascript
$(document).on('change', '#Issues_ClassA', function() {
    //分類A項目が「終了」に変更されると数値A項目に10を自動入力する
    if ($p.getControl('ClassA').val() === '終了') {
        $p.set($p.getControl('NumA'), 10);
    }
});
```

jQueryのonメソッドでchangeイベント処理を記述する手法です。これをサーバスクリプトで置き換えると以下のようになります。
##### サーバスクリプト
```JavaScript
if (context.ControlId === 'Issues_ClassA') {
    if (model.ClassA === '終了') {
        context.ResponseSet('NumA', 10);
    }
}
```
このサーバスクリプトを「条件：画面表示の前」で登録します。
また、分類Aに対して「エディタ」－「詳細設定」で「自動ポストバック」にチェックします。

まず「自動ポストバック」について説明します。チェックONにするとその項目の値が変更した際に自動的にポストバック処理が実行し、その結果サーバスクリプトの「画面表示の前」の条件が発動します。この仕組みを使って、「分類Aが変更したら」を実現します。
次にサーバスクリプトの説明です。1行目のif文で使用している```context.ControlId```は、[マニュアル](https://pleasanter.org/ja/manual/server-script-context)を見ると「要求元コントロールのID」と説明があります。条件「画面表示の前」に記載した場合は、画面表示のきっかけとなる自動ポストバック処理のトリガーとなったコントロールのIDを求めることができます。つまりサーバスクリプト1行目のif文```if (context.ControlId === 'Issues_ClassA')```では自動ポストバック処理のトリガーが分類Aであるかどうかを判定しています。
2行目以降は分類Aが"終了"の場合に数値Aに"10"をセットする処理です。
好みの問題にもなりますが、私はサーバスクリプトの方が記述量も少なくかつすっきりした表記なのでわかりやすいと思います。

以上の「自動ポストバックにチェック」と「画面表示の前のサーバスクリプト」の組み合わせは応用範囲も広いので是非試してみてください。以下FAQも自動ポストバックにチェック」と「画面表示の前のサーバスクリプト」の組み合わせで置き換えできます。
[FAQ：項目の値の一部を切り取り、同レコードの別項目に転記したい \| Pleasanter](https://pleasanter.org/ja/manual/faq-slice-posting)
[FAQ：複数の項目の値を結合し1つの項目に自動入力したい \| Pleasanter](https://pleasanter.org/ja/manual/faq-combine-column-strings)

## 2. 入力禁止（読取専用）／必須入力に切り替える
[FAQ：ユーザ入力操作によって別の項目を入力禁止にしたい \| Pleasanter](https://pleasanter.org/ja/manual/faq-input-prohibited)
上記FAQでは分類Aに"1"を入力すると分類Bを読取専用にするサンプルです。
##### スクリプト
```JavaScript
$(document).on('change', '#' + $p.getControl('ClassA')[0].id', function () {
    if ($p.getControl('ClassA').val() === '1') {
        $p.getControl('ClassB').attr('readonly', true);
        $p.getControl('ClassB').css('background', '#f5f5f5');
    }
});
```
こちらをサーバスクリプトに置き換えると以下のようになります。
##### サーバスクリプト
```JavaScript
if (context.ControlId === 'Issues_ClassA') {
    if (model.ClassA === '1') {
        columns.ClassB.ReadOnly = true;
    }
}
```
このサーバスクリプトを「条件：画面表示の前」で登録します。
また、分類Aに対して「エディタ」－「詳細設定」で「自動ポストバック」にチェックします。

サーバスクリプトの[columns](https://pleasanter.org/ja/manual/server-script-columns)を利用することで、読取専用を設定できます。スクリプトではattrメソッドでreadonly属性をtrueにしたり、エディタで読取専用に設定した状態と見た目を同じするために、cssを設定したりとやや無理矢理な実装をしていますが、サーバスクリプトではたった一行で「テーブルの管理」－「エディタ」で読取専用に設定したのとまったく同じ状態にできます。
サーバスクリプトのcolumnsでは読取専用だけでなく、入力必須や非表示も設定出来ます。

と、サーバスクリプトへの置き換えを説明しましたが、実はサーバスクリプトの実装は不要で[状況による制御](https://pleasanter.org/ja/manual/status-control)で実現できます。
上記の場合であれば、
* 状況は「*」
* 全般タブ：分類Bを「読取専用」に設定
* 条件タブ：分類Aに1を設定
とするだけで、上記スクリプト、サーバスクリプトと同じことを実現できます。なお、状況による制御の利用時でも分類Aは「自動ポストバック」にチェックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314422/74f3b94d-5e3a-e94d-fca9-7f456d11c7c0.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314422/cc656cc9-eeb8-e4a0-9346-bade9f9f9787.png)


# 事例２．独自の入力検証
入力必須や正規表現による検証は「エディタ」で設定できますが、例えば「出荷数は在庫数以下であること」や「終了日は開始日よりも後の日付であること」などの複数項目を用いた入力検証を行う場合はスクリプトの出番になります。サンプルは以下のようになります。
```JavaScript
$p.events.after_validate = function() {
    if ($p.getControl('NumA').val() < $p.getControl('NumB').val()) {
        $p.setMessage('#Message', JSON.stringify({
            Css: 'alert-error',
            Text: '数値Aは数値B以下の値を入力してください。'
        }));
        return false;
    }
}
```
[$p.events.after_validate](https://pleasanter.org/ja/manual/script-events-after-validate)メソッドは項目の詳細設定で指定した入力検証後に実行するイベントです。ここで要件に応じたチェックを行い、NGの場合は```return false```とすることで登録・更新を中断することができます。  
こちらをサーバスクリプトで置き換えると以下のようになります。
##### サーバスクリプト
```JavaScript
if (model.NumA < model.NumB) {
    context.Error('数値Aは数値B以下の値を入力してください。');
}
```
このサーバスクリプトを「条件：作成前、更新前」で登録します。

[context.Error](https://pleasanter.org/ja/manual/server-script-context-error)は呼び出した時点でエラーメッセージを表示し、登録・更新処理を中断します。

以上のように、独自の入力検証を実現する場合はサーバスクリプトの方がコード量も少なくかつ可読性が高いコードになります。

# 事例３．フォームにボタンを追加したい
[FAQ：フォームにボタンを追加したい \| Pleasanter](https://pleasanter.org/ja/manual/faq-add-button)
残念ながら、サーバスクリプトではボタンを追加する機能はないので、これはスクリプトで対応するほかありません。こんな感じですね。
##### スクリプト
```JavaScript
$p.ex.myFunc = function() {
    // ボタンクリック時に実行したい処理
}

$p.events.on_editor_load = function () {
  $('#MainCommands').append($('<button id="NewButton" onclick="$p.ex.myFunc()">ボタン</button>').button({ icon: 'ui-icon-gear' }));
}
```
ボタンの追加は[$p.events.on_editor_load](https://pleasanter.org/ja/manual/script-events-on-editor-load)メソッドに記述します。また、ボタンクリック時に実行したい処理をonclickで呼び出し、処理の実体は別途functionで定義します。

ボタン追加はサーバスクリプトでは対応できませんが、ボタンクリック時の処理はサーバスクリプトに置き換えることができますので、その方法を説明します。
まず、ボタン追加処理を次のように修正します。
##### スクリプト
```JavaScript
$p.events.on_editor_load = function () {
  $('#MainCommands').append($('<button id="NewButton" onclick="$p.send($(this));" data-method="post">ボタン</button>').button({ icon: 'ui-icon-gear' }));
}
```
修正箇所は①onclickで呼び出す処理を```$p.send($(this));```に変更②```data-method="post"```を追加の2点です。この修正によって、追加したボタンをクリックした際に**自動ポストバック**されます。
はい、「自動ポストバック」ですね。事例１で説明しました。自動ポストバックと組み合わせるサーバスクリプトは、『条件：「画面表示の前」でcontext.ControlIdで判定』となります。context.ControlIdでの判定は追加したボタンのIDを指定します。以上を踏まえ、ボタンクリック時の処理をサーバスクリプトで置き換えると以下のようになります。
##### サーバスクリプト
```JavaScript
if (context.ControlId === 'NewButton') {
    // ボタンクリック時に実行したい処理
}
```
今までの例でもわかる通り、実行したい処理（ビジネスロジック）を記載する際はスクリプトよりもサーバスクリプトの方がすっきり見やすく記述できます。

なお、ボタンの追加はスクリプトではなく、[プロセス](https://pleasanter.org/ja/manual/process)機能でも実現できます。
「プロセスの詳細設定」の「全般」タブにある「実行種別」で「追加したボタン」、「アクション種別」を「ポストバック」とすることで、上記のボタン追加処理と同等のボタンが追加されます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3314422/4e61503b-fa18-e326-1e1a-0b84917e985b.png)

「プロセス」を利用するメリットは条件やアクセス制御が利用できることです。スクリプトでボタンを追加するスクリプトを見てわかる通り単純なhtml要素の追加であるため、ボタン表示する際に画面項目の値で表示を制御したい、特定ユーザだけに表示したいなどのアクセス制御を行いたい場合はその処理を独自に実装しなければならないのですが、プロセス機能を利用することでプリザンター上の設定だけで簡単に実現できます。
なお、プロセスで追加したボタンはコントロールIDとして「Process_xx」というIDが付与されます。このxxはプロセスのIDに相当します。具体的には以下のようなサーバスクリプトになります。
##### サーバスクリプト
```JavaScript
if (context.ControlId === 'Process_1') {
    // プロセスID：１のボタンがクリックされた時の処理
}
```

さらに、プロセスで表示するボタンはスクリプトで画面の任意の場所に移動することができます。以下はバージョン項目の右側に移動するサンプルです。移動と合わせてボタンの色を緑に変更します。
##### スクリプト
```JavaScript
$p.events.on_editor_load = function () {
    $('#Process_1')
        .appendTo(`#${$p.tableName()}_VerField`)
        .css({
            'background-color': 'darkgreen',
            'border': 'darkgreen',
            'position': 'absolute',
            'margin-left': '325px'
        });
}
```

ここまで説明した通り、
* プリザンターの標準機能でできることは標準機能（テーブルの管理での設定）を活用する
* ボタンや各項目の移動などhtml要素を操作する場合はスクリプトを利用
* ビジネスロジックなど要件に合わせた実処理はサーバスクリプトを利用

のように使い分けると良いと思います。


# まとめ

スクリプトとサーバスクリプトでの実現方法の違いについて３つの事例を紹介しました。サーバスクリプトを活用している皆様はすでにご存じの内容かもしれませんが、これから使ってみようという方にとって参考になれば幸いです。
プリザンターはユーザ皆様のやりたいことが実現できるツールです。サーバスクリプトとスクリプトをうまく使い分けることで開発効率も向上でき、また標準機能も充実してきましたので今までスクリプト、サーバスクリプトでなければ実現できなかったことも設定だけで実現できるようになります。標準機能とサーバスクリプト、スクリプトを組み合わせることで、今までは実現が難しかったアプリケーションも簡単に作れるようになります。
引き続きプリザンターをよろしくお願いします！