[OutSystems]JavaScriptのValidation APIがClient Actionsから使えない問題のまとめ

標準のValidationをJavaScriptで操作するAPIについて、以前以下の記事でまとめた。
[JavaScript APIで、List中のInputに対するValidation](https://qiita.com/jyunji_watanabe/items/d7eaf7927ececc1f64c4)
一般的な使い方については、上記の記事を参照して欲しい。

このAPIが（ドキュメントには記述が見つからないが）Screen Actionの中では機能しないこと、及びその回避策をメモしておく。

# ドキュメント
[JavaScriptAPI - Validation](https://success.outsystems.com/documentation/11/reference/outsystems_apis/javascript_api/validation/)
Validationに含まれるAPIが各WidgetのValidationを設定する（エラーメッセージを設定したり、クリアする）機能を提供することが書かれている。

# Screen ActionからはValidation APIを呼べる
## UI定義
以下のようにInputを画面に配置し、Nameプロパティを「Input_Name」とした。
（Nameプロパティは、後で対象Widgetを特定するのに使う）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/ebf7c4ba-a465-a4ea-356c-14d24f33f2b0.png)

## Screen Action定義
Screen ActionのAction FlowにJavaScript要素を配置し、以下のJavaScriptを記述する。

``` js
var widget = document.querySelector('[id$=Input_Name]');
if (widget)
    $public.Validation.setWidgetAsInvalid(widget.id,"テストValidationMessage");
```
　
1行目：InputのNameを使い、ページ内のDOMをidの後方一致で検索している。
　DOM要素に変換されたWidgetのidは、WidgetのNameプロパティの値で終わる。[Develop for Testability - Widget Naming](https://success.outsystems.com/documentation/11/developing_an_application/testing_your_application/develop_for_testability/#widget-naming)というドキュメントに以下の通り記述されている。
> ...  the element’s ID attribute will always terminate with the widget’s name property value. 

3行目：Validation APIのsetWidgetAsInvalid関数を使い、定義したUI (Input）にValidation Message「テストValidationMessage」を設定しエラー状態にしている。第1引数は1行目で取ったDOM要素のidを渡す。

## 動作確認
Screen Action内でJavaScript APIを呼べば、想定通りにValidationを設定できる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/9cd6e159-0e34-6403-a76a-9b1333e59a23.png)

# Screen ActionでないClient ActionからはValidation APIを呼べない
## 実装変更：Validation API呼び出しをClient Actionに移動する
Screen Actionをコピーして、Client Actionsの下に貼り付けて新しいClient Actionにする。
このActionをClient Actionから呼び出すように変更。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/de099431-0be0-9077-1c6c-6cd02805ee91.png)

## Validation API呼び出しが、必要なオブジェクトへのアクセスができずにエラー
実装変更した状態でChromeでテストすると、以下の通りエラーが発生する。
「Cannot read properties of null (reading 'widgets')」
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/2ae4d7e0-943a-dd31-056b-a7a4909ee144.png)

なお、Firefoxでテストすると、エラーメッセージは「this._model is null」。

Chromeの開発者ツールで該当部分のソースを覗くと以下の通り。
2行目が該当する。「this._model」が実際にnullであるためにエラー。
理由はわからないが、Client ActionからValidation APIを呼ぶと、このオブジェクトがないためにエラーになるということらしい。

``` js
Validation.prototype.setWidgetAsInvalid = function (widgetId, validationMessage) {
    var validationRecord = this._model.widgets.get(widgetId);
    validationRecord.validAttr = false;
    validationRecord.validationMessageAttr = validationMessage;
};
```

## 関連情報（OutSystems Forum)
[How to use the mobile javascript API class $public.Validation](https://www.outsystems.com/forums/discussion/37394/how-to-use-the-mobile-javascript-api-class-public-validation/)
Mobileの場合の話題だが、同じ問題に行き着いている。

# 回避策
一応回避策はある。
$publicという、JavaScriptでOutSystemsのAPIにアクセスするためのオブジェクトをInput Parameterとして渡す方法で、安全性や注意点について詳細な情報が得られないので気軽には使えないが、一応紹介しておく。

[Why it is not possible to use $public outsystems react API in Screen Actions?](https://www.outsystems.com/forums/discussion/73949/why-it-is-not-possible-to-use-public-outsystems-react-api-in-screen-actions/#Post303310)で、Dorine Boudry
さんが紹介している方法。

> you could retrieve the $public object at screen level and store it in a local variable of type object in your screen.
> Each client action that you want to use on the public api, takes in an input variable of type object.
> Pass this into your javascript and use it instead of $public.

というわけで、
1. Screen ActionでClient Actionを呼ぶ前にJavaScript要素で$publicを取り出す
1. Client ActionはObject型Input Parameterとして、$publicを受け取る
1. Validation APIを呼ぶときに、$publicの代わりに受け取った変数を経由して行う

とすればよい。

## 実装例
### Screen Action
Client Actionの呼び出し直前に$publicオブジェクトを取り出すようにした。
JavaScript要素で、Object型のOutput Parameter 「PublicContext」を作り、$publicを編集。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/f85871d8-5e0f-4262-dc5d-d65a5f3e9345.png)

この値をClient ActionのInput Parameterに渡す。

### Client Action
Object型のInput Parameterを持たせる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/c4d79eb0-23e4-412b-57d8-d1bda5f84cfe.png)

JavaScript要素のInput Parameterにそのまま渡し、渡したObjectを経由してValidation APIを呼ぶ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/332987/e494b500-019e-3161-c779-241da8fd795a.png)