9 Oracle JET RESTデータ・プロバイダAPIの使用

Oracle JETのRESTデータ・プロバイダAPI (RESTDataProviderおよびRESTTreeDataProvider)を使用して、JSONベースのRESTサービスに問合せを送信し、結果データをアプリケーションにフェッチします。

RESTDataProviderはOracle JETのDataProviderインタフェースを実装し、RESTTreeDataProviderTreeDataProviderインタフェースを実装します。RESTDataProviderは、フェッチAPIを使用してJSONベースのRESTサービスからデータをフェッチする基本のAPIであるため、最初にこちらから説明します。RESTTreeDataProviderは、RESTDataProvider上に構築され、階層データを取得します。

Oracle JET RESTデータ・プロバイダについて

JSONベースのRESTサービスに問合せパラメータを送信する場合はRESTDataProviderを使用して、問合せに一致するデータのサブセットがすべてのデータではなくJETアプリケーションに返されるようにします。

たとえば、部門のメンバーなど特定の条件に一致する従業員のリストをアプリケーションで表示する場合に使用します。条件に一致するデータ行を表示する前に、返されるデータを問い合せるJETアプリケーションに従業員の完全なリストをフェッチできます。不要なデータの要求を送信するため、これは非効率的です。かわりに、RESTDataProviderのインスタンスを使用して、適切なデータのサブセットを返すRESTサービスに対するリクエストを作成します。

RESTDataProviderは、RESTDataProviderインスタンスの初期化時に指定するプロパティ・オプションであるtransformsを使用してこれを実行します。RESTDataProviderには3つのtransformsプロパティ・オプションがあります。それらは、fetchFirstfetchByOffsetおよびfetchByKeysです。これらは、データ・プロバイダで使用可能な3つのフェッチ・メソッドに対応します。各プロパティ・オプションでは、次の関数を定義します:

  • request: REST APIへのフェッチAPIコールに使用するRequestオブジェクトを作成する関数。ここでページング、フィルタリングおよびソートに必要な問合せパラメータをURLに適用して、リクエストの作成に使用できます。必要に応じて、headersbodymethodなどのその他のリクエスト・オプションをリクエストに追加することもできます。
  • response: データおよびその他の関連値をレスポンス本文から抽出する関数。この関数は、少なくとも、タイプDの項目の配列(RESTDataProviderクラスに渡される汎用)であるdataプロパティを持つオブジェクトを返す必要があります。汎用タイプDは、ロードされたデータのエントリのタイプに対応します。たとえば、個人情報データの場合、{ name: string, age: number }というエントリのように、文字列および数値タイプのエントリがある場合があります。

Oracle JET Cookbookには、RESTDataProviderの使用法を示す一連のデモが含まれています。概要デモでは、リクエストおよびレスポンスの機能を含むfetchFirstメソッドを使用してRESTDataProviderのインスタンスを初期化する方法を示します。Oracle JET CookbookのRESTデータ・プロバイダに関するページ、およびOracle® JavaScript Extension Toolkit (JET) Oracle JET APIリファレンス「RESTデータ・プロバイダ」を参照してください。

Oracle JET RESTツリー・データ・プロバイダについて

JSONベースのRESTサービスから階層データを取得する場合は、RESTTreeDataProviderを使用します。

RESTDataProviderRESTTreeDataProviderの主な違いは、RESTTreeDataProviderでは、追加のインスタンス・メソッドgetChildDataProviderと、同じくgetChildDataProviderという名前のコンストラクタ・メソッドが公開されることです。これらのシグネチャは異なります。

  • インスタンス・メソッドのシグネチャ: getChildDataProvider(parentKey: K): TreeDataProvider<K, D> | null
  • コンストラクタ・オプションのシグネチャ: getChildDataProvider(item: Item<K, D>): DataProvider<K, D> | null

getChildDataProviderメソッドは、親ノードのキーを引数として取り、それに対して子データ・プロバイダを作成します。これは、その後、親ノードの子をロードするRESTTreeDataProviderインスタンス、またはnull (ノードがリーフ・ノードの場合)を返します。リーフ・ノードは、子ノードを持つことができないノードです。

getChildDataProviderに渡す親ノードのキーが、以前にフェッチされたアイテムに対応していない場合、またはメタデータがリーフ・ノードとして識別するアイテムである場合、getChildDataProvidernullを返します。getChildDataProvider(parentKey: K)へのコールは、parentKeyに対応するアイテムを取得した後、内部でgetChildDataProvider(item: Item<K, D>)をコールすることに注意してください。したがって、これは、ノードのキーに対応するアイテムのメタデータにアクセスするコンストラクタ・オプションです。コンストラクタ・オプションはJETアプリケーションによって定義されるため、これは、RESTTreeDataProviderではなく、nullDataProviderインスタンスのどちらを返すかを決定するJETアプリケーションです。このフローはフェッチ・レスポンス変換を介してメタデータを返すJETアプリケーションから始まりますが、フロー全体が、ノードに子があるかどうかをアプリケーションが判断するために十分な情報を提供するメカニズムです。

Oracle JETクックブックでは、リーフ・フィールド名が、レスポンス変換を介してクックブックのモック・サーバーで提供されるメタデータに基づいていることにも注意してください。RESTサービスでは、別のフィールド名が使用される可能性があります。

Oracle JETクックブックのモック・サーバーによって使用されるメタデータの型は、{ key: K, leaf: boolean}[]です。これは、対応するノードがリーフ・ノードであるかどうかを示します。これは、getChildDataProviderを起動するときに便利です。親ノードがリーフ・ノードの場合は、nullを返します。それ以外の場合、ノードが子ノードを持つことができなくても、oj-tree-viewコンポーネントに展開矢印付きでノードがレンダリングされます。

もう1つ注意することは、RESTTreeDataProviderの作成操作ではparentKeysオプションを使用する必要があることです。これは、RESTツリー・データ・プロバイダに関するOracle JETクックブック・エントリのイベント・デモのaddChildNodeメソッドで確認できます。parentKeysオプションの詳細は、Oracle® JavaScript Extension Toolkit (JET) Oracle JET APIリファレンスデータ・プロバイダの追加操作イベントの詳細に関する項を参照してください。

Oracle JETクックブックには、RESTTreeDataProvider APIの使用法を示す一連のデモが含まれています。概要デモでは、getChildDataProviderメソッドを使用してRESTTreeDataProviderのインスタンスを作成する方法を示します。「RESTツリー・データ・プロバイダ」を参照してください。Oracle JETクックブックのデモの他に、Oracle® JavaScript Extension Toolkit (JET) Oracle JET APIリファレンスRESTツリー・データ・プロバイダに関する項も参照してください。

Oracle JET RESTデータ・プロバイダを使用したCRUDアプリケーションの作成

Oracle JET RESTデータ・プロバイダAPIを使用して、RESTサービスAPIから返されたデータに対してCRUD (作成、読取り、更新、削除)操作を実行するアプリケーションを作成します。

リリース11より前に使用することを推奨した共通モデルおよびコレクションAPIとは異なり、RESTDataProviderおよびRESTTreeDataProviderにはCollection.removeModel.destroyなどのメソッドは含まれていません。かわりに、フェッチAPIと適切なHTTPリクエスト・メソッドを使用して、適切な操作を実行するためにリクエストをRESTサービスに送信します。このステップとともに、データ・プロバイダのmutateメソッドを使用して、データ・プロバイダ・インスタンスを更新します。

ノート:

この後のステップで、具体的にRESTDataProviderを参照していますが、この一般的なステップはRESTTreeDataProviderにも適用できます。RESTツリー・データ・プロバイダに関するOracle JETクックブック・エントリのイベント・デモを表示して、RESTTreeDataProviderを使用するCRUDタイプの機能の実装を確認します。Oracle JETクックブックのデモではモックRESTサーバーを使用するため、実際のRESTサービスでの操作やレスポンスが、クックブックのデモンストレーションとは異なる場合があります。

RESTデータ・プロバイダのデータ・モデルの定義

アプリケーションのデータ・ソースを識別して、データ・モデルを作成します。

  1. データ・ソースを識別して、データを調査します。RESTサービスから発生するデータについて、サービスURLを識別し、ブラウザでナビゲートします。

    次の例は、部門データのRESTサービスに送信されたリクエストのレスポンス本文のサンプルを示しています。

    [
      {
        "DepartmentId": 20,
        "DepartmentName": "HR",
        "LocationId": 200,
        "ManagerId": 300,
        "Date": "01 Oct 2015"
      },
      {
        "DepartmentId": 100,
        "DepartmentName": "Facility",
        "LocationId": 200,
        "ManagerId": 300,
        "Date": "13 Oct 2002"
      }
    ]
    

    この例では、各部門がDepartmentId属性によって識別されます。

  2. RESTデータ・プロバイダのインスタンスを作成し、RESTサービスからデータをフェッチするコードをアプリケーションに追加します。
    import { RESTDataProvider } from "ojs/ojrestdataprovider";
    import "ojs/ojtable";
    
    type D = { DepartmentId: number; DepartmentName: string; Date: string };
    type K = D["DepartmentId"];
    
    class ViewModel {
    
        dataprovider: RESTDataProvider<K, D>;
        keyAttributes = "DepartmentId";
        url = "https://restServiceURL/departments";
    
        constructor() {
            this.dataprovider = new RESTDataProvider({
                keyAttributes: this.keyAttributes,
                url: this.url,
    
                transforms: {
                    fetchFirst: {
                        request: async (options) => {
                            const url = new URL(options.url);
                            const { size, offset } = options.fetchParameters;
                            url.searchParams.set("limit", String(size));
                            url.searchParams.set("offset", String(offset));
                            return new Request(url.href);
                        },
                        response: async ({ body }) => {
                            const { items } = body;
                            // If the response body returns, for example, "items". 
                            // We need to assign "items" to "data"
                            return { data: items };
                        },
                    },
                },
            });
        }
    }
    
    export = ViewModel;

レコードの読取り

レコードを読み取るには、アプリのページでレコードを読み取るOracle JET要素を定義します。

次のサンプル・コードは、oj-table要素と、dataproviderを参照するdata属性を使用してレコードの表を表示するhtmlファイルの一部を示しています。この例では、table要素によってDepartment IdDepartment Nameなどの列が作成されます。

<oj-table id="table" data="[[dataprovider]]"
      columns='[{"headerText": "Department Id",
		 "field": "DepartmentId"},
		{"headerText": "Department Name",
		 "field": "DepartmentName"},
		{"headerText": "Location Id",
		 "field": "LocationId"},
		{"headerText": "Manager Id",
		 "field": "ManagerId"}]'>
</oj-table>

レコードの作成

新規レコードを作成する機能を追加するには、ユーザーからの入力を受け入れて、新規レコードをRESTサービスに送る関数を作成する要素をHTMLページに追加します。

  1. ユーザーからの入力を受け入れる要素をアプリケーションのHTMLページに追加します。

    次の例のコードでは、oj-input-*要素をHTMLページに追加して、ユーザーが部門の新しいエントリを作成できるようにします。

    <oj-form-layout readonly="false" colspan-wrap="wrap" max-columns="1">
      <div>
        <oj-input-number id="departmentIdInput" label-hint="Department Id" value="{{inputDepartmentId}}"></oj-input-number>
        <oj-input-text id="departmentNameInput" label-hint="Department Name" value="{{inputDepartmentName}}"></oj-input-text>
        <oj-input-number id="locationIdInput" label-hint="Location Id" value="{{inputLocationId}}const addedRowKey = addedRow[this.keyAttributes]"></oj-input-number>
        <oj-input-number id="managerIdInput" label-hint="Manager Id" value="{{inputManagerId}}"></oj-input-number>
      </div>
      <div>
        <oj-button id="addButton" on-oj-action="[[addRow]]" disabled="[[disabledAdd]]">Create</oj-button>
        . . . 
      </div>
    </oj-form-layout>

    oj-buttonon-oj-action属性は、次のステップで定義するaddRow関数にバインドされます。

  2. ViewModelにコードを追加して、ユーザーの入力を新規リクエストとしてRESTサービスに送信し、mutateメソッドを使用してRESTDataProviderインスタンスを更新します。
    // add to the observableArray
      addRow = async () => {
        // Create row object based on form inputs
        const row = {
          DepartmentId: this.inputDepartmentId(),
          DepartmentName: this.inputDepartmentName(),
          LocationId: this.inputLocationId(),
          ManagerId: this.inputManagerId(),
        };
        // Create and send request to REST service to add row
        const request = new Request(this.restServerUrl, {
          headers: new Headers({
            "Content-type": "application/json; charset=UTF-8",
          }),
          body: JSON.stringify(row),
          method: "POST",
        });
        const response = await fetch(request);
        const addedRow = await response.json();
        // Create add mutate event and call mutate method
        // to notify dataprovider consumers that a row has been
        // added
        const addedRowIndex = addedRow.index;
        delete addedRow.index;
        const addedRowKey = addedRow[this.keyAttributes];
        const addedRowMetaData = { key: addedRowKey };
        this.dataprovider.mutate({
          add: {
            data: [addedRow],
            indexes: [addedRowIndex],
            keys: new Set([addedRowKey]),
            metadata: [addedRowMetaData],
          },
        });
      };

レコードの更新

レコードを更新する機能を追加するには、ユーザーからの入力を受け入れて、更新したレコードをRESTサービスに送る関数を作成する要素をHTMLページに追加します。

  1. 更新可能な要素を識別し、ユーザーが要素の更新アクションを実行できるようにする要素をアプリケーション・ページに追加します。

    次の例のコードでは、oj-input-*要素をHTMLページに追加して、ユーザーが選択した部門エントリを更新できるようにします。

    <oj-form-layout readonly="false" colspan-wrap="wrap" max-columns="1">
      <div>
        <oj-input-number id="departmentIdInput" label-hint="Department Id" value="{{inputDepartmentId}}"></oj-input-number>
        <oj-input-text id="departmentNameInput" label-hint="Department Name" value="{{inputDepartmentName}}"></oj-input-text>
        <oj-input-number id="locationIdInput" label-hint="Location Id" value="{{inputLocationId}}"></oj-input-number>
        <oj-input-number id="managerIdInput" label-hint="Manager Id" value="{{inputManagerId}}"></oj-input-number>
      </div>
      <div>
        <oj-button id="addButton" on-oj-action="[[updateRow]]" disabled="[[ disabledUpdate]]">Update</oj-button>
        . . . 
      </div>
    </oj-form-layout>
    

    oj-buttonon-oj-action属性は、次のステップで定義するupdateRow関数にバインドされます。

  2. ViewModelにコードを追加して、ユーザーの更新を新規リクエストとしてRESTサービスに送信し、mutateメソッドを使用してRESTDataProviderインスタンスを更新します。
    // used to update the fields based on the selected row
      updateRow = async () => {
        const currentRow = this.selectedRow;
        if (currentRow != null) {
          // Create row object to update based on form inputs
          const row = {
            DepartmentId: this.inputDepartmentId(),
            DepartmentName: this.inputDepartmentName(),
            LocationId: this.inputLocationId(),
            ManagerId: this.inputManagerId(),
          };
          // Create and send request to update row on the REST service
          const request = new Request(
            `${this.restServerUrl}/${this.selectedKey}`,
            {
              headers: new Headers({
                "Content-type": "application/json; charset=UTF-8",
              }),
              body: JSON.stringify(row),
              method: "PUT",
            }
          );
          const response = await fetch(request);
          const updatedRow = await response.json();
          const updatedRowIndex = updatedRow.index;
          delete updatedRow.index;
          // Create update mutate event and call mutate method
          // to notify dataprovider consumers that a row has been
          // updated
          const updatedRowKey = this.selectedKey;
          const updatedRowMetaData = { key: updatedRowKey };
          this.dataprovider.mutate({
            update: {
              data: [updatedRow],
              indexes: [updatedRowIndex],
              keys: new Set([updatedRowKey]),
              metadata: [updatedRowMetaData],
            },
          });
        }
      };

レコードの削除

レコードを削除する機能を追加するには、ユーザーからの入力を受け入れて、削除するレコードをRESTサービスに送る関数を作成する要素をHTMLに追加します。

  1. 削除対象としてマークされたレコードを識別し、ユーザーが要素の削除アクションを実行できるようにする要素をアプリケーション・ページに追加します。

    次の例のoj-buttonon-oj-action属性は、次のステップで定義するremoveRow関数にバインドされます。

    <oj-button id="removeButton" on-oj-action="[[removeRow]]" disabled="[[disabledRemove]]">Remove</oj-button>
  2. ユーザーによって発行されたレコード(1つまたは複数)を削除するコードをViewModelに追加します。
    // used to remove the selected row
    removeRow = async () => {
      const currentRow = this.selectedRow;
      if (currentRow != null) {
        // Create and send request to delete row on REST server
        const request = new Request(
          `${this.restServerUrl}/${this.selectedKey}`,
          { method: "DELETE" }
        );
        const response = await fetch(request);
        const removedRow = await response.json();
        const removedRowIndex = removedRow.index;
        delete removedRow.index;
        // Create remove mutate event and call mutate method
        // to notify dataprovider consumers that a row has been
        // removed
        const removedRowKey = removedRow[this.keyAttributes];
        const removedRowMetaData = { key: removedRowKey };
        this.dataprovider.mutate({
          remove: {
            data: [removedRow],
            indexes: [removedRowIndex],
            keys: new Set([removedRowKey]),
            metadata: [removedRowMetaData],
          },
        });
      }
      this.disabledUpdate(true);
      this.disabledRemove(true);
    };