注: 以下の翻訳の正確性は検証されていません。AIPを利用して英語版の原文から機械的に翻訳されたものです。

ユーザーとの対話を可能にする

ほぼ全てのアプリケーションではユーザーとの対話が必要となり、Slate はその対話を可能にするための3つの主要な機能を提供しています:

  • 入力ウィジェット: ドロップダウンリスト、テキストフィールド、ラジオボタンなど、入力を取得するための一般的な UX 要素を提供するシンプルなウィジェットです。
  • 他のウィジェット内の選択状態: ほとんどの他のウィジェットタイプは何らかの選択をサポートしています。各ウィジェットで選択が少し異なる動作をするため、どのような選択タイプが可能か理解するために試してみる必要があります。
  • イベントトリガー: 全てのウィジェットには、ウィジェットの状態が変化した時やユーザーが何かアクションを取った時にブロードキャストするイベントトリガーのセットがあります。アプリケーションにイベントを組み込む方法の詳細については、Events ドキュメンテーションをご覧ください。

ユーザー入力を取得する最も単純なパターンは静的なフォームです:入力ウィジェットを追加し、静的なオプションを提供し、ユーザーの選択はクエリや関数で参照でき、動的なビューを提供します。通常、一つ以上の入力の選択が次の入力の利用可能なオプションのセットに影響を与える "連鎖" 入力の複雑さがあります。これらの連鎖を短く、理解しやすく保つことが重要です - 設定するパラメーターが多すぎると、アプリケーションが直感的でなく、パフォーマンスが低下します。下記の 'Open Ended Exploration' のアンチパターンを参照してください。

このような依存性のある入力の複雑な構成では、フィルター処理を設定するワークフローと、結果として得られたデータを分析するワークフローを分けることがベストプラクティスです。単純に言うと、これはユーザー入力に依存する任意のクエリを manual に設定し、ユーザーに Update Data というボタンウィジェットを提供するという意味です。このパターンは、フィルターの変更ごとに全てのクエリを再実行することでリソース(およびユーザーの時間)を無駄にしないようにします。これは特にフリーテキスト入力 - テキストフィールドテキストエリア タイプのウィジェット - を持っている場合に重要です。そうでなければ、クエリのような下流の依存関係が ユーザーのキーストロークごとに 再評価されます。

書き戻しワークフロー

アプリケーションがユーザー入力を書き戻しデータを取得するときは、必ずクエリを手動で実行するように設定し、明確なユーザーアクションにクエリをトリガーします。そうしなければ、データを永続化するクエリはすべての入力変更、テキスト入力ウィジェットの各キーストロークを含む、全てで実行され、非常に予期せぬ挙動を引き起こします。

セッション間でのユーザー選択の維持

アプリケーションが有用なビューを取得するためにさまざまな入力の設定を必要とする場合、ユーザーが設定を "保存" して後で戻るか共有する方法が欲しいと思うことがよくあります。

この機能のカスタムバージョンを作成する方法はいくつかあります - 下記の Persisting and Restoring State セクションを参照 - しかし、単純にアプリケーションを表示しているときの Actions ドロップダウンメニューから Get Shareable View リンクを指し示す方がはるかに簡単です。

この組み込み機能は、カスタムビュー ID と共にアプリケーション URL を生成し、Slate データベースに全ウィジェット選択の状態を保存します。その URL を通じてアプリケーションを表示すると、デフォルトとして行われた選択でアプリケーションが再読み込みされます。

選択のリセットとデフォルト値の管理

もう1つの一般的なパターンは、ユーザーが全てのフィールドをデフォルト値にリセットするための単一のアクションを提供することです。ここでの最も単純なパターンは v_defaults 変数を定義することです:

Copied!
1 2 3 4 5 6 7 8 9 10 11 { // "w_multiselectWidget_raw" は、選択された項目の実際の値のリストです。 "w_multiselectWidget_raw": ["a", "b"], // "w_multiselectWidget_display" は、選択された項目の表示名のリストです。 "w_multiselectWidget_display": ["Alpha", "Beta"], // "w_textInput" は、テキスト入力フィールドのデフォルト値です。 "w_textInput": "default", ... }

次に、各ウィジェットの設定で、json定義(</> アイコンの下)に選択した値プロパティの特定のバージョンをテンプレート化できます。

表示値と生の値の両方を持つ任意のウィジェットでは、selectedValuesselectedDisplayValues の両方をテンプレート化することを確認してください:

Copied!
1 2 3 4 5 6 7 8 { ... // 選択された値 selectedValues: "{{v_defaults.w_multiselectWidget_raw}}", // 選択された表示値 selectedDisplayValues: "{{v_defaults.w_multiselectWidget_display}}", ... }

最後のステップは、イベントを設定して v_defaults 変数への更新をトリガーし、依存関係グラフがダウンストリームのすべてのノードを更新するようにします。これには、テンプレート化された選択値を持つすべての入力ウィジェットが含まれ、選択肢がデフォルトに戻ります。

単純にこのようなイベントを設定するだけで十分かもしれません:w_resetWidgetButton.clickv_defaults.set

Copied!
1 2 // v_defaults を自身に設定します return {{v_defaults}}

上で説明した Slateでの事象の発生理由 のセクションにおいて、ノードの解決された値が変わっていない場合に依存関係グラフを再評価させるというニュアンスがあります。そのため、Slateがこれが新しい値であることを理解するために、「エントロピー」を追加する必要があります。これは次のように簡単です:

Copied!
1 2 3 4 5 6 7 8 // デフォルト値を取得 const defaults = {{v_defaults}} // 乱数を生成 defaults.entropy = Math.random() // 値を設定 return defaults

このパターンを使用すると、ユーザーにはデフォルトをリセットするためのボタンをクリックするように簡単に指示することができますし、クエリが送信された後にそれらをリセットすることもできます。

デフォルトをさらに進めるためには、URL パラメーターを通じて変数値を設定することができます。例えば、あるアプリケーションからのリンクをたどるユーザーには、アプリに直接アクセスするユーザーや、完全に別のツール内の iframe 内でそれを表示するユーザーとは異なるデフォルトを提供することができます。

これを上記のパターンに統合するためには、複数のプロパティを持つ複雑な単一変数を使用するのではなく、デフォルトごとに一つの変数を「分解」し、URL内で変数名を使用できるようにします(この仕組みについての詳細は Variables を参照してください)。追加の変数は「エントロピー」を提供し、これらをすべて組み合わせて、元々単一の変数にあったものと全く同じオブジェクトを返す関数を作ることができます。

Copied!
1 2 3 4 5 6 7 8 9 10 11 const defaults = { // 複数選択ウィジェット(生データ) "w_multiselectWidget_raw": {{v_multiSelect_raw}}, // 複数選択ウィジェット(表示用データ) "w_multiselectWidget_display": {{v_multiSelect_raw}}, // テキスト入力ウィジェット "w_textInput": {{v_textInput}}, // エントロピー "entropy": {{v_entropy}} ... }

デフォルトに戻したい場合は、イベントで v_entropy をランダムな値にリセットするだけで、デフォルトがリセットされます。

ユーザー入力の検証

頻繁に、ユーザーの入力を検証し、アクションを無効にしたり、ユーザーにフィードバックを提供する必要があります。これをSlateで行う方法は多数ありますが、一般的なケースで役立つ可能性がある共通のパターンを紹介します。この関数は、すべてのユーザー入力を集めてチェックを実装します。各チェックは、フォームを無効にしたり、ユーザーにフィードバックを提供することができます。

この例では、プロジェクトの情報を収集するためのフォームを検証します。これには、タイトル、URL、連絡先の名前、プロジェクトの説明、プロジェクトの状況が含まれます。私たちはユーザーの入力が有効であることを確認したい(つまり、メールアドレスが正しい形式であること)だけでなく、プロジェクトがすでに存在しないことも確認したい(つまり、主キーがすでに使用されていないこと)。

最終的には、私たちの検証を表すJSON出力を作成し、その出力をアプリケーション全体で参照して、ユーザーにフィードバックを提供し、特定のアクションを無効にすることができます。

f_validateInputs

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 // ----------------------------- // バリデーションのための入力を集める // ----------------------------- // ユーザーの入力から来る値 var inputs = { Project_Title: {{i_projectTitle.text}}, // プロジェクトのタイトル Primary_URL: {{i_primaryUrl.text}}, // プライマリURL Contact: {{i_contact.text}}, // 連絡先 Project_Quote: {{i_projectQuote.text}}, // プロジェクトの引用 Status: {{i_status.selectedValue}}, // 状態 } // バリデーションに役立つ他の値 var uniqueTitle = {{q_searchDuplicateProject.result.[0].hits.length}} ? false : true // タイトルが一意であるかどうかを判定 // -------------------- // 変数の初期化 // -------------------- // アクションが無効化すべきかどうかを判断するグローバルフラグ var disable = false; // ユーザーに表示するメッセージ var messages = []; // 'Save'ボタンに表示するテキスト var button_text = "Save " + (inputs.Project_Title ? inputs.Project_Title : "") // -------------------------------- // フォームのバリデーションチェックを実装 // -------------------------------- // すべての新規プロジェクトについて、タイトルが既に使用されていないかチェックする if ({{i_newProjectToggle.selectedValue}} && !uniqueTitle){ disable = true; messages.push(`タイトルは一意でなければなりません。"${{{i_projectTitle.text}}}"はすでに存在します。`) } // 新規プロジェクトについて、タイトルが既に主キーでないかチェックする if ({{i_newProjectToggle.selectedValue}} && {{q_checkUniquePrimaryKey.result.[0].rows.length}}){ disable = true; messages.push(`このタイトルは既存のプロジェクトと競合します。プロジェクトは"${{{q_checkUniquePrimaryKey.result.[0].rows.[0].primaryKey.project_id}}}"として作成され、現在は "${{{q_checkUniquePrimaryKey.result.[0].rows.[0].columns.title}}}"というタイトルになっています。`) } // すべての必須フィールドに値があるかチェックする if (!(inputs.Project_Title && inputs.Contact && inputs.Primary_URL && inputs.Project_Quote && inputs.Status )){ disable = true; messages.push("すべての必須フィールドを完了してください。"); } const email_regex = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; if (inputs.Contact && !email_regex.test(inputs.Contact)){ messages.push('"連絡先のメール"に有効なメールアドレスを入力してください') disable = true; } // プロジェクトの説明の文字数をチェックする if (inputs.Project_Quote && inputs.Project_Quote.length > 140){ messages.push ("'プロジェクトの引用'は140文字以下である必要があります。"); disable = true; } // ボタンのSaveとUpdateのテキストを動的に変更 if (inputs.Project_Title && uniqueTitle) { button_text = "Update " + inputs.Project_Title } return { inputs, disable, messages, button_text }

この関数の出力を使用して、w_submit ボタンの disabled プロパティを {{f_validate.disable}} にテンプレート化することができます。また、エラーメッセージを赤い警告として表示するための単純なテキストウィジェットも設定できます。

Copied!
1 2 3 4 5 6 <!-- 以下のコードは、f_validateForm.messagesに含まれる各メッセージを取り出し、HTMLのdivタグ内に表示します --> {{#each f_validateForm.messages}} <!-- 'pt-tag pt-intent-danger pt-minimal'というクラスを持つdivタグを作成します。この中にメッセージが表示されます --> <div class='pt-tag pt-intent-danger pt-minimal'>{{this}}</div> {{/each}} <!-- すべてのメッセージが表示されたら、ループを終了します -->

これは非常に単純な例で、特定のウィジェットの横に表示されるチェックごとに固有のメッセージを持たせたり、特定のウィジェットの表示を制御するクラスを適用したりできるように拡張することが容易です。例えば、invalidクラスを適用して、CSSで入力ヘッダーを赤にすることができます。

"ダイナミックな入力"

時には、静的な入力ウィジェットには収まりきらない入力を取得する必要があります。多くの場合、問題を逆転させてシンプルな解決策を見つけることができますが、ユーザーにより複雑なフィルター処理を構築させる必要があるとしたら、またはユーザーのユースケースが静的な入力の創造的な使用でどうしてもできないとしたらどうでしょうか。

このワークフローには Repeating Container ウィジェットを使用したいと思うかもしれませんが、これらのウィジェットは表示のみに限定されています。Repeating Containerの中に入力ウィジェットを使用することはできますが、それはそのコンテナに対して のみ スコープされ、その入力ウィジェットのインスタンスをコンテナ内の他のウィジェットからしか参照できません。

代わりに、入力の単一のインスタンスを持つことができますが、ユーザーに複数回の選択を許すことで、ユーザーは自分の選択を"保存"することができます。累積した選択をHTMLウィジェットで表示し、以前の選択を削除する機能を組み込むことも可能です。

このパターンを実装するには、変数 v_userSelections を空の配列 ([]) に設定します。次に、ユーザーが選択を保存するボタンをクリックするたびに、この変数を更新できます:

w_saveSelection.clickv_userSelections.set

Copied!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // 既存の選択肢を取得する const userSelections = {{v_userSelections}} // 入力ウィジェットの現在の状態から新しい選択肢を取得する const newSelection = { // 主要カテゴリを取得する primaryCategory = {{w_primaryCategory.selectedValue}}, // 副カテゴリを取得する secondaryCategories = {{w_secondaryCategory.selectedValues}} ... } // 新しい選択肢を以前の選択肢と組み合わせる const userSelections.push(newSelection); // 変数の値を更新する return userSelections;

次に、さらなるイベントを実装して、ユーザーが既存の選択肢を操作できるようにすることができます。最も一般的なのは、それぞれを削除できるように「削除」アクションを持たせることです。

このパターンを使用すると、ユーザーは任意の複雑さの入力条件のセットを作成できます。複雑さが急増することに注意し、上記のイベントセクションで、イベント重視のパターンに関する注意事項をさらに読んでください。

一般的に、このパターンは、以下で詳しく述べられている一般的なステートフルアプリケーションパターンの特定のケースです。

相互依存するフィルター処理

依存関係グラフの性質上、ウィジェットが循環関係で互いに依存するように設定することはできません。入力ウィジェット間の依存関係は「滲み込む」必要があり、任意のフィルター処理選択がすべてのウィジェットに影響を与えるフィルターセットを構築することはできません。