[OutSystems]Reactive WebのClient Actionでコールバック等を実装してみた

Forgeのある部品を見ていると、JavaScriptのobjectをClient Actionの引数や戻り値（Object型）として利用しているのを見つけたので、ひょっとしてClient Actionにはコールバック関数を渡せるのでは？と思い試してみました。伝わりにくいと思いますので、下の具体例を見てみて下さい。

# 実装したもの
## Range Action
まずはPythonで言うrange関数っぽいものを実装してみました。[^1]

```javascript
/**
 * [From,To)の半開空間の連想配列を返す。
 * @module Range
 * @param {Integer} From
 * @param {Integer} To
 * @return {Object} IntegerDict
 */ 
```

本当は連想配列ではなく配列を返したかったのですが、~~Object型の変数に配列を代入した後にその変数を利用しようとしてもnullになっていました。~~そのためやむなく連想配列を使っています。メイン処理の実装は以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337041/24245c56-8e0c-3f2d-ba43-4819419dafc5.png)

JavaScriptの連想配列dictをOutSystemsのObject型のIntegerDictに代入しています。この後（キャプチャーには含まれていないですが）、Range Actionの戻り値IntegerDictにこの値を代入しています。

## Map Action
次にmap関数っぽいものを実装していました。この引数の1つがコールバック関数です。

```javascript
/**
 * InputDictに各要素に対して、CallbackObjectを適用し、その結果を返す。
 * @module Map
 * @param {Object} InputDict
 * @param {Object} CallbackObject
 * @param {Boolean} UseClientAction
 * @return {Object} OutputDict
 */ 
```

InputDictには先ほどのMap Actionの戻り値を渡しています。CallbackObjectは次の画像のようにJavaScriptで関数定義することもできますし、予め作成したClient Action（この例では`$actions.Square`）を設定することもできます。Clinet Actionを利用する場合はUseClientActionをTrueに設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337041/029faff0-c2f8-1dbd-ec27-a43475525d62.png)

Map Actionのメイン処理の実装は以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337041/b723e699-7a65-36a2-19b0-0590ede19c02.png)

1. map関数を利用するために連想配列から配列に変換
1. map関数を利用し配列にCallbackObjectを適用
1. Client Actionを利用する場合には配列の要素がプリミティブ型ではなく連想配列になるので、それをプリミティブ型に変換（おそらくClient Actionでは複数の戻り値を設定できるので連想配列になるのだと思います[^2]）
1. 配列から連想配列に変換

ということをしています。

## Stringify Action
最後にJOSN.stringifyっぽいものを実装してみました。OutSystemsにはObject型をテキスト型に変換する組み込み関数はないので、このActionでそれを実現します。[^3]

```javascript
/**
 * ObjectをJSON（テキスト）に変換する。
 * @module Stringify
 * @param {Object} Object
 * @param {Boolean} ConvertToArray
 * @return {Text} JSON
 */ 
```

連想配列ではなく、配列のJSONが必要な場合はConvertToArrayをTrueに設定します。メイン処理の実装は以下の通りです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337041/d2647d20-db92-c728-08a6-1ccb68492218.png)

## ご参考：Square Action
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337041/42491a06-63dd-a48c-56bc-edde26d83a69.png)

# 実装を通してわかったこと
* ~~Range Actionのところに書いたようにOutSystemsのObject型にJavaScriptの配列を代入してもうまくいかない~~
* JavaScript WidgetのParameterの型はBasic TypesのみでListが選択できない
* Service Studioのデバッガーを使ってもObject型の中身は確認できないため、ブラウザの開発者ツールのデバッガーを使った方がいい

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337041/15fb88d4-7887-ae67-490c-bd26163fba80.png)

~~特に上の2点が改善され、配列やListが使えるようになると、Client Actionの実装方法の幅は広がるように感じました。~~クライアントサイドでは外部のJavaScriptライブラリを使ったり、DOMの操作をしたりと、どうしてもJavaScriptのコードを書く機会が多くなってしまうので、それならば処理をJavaScript Widgetの中に集約させた方がいい気がしています。例えばFor Each Widgetを使ってループさせるのではなく、JavaScriptのfor文を使うなどです。~~現時点ではJavaScript Widgetに配列を渡せずこれは実現できないので改善を待つことにします。~~

# 【追記】実装したもの（配列版）
OutSystemsのObject型の変数にJavaScriptの配列を代入してもうまくいかないと書いたのですが、どうも勘違いしていたようです。@jyunji_watanabeさん、[コメント](https://qiita.com/shoji9x9/items/9d316baabbd6b2a49b3d#comment-141cd6e4282c7bc5b4dd)ありがとうございました。ということで、配列を使った実装です。基本的な処理内容は連想配列版と同じなので説明割愛します。

## Range Action
```javascript
/**
 * [From,To)の半開空間の配列を返す。
 * @module Range
 * @param {Integer} From
 * @param {Integer} To
 * @return {Object} IntegerArray
 */ 
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337041/72322c90-8a4c-6d99-5c62-057bdf8dc80d.png)

## Map Action
```javascript
/**
 * InputDictに各要素に対して、CallbackObjectを適用し、その結果を返す。
 * @module Map
 * @param {Object} InputArray
 * @param {Object} CallbackObject
 * @param {Boolean} UseClientAction
 * @return {Object} OutputArray
 */ 
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337041/a859ce1a-15dc-a2f5-0ec8-a5a8edaa0b74.png)

## Stringify Action
[変更なし。](#stringify-action)

## ArrayToText Action
Stringify Actionでもいいのですが、JSONの中の[]などを表示したくない場合もあるので、joinっぽいActionも作ってみました。

```javascript
/**
 * 配列をSeparatorで区切られたテキストに変換する。
 * @module ArrayToText 
 * @param {Object} Array
 * @param {Text} Separator
 * @return {Text} JoinedText
 */ 
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/337041/dbfe0406-8e2d-e0cd-cd02-5f0c4a8a1abe.png)

[^1]: そういえば昔[ExtensionでもRange Action作っていました](https://qiita.com/shoji9x9/items/1e4653ba2d2bae02ef21)。
[^2]: 記事を書いてから気付きましたが、Square ActionをFunctionにすると配列の要素がプリミティブ型になりましたので、この処理はなくても問題ありません。
[^3]: 一応、ObjectToRecordListというExtensionがあり、この中のObjectToRecordList ActionでObject型をRecordList型に変換し、その後Text ExtensionのString_Join Actionを使えばText型に変換できるかもしれません（試していません）。