注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。
Slate では、すべてのウィジェット、関数、変数、クエリが、グラフ内のノードとしてモデル化されます。各ノードは評価されて JSON 出力を生成し、ほかのノードはその出力を参照するようにテンプレート化されます。これらの参照によってノード間の依存関係が定義されます。Slate はこの依存関係を使い、何かが変更されたとき(新しいユーザー入力、クエリの実行、変数値の更新など)に、いつノードを再評価するかを判断します。
Slate アプリケーションの「開発」における主要なプロセスは、出力の表示と入力の取得に使うウィジェット、表示用のデータの軽微な操作とアプリケーションのログイン処理に使う関数、データのクエリやほかの Foundry サービス API への接続に使うクエリを追加してこれらのノードを定義し、Handlebar 構文で依存関係を設定することです。
依存関係タブでは、この依存関係グラフをいつでも表示し、Handlebar の参照を通じて設定した関係を Slate がどのように解釈しているかを確認できます。このグラフ表現_こそが_アプリケーションです。「間違っている」と思えるものが表現されている場合は、グラフがアプリケーションを誤って表現していると考えるのではなく、_なぜ_そうなっているのかを理解することから始めます。
アプリケーションで予期しない動作が起きた場合は、まず依存関係タブで、意図どおりに動作していないウィジェットの上流ノードを確認します。無関係なクエリやユーザー入力に基づいてノードが再評価される原因となり得る、予期しない関係を探します。
依存関係グラフを「遅延評価」するものとして捉えると役立つ場合があります。上流の参照先の値が変わったときにのみノードを再評価することで、不要な処理を避けます。このため、上流ノードが以前と同じ値に更新された場合には、混乱を招く可能性のある動作が起こります。下流のクエリは実行されず、下流のウィジェットも再レンダリングされません。これは、ユーザー入力をリセットするパターンでよく見られます。Foundry で Intro to the Dependency Graph and Events Framework チュートリアルと Resetting Widget Selections の例を検索すると、この動作と、Math.random() を使って依存関係グラフの評価にランダム性を加え、ノードが確実に再評価されるようにする回避方法を確認できます。
Slate は依存関係グラフを使用して、上流の依存関係の状態に基づき、関数を再計算するタイミングやクエリを再実行するタイミングを判断する複雑な処理を行います。ほとんどのワークフローはこの機能を使用して設定できます。Slate 開発者は、可能な限り依存関係グラフを活用することを、常に最初に考えるべきです。
ただし、依存関係グラフを明示的なトリガーと操作の組み合わせで補完することが適切な場合もあります。Slate ではこれを「イベント」と呼びます。簡単な例として、クエリの完了時にバナーやトーストの表示をトリガーする、ユーザーが詳細を表示するために何かをクリックしたときにダイアログを開く、いくつかの入力を設定した後にボタンのクリックでクエリを実行する、といったケースがあります。
イベントフレームワークの強力さを理解すると、Slate でのあらゆる開発タスクをイベントベースのソリューションとして捉えたくなるかもしれません。その結果、1つのアプリに数百ものイベントと操作の組み合わせを含めてしまう、よくある失敗に陥ることがあります。技術的には可能ですが、この複雑さによってアプリケーションの期待される動作を順に追うことが難しくなり、簡単なデバッグさえできなくなる場合があります。
目安として、イベントの「チェーン」はできるだけ短くし、イベントではなく依存関係グラフを活用するソリューションを優先してください。アプリのイベント数が50個を超えるようになったら、特にそれらのイベントが単にトーストをトリガーしたりクエリを開始したりする以上の処理を行っている場合は、いったん立ち止まり、アプリケーションのアーキテクチャを慎重に見直すよいタイミングです。リファクタリングして開発を簡素化したり、アプリケーションをさらにサブアプリケーションに分割したりする余地がないか検討してください。
イベントフレームワークと、よく使われる基本的なイベントパターンの例について、詳しくはこちら。
組み込みのイベントの種類に加えて、html およびテーブルタイプのウィジェットでカスタムイベントを作成し、これらのウィジェット内の異なる要素から、ユーザーのクリックによって異なるイベントを発信できます。
これらのカスタムトリガーの設定は、2つのステップで行います。
slClickEvent プロパティを使用して、HTML 要素にトリガーを定義します。Copied!1<div class='pt-button' slClickEvent='delete'>Delete Me</div>
新しいイベントを登録すると、イベントトリガーを選択できる場所に、myWidgetName.click.delete のような新しい選択肢が表示されます。クリックイベントを定義する HTML を動的に生成する場合でも、それらのイベントの登録はハードコードする必要があります。これにより、条件によってその時点では実際にボタンが生成されていなくても、Slate が潜在的なトリガーを把握できます。
これらのイベントを使用して、HTML でカスタムのボタングループを作成できます。また、以下のイベント値を渡すで説明するイベント値と組み合わせて、各項目に独自の操作を持つリストやテーブルを作成することもできます。
追加の条件(単にトリガーが発火すること以外の条件)が必要な場合は、イベント設定の JavaScript 部分を使用して、追加のロジックを実行できます。ロジックを評価し、イベントが操作を実行すべきでない場合は、特別な値 {{slDisableAction}} を返します。この値はイベントを永続的に無効化するものではなく、そのイベント操作の特定の実行だけを中断します。
ベストプラクティスとして、イベントの JavaScript 内で複雑なロジックを開発することは避けてください。Linter による解析やエラー報告がないため、誤りや考慮されていないエッジケースがあると、コードが通知なしに失敗し、予期しない動作につながる可能性があります。代わりに、イベントの有効性を判断するロジックをカプセル化した関数の実装を検討してください。そうすれば、イベントの JavaScript を次のようにシンプルにできます。
Copied!1 2 3if (!{{f_checkValidAction}}) { return {{slDisableAction}}; }
ただし、イベントに debugger() 文を記述し、Chrome Developer Tools を使用してその実行を捕捉し、ロジックをステップ実行することはできます。
slClickEvent プロパティに加えて、特定の HTML 要素に slClickEventValue を定義できます。このイベントがトリガーされるたびに、特別な変数 {{slEventValue}} を使用して、イベントの JavaScript 内で関連する値を参照できます。
これにより、動的なインタラクションのさまざまなパターンを実現できます。
slClickEvent と slClickEventValue を定義する関数を作成します。variable.changed イベントトリガーを起点として、さらに操作(通常はクエリ)をトリガーし、変数の値を参照します。たとえば、このパターンを使用して、テーブルの行の横に小さな 「x」 を追加できます。この要素では、slClickEvent を delete、slClickEventValue を行の主キーにします。その後、myTable.click.delete イベントが発火したときに、v_idToDelete という変数の値をイベント値に設定できます。続いて、v_idToDelete.changed イベントに基づいて q_deleteRow クエリを実行し、この短いチェーンを完成させます。
チェーンをこれより大幅に長くしたり複雑にしたりすることには注意してください。以下の循環依存に関する警告を参照してください。大規模、入れ子、または入り組んだ構成は、理解やデバッグが難しくなり、予期しない動作につながることが多いため、短く独立したイベントを優先してください。
Slate の依存関係グラフは、技術的には「有向非巡回グラフ」(DAG) です。これは、ノード間の関係に方向があること(ルートノードからリーフノードへの階層があり、その方向にノードの解決が行われること)と、グラフ全体が非巡回でなければならないことを意味します。つまり、Slate は依存関係グラフに、長さにかかわらず「ループ」が設定されることを防ごうとします。
ただし、イベントを使用して変数の値を設定したりクエリをトリガーしたりすると、アプリケーションにループを追加できてしまい、非決定論的な動作につながります。循環依存が必要に思えるワークフローがある場合は、いったん立ち止まって別のパターンを検討してください。ほとんどの場合、求めるワークフローの機能を実現する方法があります。
依存関係グラフのフレームワークでは、デフォルトでページの読み込み時に依存関係グラフ全体が解決されます。つまり、グラフ内の各ノードを解決するために、すべてのクエリと関数が必要な順序で実行されます。実際には、開発中にこの点に注意していないと、ページの読み込みパフォーマンスが徐々に低下し、やがて断続的にタイムアウトが発生することがよくあります。根本的な原因は、ほぼ常に、上流のクエリへの依存関係を持たない「ルート」ノードにあたるクエリが多数あることです。つまり、これらのクエリがすべて同時に実行を試みます。ブラウザーからの同時接続、同じテーブルへの同時クエリ、アプリケーションに返されるネットワークトラフィックが多すぎるため、キューイングや負荷制限が発生する可能性があります。最終的には、クエリの失敗が非決定論的に発生し、ページの読み込み時にアプリケーションが完全に機能しなくなるか、少なくとも動作が重く感じられる状態になります。
Postgate では、アプリケーションの各ユーザー専用の接続プールが作成されます(最近そのアプリケーションをすでに使用していた場合は、キャッシュから復元されます)。各接続プールの同時接続数は10に制限されています。Postgate はアプリケーションのすべてのユーザーと、同期済みのデータを使用するほかのアプリケーションで共有されるため、この制限によって、1つのアプリケーションインスタンスが Postgate に過剰な負荷をかけることを防ぎます。パフォーマンス面では、Postgres は処理する同時クエリ数が少ないほど全体として高速になるため、この制限により、すべてのクエリを一度に通す場合(クエリ数が10を超える場合)よりも、実際にはパフォーマンスが向上します。
10個の接続がすべて使用されると、残りのクエリリクエストはキューに入り、接続が利用可能になり次第、順次取得します。ただし、接続を無期限に待たせるのではなく、接続プールは接続を5秒間待機しているクエリをタイムアウトさせます。つまり、実行に数秒かかるクエリによってプールが埋まっていると、本来は高速なクエリ(たとえば、テストボタンを使って単独で実行した場合には高速なクエリ)でも、長時間かかったり、タイムアウトしたりする可能性があります。
この問題の解決策は、次のいくつかの要素からなります。
2つ目の点を掘り下げると、あらゆる開発で無駄のないコードを目指すべきです。コードは、必要な処理だけを可能な限り効率的に行うべきです。Slate のクエリでは、同じ処理を何度も繰り返すことに多くの時間を費やしてしまい、これが弱点となることがよくあります。ユーザーがカテゴリーを選択するためのドロップダウンに値を設定する、次のシンプルなクエリを考えてみます。
Copied!1SELECT DISTINCT(COALESCE(category_col)) FROM "my_table"
このクエリでは、ページを読み込むたびに、Postgres がすべての行の category_col の値を調べて null であるかを確認し、重複のないすべての値のリストを生成する処理を繰り返す必要があります。トランスフォームと新しい派生データセットを使用すれば、上流に新しいデータが追加されるたびに、この処理を1回だけ実行し、クエリを次のように単純化できます。
Copied!1SELECT * FROM "my_table_categories"
たとえばユーザー入力に対応する必要がある場合など、SQL 式を完全には取り除けなくても、可能な限り多くの処理を上流のトランスフォームに切り出し、よりシンプルなクエリを作成することを目指すべきです。「処理を行う」回数を可能な限り少なくするという原則を常に念頭に置くことで、より安定し、パフォーマンスの高いアプリケーションに近づけます。
クエリの実行タイミングを制御するための2つの一般的なパターンについて、以下で詳しく説明します。これらをアプリケーションで使用すると、ページの読み込み時に実行されるクエリ数を大幅に減らし、アプリケーションが期待どおりに動作するようにできます。
条件付きクエリは、クエリの実行を最も細かく制御できる機能です。「イベントによるトリガー」や「手動」の設定(後述)と組み合わせて、クエリの実行に必要な論理条件を追加できます。クエリの実行条件を有効にするには、実行の横にあるドロップダウン内の、さらに...のチェックボックスをオンにします。
最も単純なケースでは、すべての依存関係が null ではないというオプションにより、クエリまたはそのパーシャルに含まれるハンドルバー式のいずれかに値が指定されていない場合、クエリの実行が防止されます。これは、意味のある処理を行うために一定のユーザー入力を必要とするクエリで特に役立ち、WHERE 文が不完全なために失敗するクエリで、データベースとの不要な通信が発生することを防ぎます。
クエリの実行タイミングを制御するシンプルな方法は、実行ボタンの横にあるドロップダウン内のチェックボックスを使い、クエリを手動に設定することです。
手動実行に設定されたクエリは、イベントまたは手動で指定された依存関係によってトリガーされた場合にのみ実行されます。
これは、ユーザーの操作によってトリガーされるべきクエリに適したパターンです。ただし、あまりに多くのクエリを連鎖させると、不必要に複雑になる可能性があります。上記のイベントセクションのガイダンスを参照してください。手動クエリがトリガーによって実行されると、依存関係グラフは下流の依存関係を自動的に更新します。q_queryName.success に基づくイベントを追加する必要はありません。
関数を使用すると、データを軽く加工したり、ユーザーの選択やアプリケーションの状態に基づいてロジックを実行したりできます。見落とされがちなパターンや機能をいくつか活用することで、関数を使った処理をより整理し、シンプルにできます。
ユーザー入力を検証するの例を見ると、検証関数が単一の値ではなく、JavaScript オブジェクトを返していることが分かります。Slate はこれらのオブジェクトをパースし、アプリケーション内のほかの場所でも Handlebars を通じて利用できるようにします。
つまり、たとえば異なるウィジェットで使用する新しい表示用の列をいくつか導出する必要がある場合は、列ごとに関数を作成する代わりに、必要な表示値をすべて異なる形式で作成する単一の関数を作り、オブジェクトにまとめて返すことができます。次のセクションで、この例を詳しく説明します。
ロジックを少数の関数に集約することと、分割することの間には、常にバランスが必要です。集約すると、処理の重複を減らし、類似の処理をまとめておくのに役立ちます。一方、分割すると、個別の操作をカプセル化し、必要なコードだけを見つけやすくなります。このトレードオフを念頭に置き、アプリケーションを改善する過程で関数を定期的にリファクタリングしてください。
グラフに表示するためにデータを整形したり、テーブルに表示する HTML を生成したりするために、追加の処理が少し必要になることはよくあります。日付の書式設定など、可能な場合は、この処理をデータ変換および準備の段階で行うことを検討してください。これは、処理の実行回数をできるだけ少なくするという原則に沿ったものです(つまり、ユーザーごとにページを読み込むたびに処理するのではなく、トランスフォーム内で1回だけ処理します)。
それができない場合は、Slate が想定するデータ構造を保持しながら、列の各値に処理を適用して新しい値を生成するシンプルな関数を記述できます。
Copied!1 2 3 4 5 6 7 8`// f_deriveDisplayColumns var data = {{q_myQuery.results.[0]}} data.newColA = _.map(data.primaryKey, (value,key,index) => { // 新しい列を導出する処理 return newColValue }); return data;`
このスニペットでは、Lodash ↗ の _.map() 関数を使用しています。この場合は、組み込みの Array.map() も同様に使用できます。Lodash のヘルパー関数は、JavaScript オブジェクトの操作に非常に役立ち、ゼロから実装し直すとバグが生じやすい複雑なアルゴリズムも実装されていることが多いため、使い慣れておくと便利です。
関数が完成したら、{{q_myQuery}} を直接参照する代わりに、アプリ内のどこからでも {{f_deriveDisplayColumns}} を参照できます。元のクエリのレスポンスオブジェクトと同じ構造を持ち、「列」が追加されているだけだからです。
かなり複雑な JavaScript を実装する必要がある場合は、関数エディターの左下にあるカスタムライブラリへの移動を検討してください。ライブラリを使うと、純粋な JavaScript 関数を Slate のどの「関数」からでも利用できるため、同じヘルパー関数を何度も実装せずに済みます。
ライブラリは純粋な JavaScript であるため、その内部では Handlebars を使用できません。代わりに、通常の JavaScript と同様に、Slate の「関数」からライブラリ関数を呼び出す際にパラメーターを渡すことができます。これらのパラメーターには、Handlebars を使って値を設定したり、ほかの方法で動的に生成した値を設定したりできます。
ライブラリのコードと関数のどちらでも、必ず時間を取ってコードを分かりやすく整理し、直感的には理解しにくい可能性がある箇所を文書化してください。「自己文書化」されたコードを書くことは常に目指せますが、ロジックを分かりやすく簡潔に文書化する習慣を身に付けることは、将来の開発者や保守担当者への配慮になります。