[OutSystems]JavaScript APIで、List中のInputに対するValidation

OutSystemsに標準で備わっているValidationは、そのままではListなどの繰り返し要素に配置したInputでは **一部** うまく動作しない。
そういうときには、Validationの仕組みをカスタマイズできるJavaScript API Validationがある。

# 確認環境
Personal Environment(Version 11.17.0 (Build 36291))
Service Studio(Version 11.53.35)

# サンプル
Forgeコンポーネント: [HousesoftSampleReactive](https://www.outsystems.com/forge/component-overview/7086/housesoftsamplereactive)のV1.0.14。

MainFlow > ValidationJavaScriptAPI Screen

# Built-inのValidation
## 必須／型チェックは自動でやってくれる
Input Widgetには、入力値を自動でチェックし、エラーメッセージを表示する機能が備わっている。

- Mandatory PropertyをYesにしておくと、その項目が未入力のときにエラー表示
- 型に合致しない入力があると、エラー表示

エラーは、Input Widgetの直下に表示される。

## その他のチェックもローコードで設定できるが……
実行時に各InputのValidation結果は、以下のプロパティに設定される。

- Valid プロパティ: FalseならValidationエラーあり
- ValidationMessage プロパティ: エラー内容をユーザーに表示するためのメッセージ

これらのプロパティを実行時に編集することで、組み込みの必須／型以外のValidationも行える。

このあたりについては、以前、[OutSystemsのReactive Web App更新処理のバリデーション](https://qiita.com/jyunji_watanabe/items/e77c3e6d190ef5d94fc5)に書いた。

## 問題点：List中に配置したInputのValidにアクセスできない
ここで、問題点が1つ。
かなり前から、OutSystemsのForum上で繰り返し質問されていることだが、List等の繰り返し要素中にあるInputのValidやValidationMessageプロパティにはローコードロジックからアクセスできない。

そのため、List内のInputにカスタムのValidationを行うには別の手段がいる。

# JavaScript APIによるValidation
## APIの概要
OutSystemsが提供する組み込みの関数に[Validation](https://success.outsystems.com/Documentation/11/Reference/OutSystems_APIs/JavaScript_API/Validation)というものがあり、このAPIを使うとHTMLタグ上のidを指定し、

- Validプロパティを取得（isWidgetValid関数）
- Validationエラーに設定（setWidgetAsInvalid関数）
- Validationエラーを解消（setWidgetAsValid関数）

を行える。

## 実装例：Validプロパティ取得
あるButtonをクリックすると、List中のInput全てのValidプロパティをチェックし、結果をまとめてFeedback表示する場合。

### Input Widgetにdata-の属性を付与しておく
List内の各Inputに対応するDOM要素を実行時に取得するため。

Attributesの下に「data-input-name」という項目を設定した。
こうすると、JavaScriptから、この項目を持つ要素を抽出したり、この項目の値で検索したりできる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/715a1c11-bcb7-7750-33ef-153c7ce5a703.png)

### ロジック
以下の通り、JavaScriptでOutput Parameterに結果を詰め、Feedbackで表示するだけ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/3193e809-48c3-e9fe-997e-0813c78fe19e.png)

Validation APIにアクセスするには、「$public.Validation」オブジェクトを経由する点に注意。
document.querySelectorAllで、↑で付与した「data-input-name」を属性に持つ要素だけを抽出し、ループしてValidプロパティを取り出している。
``` js
const nodes = document.querySelectorAll("[data-input-name]");
let nodesLength = nodes.length;
$parameters.Result = "";
for (let i = 0; i < nodesLength; i++) {
    if ($parameters.Result !== "") $parameters.Result += ", ";
    $parameters.Result += nodes[i].dataset["inputName"] + ": " + 
                    $public.Validation.isWidgetValid(nodes[i].id); 
}
```

### 実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/22b56b38-74c1-f784-5229-7b254742dc9b.png)


## 実装例：Input WidgetのOnChangeでチェック
### InputのOn ChangeイベントハンドラにInput 
ListItem内であれば、Input WidgetのIdプロパティ（HTML上のidが取れる）にアクセスできるので、以下の通り、イベントハンドラに渡しておく。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/0e0bb77a-cb13-9135-28e3-bca119747a2a.png)

### 実装
値のチェックをするため、document.getElementByIdを使ってDOM要素を、次いでその値を取得している。

ここでは、値が負ならエラー（setWidgetAsInvalid関数呼び出し、第2引数荷エラーメッセージを設定する）、0以上なら正常（setWidgetAsValid関数呼び出し）にしている。

``` js
let widget = document.getElementById($parameters.WidgetId);
if (!widget) return;

var value = parseInt(widget.value);
if (value < 0)
    $public.Validation.setWidgetAsInvalid($parameters.WidgetId, "0以上の値を入力してください");
else
    $public.Validation.setWidgetAsValid($parameters.WidgetId);
```

### 実行結果
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/5b30999c-8792-e03e-18aa-29bca9ad1c4e.png)