5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

[OutSystems]JavaScriptでInputの値を変更する方法

Last updated at Posted at 2023-06-17

Input Widgetの値への変更をJavaScriptで、DOM要素の.valueを経由して行うと、Inputに紐づけた変数に値が反映されない。

Inputに機能を追加するBlockを作るときに困るので、解決策を探ってみる。

環境情報

Personal Environment(Version 11.21.0 (Build 39357))
Service Studio(Version 11.54.13)

サンプル

HousesoftSampleReactiveのバージョン1.0.19。
ChangeInputValueWithJS Screenを参照。

NG例:JavaScriptでinput.valueの値を変更

まずはダメなケースの実装を確認する。

以下のコードをJavaScript要素に書いてもうまくいかない。
なお、Input WidgetのIdはJavaScript要素のInput Parameterとして渡している。

document.getElementById($parameters.InputId).value = "JavaScriptでinput.valueを更新"

以下のように、Input Widgetに表示されている値は変わっているが、Input WidgetのVariableに設定した変数の値は変わらない。
image.png

原因を検討:恐らくReactのせい

OutSystemsのReactive/Mobileはフロントエンドの仕組みとして、Reactを利用している。
したがって、クライアント側でこういう不思議なことが起こるときは、Reactが怪しい。

というわけで、Reactをキーワードに入れて探すと、以下のページが見つかった。
Changing a React Input Value from Vanilla Javascript

React overrides the native Javascript onChange behavior. Triggering an onChange event does nothing to change the input field value in React’s eyes.

「ReactがJavaScript自身が持っているonChangeのふるまいを変えているため、onChangeイベントを発生させるだけだと、Reactの観点からは値が変わったように見えない。」

対策①_valueTrackerを使う

「原因を検討」で引用したページで示されていた対策。
Platform Server v11.12.0により、Reactのバージョンは16。よって引用したページの対策のうち、_valueTrackerを使う方法を利用する。

JavaScript要素に記述するコードは以下の通り。

// see: https://chuckconway.com/changing-a-react-input-value-from-vanilla-javascript/
// 1
let element = document.getElementById($parameters.InputId);
// 2 
let lastValue = element.value;
element.value = "JavaScriptでinput.valueを更新してdispatchEvent(_valueTracker利用)";

let ev = new Event ("input", { bubbles: true });
// 3
let tracker = element._valueTracker;
if (tracker) {
    tracker.setValue(lastValue);
}
// 4
element.dispatchEvent(ev);
  1. Input Widgetに対応するinput要素を取得
  2. 変更前の値を保存し、.valueプロパティを経由して値を変更
  3. _valueTrackerの値を変更前の値に設定
  4. inputイベントを発生させる

という流れ。この中の「3」のステップが肝になっている。
ただし、プレフィックスに「_」がついているのであんまり公然と使うものでもなさそう。

この方法で変更すると、以下の通り変数の値も更新される。
image.png

方法②inputのsetterを直接呼ぶ

Reactが本来のsetter関数を書き換えるのであれば、書き換えられる前のsetter関数を使って値を設定すればよい、というアイデア。
What is the best way to trigger change or input event in react jsのacceptedな回答で示されていた方法。

// see: https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-change-or-input-event-in-react-js#46012210
// 1
let element = document.getElementById($parameters.InputId);
// 2
let nativeSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set;
nativeSetter.call(element, "JavaScriptでinputのsetterを呼んでdispatchEvent");

// 3
let ev = new Event ("input", { bubbles: true });
element.dispatchEvent(ev);
  1. Input Widgetに対応するinput要素を取得
  2. input要素のsetter関数(PropertyDescriptorのsetから取得)を使って値を更新
  3. input Eventを発生させる

Input Masks LibraryというForgeコンポーネントもこの方法を使っている。

適用に当たって

VanillaなJavaScriptのみで実現している方法②をとりたい。
ただし、OutSystemsのバージョンアップ (にともなうReactのバージョンアップやフロントエンドフレームワーク事態の変更)に伴って使えなくなることにも備えたい。

そこで、こういう習性は共通部品のBlockやClient Actionに分離し、必要になったら簡単に置き換えられるようにしよう。

5
4
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?