このブログでは、 Typescriptについて学ぶ そして、その方法をお見せします あなたの属性を使用する Mendix ウィジェットのモデル、どのように ブラウザAPIを活用する そして、 React での条件付きレンダリング.
これは複数部構成のシリーズの 1 番目のブログです。ブログ XNUMX は、こちらでご覧いただけます。 ウィジェットを構築する Mendix React を使う — パート 1 — カラー カウンター
プラグ可能なウィジェットを使用する場合
新しいプラグ可能なウィジェットを作成するかどうかを決定するときに使用するリトマス試験がいくつかあります。
- UIコンポーネントは必要ですか? — Javascriptを使用する必要があるがUIコンポーネントがない場合は、 JSアクション あなたのユースケースに適しているかもしれません
- それは利用可能でしょうか Mendix マーケットプレイス?
- ブラウザ API または NPM パッケージを使用しますか?
私たちが構築しているもの
今回は、 一定の間隔でページを更新するためのタイマー「JavaScriptでやればいいじゃないか」と疑問に思う人もいるでしょう。それは、視覚的なコンポーネントも用意するからです。 次の更新までの残り時間を表示します.

これは実際の使用例に基づいており、ダッシュボードが工場のフロアに表示され、30秒ごとに更新される必要がありました。

スタートガイド
このウィジェットでは、 タイプスクリプトは、JavaScriptを強く型付けしたラッパーです。最初はコードを書くのが少し難しくなりますが、 組み込みのリンティング の三脚と 広範なドキュメント プラグイン可能なウィジェットを作成するときに非常に便利です。Typescriptのこれらの要素は、 大規模チームでの共同作業が容易 の三脚と 複雑なAPIとの統合.

まずウィジェットを足場にして、 ブログ1 今回はプログラミング言語として Typescript を選択します。
React における関数コンポーネントとクラスコンポーネントについての簡単なメモ。 このブログでは機能コンポーネントを使用しますただし、 クラスコンポーネントを使用して書かれたReactに遭遇する。 それの訳は、 まで フックのリリース React 16.8で, クラスコンポーネントは状態を管理する唯一の方法だった、関数コンポーネントは主にフロントエンドのプレゼンテーション層でした。現在、Reactコミュニティは 機能コンポーネントの使用への移行 彼らはあるとして 文法的に単純 もっと 軽量.
開発時間
プラグ可能なウィジェットのフォルダ構造と開発環境の設定方法については、私の 前のブログ.
まず最初に
開発プロセスをスピードアップするために、エディター プレビュー ファイルを削除します。このファイルは、Studio Pro または Studio のデザイン モードでウィジェットのプレビューを作成しますが、このガイドではこれについては考慮しません。更新する項目が 1 つ増えるだけです。では、先に進んでファイルを削除しましょう。 Timer.editorPreview.tsx。
ブリーフ
ウィジェットで何を達成しようとしているのかを、3 つの主要なセクションに分けることができます。
- ステップ1: タイマーを、 Mendix
- ステップ2: タイマーを1秒間隔で減らす
- ステップ3: アクションを実行する
ステップ1: タイマーを、 Mendix
まずは、 Mendix モデルとウィジェット。単純な整数を使用することもできますが、属性を使用すると、実行時にタイマー値を動的に変更する柔軟性が高まります。
Timer.xml を更新して次の内容を含めます。
<property key="timeAtt" type="attribute" required="true
<caption>Time attribute</caption>
<description>The number of seconds on refresh</description>
<attributeTypes>
<attributeType name="Integer"/>
</attributeTypes>
</property>
ついでに、子コンポーネントの名前を TimeText に、インターフェースの名前を TimeTextProps に変更する必要があります。ファイルを保存して、次のコマンドを実行します。npm run build、エラーが表示されますが、これは良いことです。

これは、sampleText プロパティが timeAtt に置き換えられたことを示しています。デフォルトの sampleText の代わりに timeAtt を使用して Timer ファイルを更新してみましょう。
Timer.tsxファイルでtimeAttをハイライトすると、それがタイプであることがわかります。 EditableValue<Big>.
編集可能な値 は ジェネリックタイプ それ Mendix すべての属性を表すために使用され、その中の値の読み取りと書き込みを可能にします。 Mendix モデル。
'<>' 部分は、属性タイプによって決まります。整数または小数点の属性の場合は、Big.js ライブラリを使用します。これは、アプリケーションで使用される数値が、デフォルトの JavaScript 数値制限によって制約されないようにするためです。
使用状態
これまでに、 Mendix コンテナコンポーネントのpropsを介してウィジェットに渡します。次に、ページに表示するために、TimeTextコンポーネントに読みやすい値を渡す必要があります。これを行うには、 EditableValue<Big> 単純な整数を 状態 コンテナ コンポーネントの。
を使用しております 状態 ここでではなく 小道具 タイマーの値は時間の経過とともに変化するため、 小道具は直接変更してはならない:
まずはインポートから始めましょう 使用状態 「React」から 状態 呼ばれます 時間 次のように:
const [time, setTime] = useState<number>();
これにより、状態変数(時間) とその状態を変更する setState 関数を使用します (props と同様に、状態は直接変更することはできず、代わりにこの関数を介して変更できます)。
useEffectの使用
次に、 属性値を状態に渡すこれを行う最も簡単な方法は、useState に値を渡すことです。
const [time, setTime] = useState<number>(timeAtt.value.toNumber());
しかし、値が空になる可能性もあるため、その方法は採用しません。代わりに、React のもう 1 つの中心的な概念である「useEffect」を使用します。
では、useEffect とは何でしょうか? 基本的には、レンダリングのたびに関数を実行します。次のようにして時間を設定することができます。
export function Timer({timeAtt}: TimerContainerProps):ReactElement {
const [time, setTime] = useState<number>();
useEffect(() => {
if (timeAtt.value) {
setTime(timeAtt.value.toNumber());
}
}, []);
return <TimeText sampleText={sampleText ? sampleText:"World"}/>;
}
次に、新しい時間状態を受け入れるようにプロパティを更新して、これを子コンポーネントの TimeText に渡すことができます。
return <TimeText value={time}/>;
これでタイマー コンポーネントが更新されました。
ここで、TimeTextコンポーネントにいくつかの小さな変更を加える必要があります。 新しい小道具を受け入れるTypescriptを使用しているため、 インタフェース コンポーネント用です。これは、このコンポーネントを使用するコードがどのようにコンポーネントと対話するかを定義します。それでは、表示コンポーネントを次のように更新しましょう。
export interface TimeTextProps {
value: number | undefined;
}
export function TimeText({ value }: TimeTextProps): ReactElement {
return <div>{value} seconds</div>;
}
ここではインターフェースは不要と思われるかもしれませんし、このような小さなウィジェットではおそらくそうでしょうが、 プロジェクトが大きくなると非常に便利になります.
テストするには、 Mendix Studio Pro を開き、F4 キーを押して更新をウィジェットに同期します。次に、数値属性をタイマー ウィジェットに渡すことができるようにページを設定する必要があります。

We [実行]をクリックします そして…私たちのページにはただ「秒」とだけ書かれています。

心配しないでください これはどのように予想されるか Mendix 属性をロードするを選択します。 属性値は非同期に読み込まれます、これが Mendix フロントエンドは非常に高速ですが、ウィジェットを動作させるにはさらに数行のコードが必要になります。
useEffectを正しく使用する
useEffectの2番目のパラメータは空のままにしました。 これは、コンポーネントが最初にマウントされたときにuseEffect関数が1回実行されることを意味します。、その時点で 属性値がまだ準備ができていません.
これを確認したい場合は、 console.log(`Render ${timeAtt.value}`) if ステートメントの上にカーソルを置き、ブラウザ コンソールを確認します (ブラウザから 'Ctrl + Shift + i')。
したがって、代わりに、timeAtt が更新されるたびに useEffect 関数が実行されるようにするには、useEffect の 2 番目のパラメータに timeAtt を追加するだけです。
export function Timer({timeAtt}: TimerContainerProps):ReactElement {
const [time, setTime] = useState<number>();
useEffect(() => {
if (timeAtt.value) {
setTime(timeAtt.value.toNumber());
}
}, [timeAtt]);
return <TimeText value={time}/>;
}
アプリケーションを再度実行すると、 Mendix タイマーの値が表示され、コンソール ログを確認すると、ウィジェットが 2 回レンダリングされていることがわかります。1 回は値が未定義のとき、もう 1 回は数値があるときです。


成功しました!これでパート 1 は完了です。
ステップ2: タイマーを1秒間隔で減らす
いいですね。画面に数字が表示されていますが、あまり役に立ちません。 タイマーをカウントダウンさせたいそのために私たちは ブラウザのJavascript APIを使用する 特に window.setTimeout()、その 一定時間後に関数を実行する.
これを古い友人のuseEffectと組み合わせると、カウントダウンを作成できます。useEffect関数を時間状態に依存し、1秒待ってから変更すると、 時間 状態を 1 秒ずつ変更すると、基本的にループが作成されます。
export function Timer({timeAtt}: TimerContainerProps):ReactElement {
const [time, setTime] = useState<number>();
useEffect(() => {
if (timeAtt.value) {
setTime(timeAtt.value.toNumber());
}
}, [timeAtt]);
>seEffect(() => {
if (time !== undefined) {
window.setTimeout(() => setTime(time - 1), 1000);
}
}, [time]);
return <TimeText value={time}/>;
}
ただし、このカウントは永久に継続されるため、これを停止するにはもう 1 つの条件を追加する必要があります。
useEffect(() => {
if (time !== undefined) {
if (time > 0) {
window.setTimeout(() => setTime(time - 1), 1000);
}
}
}, [time]);
ウィジェットをリロードすると、カウントダウンが表示されていることがわかります。

使用している場合は、 npm run start ウィジェットの変更を監視します キャッシュをクリアして更新する必要があります (Chrome ではブラウザ ツールを開き、更新ボタンを右クリックします)
ステップ3: アクションを実行する
カウントダウンが 0 になったときに何かが起こるようにする必要があります。
幸いにも Mendix モデルと統合してアクションを実行するための非常に使いやすい API を提供します。まず、Timer.xml を更新して次の内容を含めます。
<property key="action" type="action" required="true">
<caption>Action</caption>
<description>Action to trigger when the time elapses</description>
</property>
次に、Timer.tsx でアクションを実行する関数を追加し、useEffect 関数を更新して 0 でアクションを実行します。
最終的な Timer.tsx は次のようになります。
export function Timer({ timeAtt, action }: TimerContainerProps): ReactElement {
const [time, setTime] = useState<number>();
useEffect(() => {
if (time !== undefined) {
if (time > 0) {
window.setTimeout(() => setTime(time - 1), 1000);
} else {
execAction();
}
}
}, [time]);
useEffect(() => {
if (timeAtt.value) {
setTime(timeAtt.value.toNumber());
}
}, [timeAtt]);
const execAction = () => {
if (action && action.canExecute) {
action.execute();
}
};
return <TimeText value={time} />;
}
この新しいコードをテストするには Mendix F4 キーを押して実行するアクションを選択すると、アプリが起動します。

ページを更新するマイクロフローを呼び出すことを選択すると、繰り返しタイマーが取得されます。
最後に
コンポーネントが最初にロードされるとき、TimeText コンポーネントに渡される時間値が未定義になる短い期間がありました。これはウィジェットが「秒」をレンダリングするだけなので大きな問題ではありませんが、値がロード中であることを示す何らかの表示を与えるのがベストプラクティスです。
そのためには、条件付きレンダリングに React の一般的なパターンを使用します。
if ステートメントを使用して、値がまだ定義されていないかどうかを確認し、次のように読み込み中を表示できます。
export function TimeText({ value }: TimeTextProps): ReactElement {
if (value === undefined) {
return <div>Loading</div>;
} else {
return <div>{value}</div>;
}
}
しかし、これは非常に冗長です。 我々は使用することができます ES6 の三項演算子 これを短縮すると次のようになります。
export function TimeText({ value }: TimeTextProps): ReactElement {
return value ? <div>{value} seconds</div> : <div>Loading</div>;}
そして出来上がり!

製品概要
おめでとうございます。プラグ可能なウィジェットを構築できました。
このビルドでは、Pluggable Widget API、ウィジェットからデータを取得する方法について説明しました。 Mendix モデル化して変更します。Typescriptを使用して、どのように統合するかをよりよく理解します。 Mendix アプリを作成し、Reactの主要コンセプトを再確認し、 Mendix ウィジェット内のモデル。
このウィジェットの最終状態は次のようになります: GitHub – joe-robertson-mx/タイマー
次に、カンバンボードを構築します。 Mendix プラグイン可能なウィジェットでは、ウィジェットを組み合わせて洗練されたエンドユーザーエクスペリエンスを作成する方法や、変更をWebサイトに永続的に保存する方法について詳しく説明しています。 Mendix データベース。