レコードを表示するための UI の作成
学習の目的
- ユーザインターフェース API に対する要求を実行してレコードデータとメタデータを取得する。
- フォーム要素、レイアウト種別、およびアクセスモードを要求する理由と方法を理解する。
- 子レコードを要求する理由と方法を理解する。
レコードの取得
ユーザインターフェース API は、UI の表示に必要なものがまとめて提供されるという点でも優れています。UI API に何回 HTTP 要求を実行すれば、Universal Containers レコードを表示するのに必要なオブジェクトメタデータ、レイアウトメタデータ、項目データを取得できるでしょうか? 1 つ
/ui-api/record-ui/{recordIds}
Record Viewer コードで、要求がどうなっているかを見てみましょう。/client-src/sagas/recordFetcher.js ファイルに移動します。GitHub またはローカルマシン上で見つけることができます。
let recordViewUrl = action.creds.instanceUrl + '/services/data/v48.0/ui-api/record-ui/' + action.recordId + '?formFactor=' + action.context.formFactor + '&layoutTypes=Full&modes=View,Edit';
要求は 1 つのレコード ID action.recordId を送信します。これは、ユーザが [Recent Items (最近使ったデータ)] リストから選択するレコードです(エンドポイントでは複数のレコード ID がサポートされますが、Record Viewer アプリケーションで要求するのは 1 つだけです)。
応答にはレイアウト情報が含まれます。この情報から、項目がどこに移動し、どの UI セクションにあるかがわかります。また、どのセクションが折りたたまれているか (レイアウトユーザステート) もわかります。
レイアウト情報に何を含めるかを指定するために、要求ではパラメータ formFactor (フォーム要素)、layoutTypes (レイアウト種別)、modes (モード) を使用します。
フォーム要素は項目のレイアウトを変更します。アプリケーションが実行されているハードウェアの種類と一致するフォーム要素を選択します。Large (大) はデスクトップクライアント用のレイアウトです。Medium (中) はタブレット用のレイアウト、Small (小) はスマートフォン用のレイアウトです。大と中のフォーム要素では、セクションは 2 列レイアウトになります。小のフォーム要素では、セクションは 1 列レイアウになります。
レイアウト種別 によって、返される項目の数が決まります。有効なレイアウト種別は、Full (フル) と Compact (コンパクト) です。デフォルトのフルレイアウトには、現在のユーザに割り当てられているページレイアウトからすべての項目が含まれます。コンパクトレイアウトには、現在のユーザに割り当てられているコンパクトレイアウトからすべての項目が含まれます。どちらのレイアウトも [Setup (設定)] で編集できます。レイアウト種別に関係なく、応答には、ユーザにアクセス権がある項目のみが含まれます。
モードは、ユーザが実行しているタスクに対応して Create (作成)、View (表示)、Edit (編集) のいずれかにします。レイアウト情報は、モードに応じて異なります。たとえば、作成モードでは、レイアウトには [System Information (システム情報)] セクション ([Created By (作成者)] や [Last Modified By (最終更新者)] のような項目が含まれる) は含まれません。
Record Viewer アプリケーションは表示モードと編集モードを要求するため、ユーザが [Edit (編集)] をクリックした場合、アプリケーションにはすでに UI を表示するのに必要な情報があります。
要求の送信とグローバルステートの更新
RecordViewer アプリケーションは REST 要求を UI API エンドポイントに送信し、その情報で Redux ステートを非同期に更新します。それを待機していた React コンポーネントツリーは、ステート変更に反応し、UI を更新します。
Redux saga はアプリケーションの REST 要求を管理します。/ui-api/record-ui/{recordIds} 要求は、recordFetcher.js saga を使用して実行されます。このコードは REST URL を作成し、OAuth アクセストークンをベアラとして使用して要求を発行します。また、X-Chatter-Entity-Encoding ヘッダーを false に設定して、返される JSON 内の特殊文字が HTML エスケープ処理されないようにします。
応答が返され、JSON が正常に解析されると、saga は JSON 応答が含まれる RECEIVE_RECORD アクションを送信して完了します。Redux モデルでは、アクションは、グローバルステートを変更する意図を表明するために発行されます。操作を外部要求の実行、ステートの変更、コンポーネントの更新に分離することで、ゆるやかな結合が維持されます。
/* /reducers/record.js */ case 'RECEIVE_RECORD': return { record: recordLayout.getLayoutModel(action.recordId, action.record), mode: 'View' }
JSON ペイロードは、action.record に保持されます。変更後の新しいステートは、state.record を介してコンポーネントで使用できます。record.js レデューサーからのステートのこの部分は、reducers/index.js のグローバルステートにまとめられます。
JSON 応答の解析とレコードの表示
次は、record.js ステートレデューサーで使用されている recordLayout.getLayoutModel() ヘルパーの内部を見てみましょう。このヘルパーは、/ui-api/record-ui/{recordIds} JSON 内のレコードデータ、レイアウト、オブジェクト情報を調整してデータ構造を作成します。このデータ構造が、アプリケーションの React コンポーネントを駆動するステートになります。
- レイアウト情報
- UI で項目値に加えられた変更を追跡するために使用する editValues の対応付け
- 手元のレコードに対応する 1 つの objectInfo
- レコード ID
recordLayout.getLayoutModel() 関数は、レイアウトをループしながらレイアウト情報を処理します。応答の各レイアウトのセクションごとに、getLayoutSectionModel() をコールします。このメソッドは、セクション JSON からセクションヘッダー情報を取得します。次に、セクション内の各行をループしながら、各行内でレイアウトの各項目をループします。
- ネストされた値のリスト (住所のような項目には複数の値がある可能性がある)
- リンク情報 (他のレコードへのリンク)
- カスタムリンク情報 (外部 URL へのリンク)
各値には、テキストとして表示するもの、表示ラベル、項目メタデータ、編集可能性、および対応する UI API 選択リストの URL (該当する場合) に関する情報が保持されます。
} else if (record.fields[compValue]) { var displayValue = record.fields[compValue].displayValue; let rawValue = record.fields[compValue].value; if (displayValue == null && rawValue != null) { displayValue = rawValue.toString(); } values.push( {displayValue: displayValue, value: rawValue, label:component.label, field:compValue, fieldInfo, picklistUrl, editableForNew:item.editableForNew, editableForUpdate:item.editableForUpdate, isNull:displayValue == null})
レイアウトコンポーネントの表示
ここまで学習したように、recordLayout.getLayoutModel() から返されたオブジェクトは、グローバルステートに state.record.record として保存されます。このステートは、UI に対応するコンポーネントを表示するのに使用されます。最上位コンテナの RecordViewerWrapper.js は、state.record.record が使用可能かどうかを確認し、record プロパティ内のネストされた RecordViewer.js コンポーネントに渡します。RecordViewer.js コンポーネント内にネストされた RecordSection.js および RecordRow.js は、そのセクションと行を表示します。
{ item.values.map((component, i) => { if (component.displayValue && component.displayValue.length > 0) { if (component.fieldInfo.htmlFormatted) { return ( <div key={'component' + itemLabel + ',' + i} dangerouslySetInnerHTML={{__html: component.displayValue}}></div> ) } else { return ( <div key={'component' + itemLabel + ',' + i}>{component.displayValue}</div> ) } } else { return null } } )}
結果として、アプリケーションの動作は、UI API から返されたレイアウトとオブジェクト情報によって動的に進みます。レイアウトに項目と項目位置をハードコード化する代わりに、UI API を使用して、システム管理者が Salesforce でレイアウトやオブジェクトを更新したら自動的に再配置可能な UI を作成しましょう。