Oracle JET仮想DOMアプリケーションのREST APIからデータをフェッチします

はじめに

このチュートリアルでは、RESTサービスへのアクセス方法、Oracle JavaScript Extension Toolkit (Oracle JET)仮想DOMアプリケーションへの統合方法、およびユーザー・インタフェースのリスト・ビューへのデータのバインド方法について説明します。

目的

このチュートリアルでは、RESTDataProviderクラスのインスタンスを作成する方法を学習します。このクラスは、JSONベースのRESTサービスから使用可能なデータを表します。

前提条件

タスク1: Starter Virtual DOMアプリケーションのダウンロード

前の学習パスで作成したアプリケーションで引き続き作業する場合は、このタスクをスキップします。

  1. jet-virtual-dom-app-temp.zipの名前をJET-Virtual-DOM-app.zipに変更します。JET-Virtual-DOM-appディレクトリにコンテンツを抽出します。

  2. JET-Virtual-DOM-appディレクトリに移動し、Oracle JET仮想DOMアプリケーションをリストアします。

    npm install
    

    仮想DOMアプリケーションを使用する準備ができました。

タスク2: RESTサービスへのアクセス

「Apex」リンクをクリックして、アクティビティ・リソース・エンドポイントのRESTデータを表示します。

データには、様々な属性を持つアクティビティのリストが含まれます。

{
  "items": [
      {
      "id": 1,
      "name": "Baseball",
      "short_desc": "Equipment we carry for baseball players.",
      "image": "css/images/product_images/baseball.jpg"
      },
   . . .
   ],
   "hasMore": false,
   "limit": 25,
   "offset": 0,
   "count": 4,
   "links": [
      {
      "rel": "self",
      "href": "https://apex.oracle.com/pls/apex/oraclejet/lp/activities/"
      },
      . . .
   ]
}

エンドポイントが返すデータおよびプロパティについて理解します。このチュートリアルの後半でRESTDataProviderのインスタンスを作成するときに、これらの詳細を理解する必要があります。たとえば、エンドポイントが一連の個々のアクティビティを参照するitemsプロパティを返す方法に注意してください。

タスク3: アクティビティ・データをフェッチするデータ・プロバイダの作成

  1. JET-Virtual-DOM-app/src/components/ディレクトリに移動し、エディタでParentContainer1.tsxファイルを開きます。

  2. ParentContainer1.tsxファイルの先頭で、RESTDataProviderモジュールをインポートし、MutableArrayDataProviderモジュールおよびstore_data.jsonファイルのインポート文を削除またはコメント・アウトします。

    また、後でRESTDataProviderを作成するときに使用するuseMemoフックもインポートします。

    import { h } from "preact";
    . . .
    // import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider");
    // import * as storeData from "text!./store_data.json";
    import { RESTDataProvider } from "ojs/ojrestdataprovider";
    import { useState, useMemo } from "preact/hooks";
    . . .
    
  3. keyattributes変数およびアクティビティ・キー属性を参照するrestServerURLActivities変数と、次のステップで作成するRESTDataProviderインスタンスに渡すRESTエンドポイントを作成します。

    let keyAttributes: string = 'id';
    // REST endpoint that returns Activity data
    const restServerURLActivities: string =
      'https://apex.oracle.com/pls/apex/oraclejet/lp/activities/';
    
  4. RESTDataProviderモジュールを参照する新しいactivityDataProvider変数を作成し、MutableArrayDataProviderモジュールを参照する既存のactivityDataProvider変数を削除またはコメント・アウトします。

    ParentContainer1関数内に新しいactivityDataProvider変数を作成し、それをuseMemoフック内にラップして、データ・プロバイダのデータが実際に変更された場合にのみデータ・プロバイダ・インスタンスを再作成できるようにします。

    const ParentContainer1 = () => {
    
    const activityDataProvider = useMemo(() => new RESTDataProvider<Activity["id"], Activity>({
       keyAttributes: keyAttributes,
       url: restServerURLActivities,
       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, totalSize, hasMore } = body;
             return { data: items, totalSize, hasMore };
          },
          },
       },
     }), [])
    . . .
    

    ノート:エンドポイント・レスポンス本文からデータおよびその他のプロパティを抽出する前述のresponse関数は、dataプロパティを持つオブジェクトを返す必要があります。操作するエンドポイントがitemsプロパティを返すと、レスポンス関数で後者のプロパティをdataに割り当てます。

  5. ParentContainer1.tsxファイルを保存します。

    ParentContainer1.tsxファイルは、ParentContainer1-a.tsx.txtのようになります。

  6. JET-Virtual-DOM-app/src/components/Activityディレクトリに移動し、エディタでActivityContainer.tsxファイルを開きます。

  7. ActivityContainer.tsxファイルの先頭で、RESTDataProviderモジュールをインポートし、MutableArrayDataProviderモジュールのインポート文をコメント・アウトまたは削除します。

       import { h, ComponentProps } from "preact";
       . . .
       // import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider");
       import { RESTDataProvider } from "ojs/ojrestdataprovider";
       . . .
    
    
  8. Props型の別名で、オプションのdataプロパティを変更して、既存の型MutableArrayDataProvider<Activity["id"], Activity>ではなくRESTDataProvider型を参照します。

       type Props = {
          data?: RESTDataProvider<Activity["id"], Activity>;
          // data?: MutableArrayDataProvider<Activity["id"], Activity>;
       . . .
       };
    
  9. ActivityContainer.tsxファイルを保存します。

    ActivityContainer.tsxファイルは、ActivityContainer.tsx.txtのようになります。

タスク4: データのフェッチ失敗を管理するためのエラー・ハンドラの追加

RESTDataProviderインスタンスには、データのフェッチの試行が失敗したときにコールバック関数の呼出しに使用できるエラー・オプションがあります。この機能は、アクティビティのリストのフェッチに失敗するシナリオに対して実装します。

  1. JET-Virtual-DOM-app/src/components/ディレクトリのParentContainer1.tsxファイルで、PreactからuseRefフックをインポートします。

    . . .
    import { RESTDataProvider } from "ojs/ojrestdataprovider";
    import { useState, useMemo, useRef } from "preact/hooks";
    . . .
    
  2. RESTDataProviderを参照するactivityDataProvider変数に、errorオプションと、起動するコールバック関数(fetchErrorHandler)への参照を追加します。

    const ParentContainer1 = () => {
    
       const activityDataProvider = useMemo(() => new RESTDataProvider<Activity["id"], Activity>({
          keyAttributes: keyAttributes,
          url: restServerURLActivities,
          error: fetchErrorHandler,
          transforms: {
          . . .
    
  3. activityDataProvider変数の前に、fetchErrorHandlerのコードと、データのフェッチの試行が成功したかどうかを判断するために使用するフック(useStateおよびuseRef)を追加します。

    . . .
    const ParentContainer1 = () => {
    
    const [fetchStatus, setFetchStatus] = useState(true);
    const fetchError = useRef<string>();
    
    const fetchErrorHandler = (errorDetail: RESTDataProvider.FetchErrorDetail<number, Activity> |
                                            RESTDataProvider.FetchResponseErrorDetail<number, Activity>) => {
       setFetchStatus(false);
       if (errorDetail.hasOwnProperty('response')) {
          fetchError.current = `${(errorDetail as RESTDataProvider.FetchResponseErrorDetail<number, Activity>).response.status}`;
       }
       else {
          fetchError.current = (errorDetail as RESTDataProvider.FetchErrorDetail<number, Activity>).error.message;
       }
    }
    
    const activityDataProvider = new RESTDataProvider<Activity["id"], Activity>({
    . . .
    
  4. ParentContainer1.tsxファイルの最後にあるreturn文に、データのフェッチの試行が失敗した場合に、アクティビティのリストを表示するか、メッセージを表示するかを決定するチェックを追加します。

    . . .
    return (
       <div>
          {fetchStatus ? (
          <div id="parentContainer1" class="oj-flex oj-flex-init">
             <ActivityContainer data={activityDataProvider} onActivityChanged={activityChangedHandler} />
             {showActivityItems() && (<ParentContainer2 activity={selectedActivity} />)}
             {!showActivityItems() && (<h4 class="oj-typography-subheading-sm">Select activity to view items</h4>)}
          </div>) :
          (<p>Sorry that we couldn't get your product information right now. Please contact your system administrator.</p>
          )}
       </div>
    );
    };
    
    export default ParentContainer1;
    
  5. ParentContainer1.tsxファイルを保存します。

    ParentContainer1.tsxファイルは、ParentContainer1-b.tsx.txtのようになります。

タスク5: 品目データをフェッチするデータ・プロバイダの作成

別のRESTDataProviderインスタンスを使用して、データのサブセット、特定のアクティビティのアイテムのリストをフェッチします。これを行うには、選択したアクティビティIDを含む新しいURLを指定します。

  1. JET-Virtual-DOM-app/src/components/ディレクトリに移動し、エディタでParentContainer2.tsxファイルを開きます。

  2. ParentContainer2.tsxファイルの先頭で、RESTDataProviderモジュールをインポートし、MutableArrayDataProviderモジュールおよびstore_data.jsonファイルのインポート文を削除またはコメント・アウトします。また、作成するRESTDataProviderインスタンスでフィルタリング機能を有効にするときに使用するTextFilterインタフェースもインポートします。

    import { h } from "preact";
    . . .
    import { RESTDataProvider } from "ojs/ojrestdataprovider";
    import { TextFilter } from "ojs/ojdataprovider";
    
    // import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider");
    // import * as storeData from "text!./store_data.json";
    . . .
    
    
  3. Item型の別名の後に、次のステップで作成するRESTDataProviderのインスタンスに渡すRESTエンドポイントを参照するbaseServiceUrl変数を作成します。

    type Item = {
       . . .
     };
    
    const baseServiceUrl =
      "https://apex.oracle.com/pls/apex/oraclejet/lp/activities/";
    
  4. 後続のステップでuseStateおよびuseEffect Preactフックに渡すRESTDataProviderの初期インスタンスを作成します。

       const baseServiceUrl = 'https://apex.oracle.com/pls/apex/oraclejet/lp/activities/';
    
       let INIT_DATAPROVIDER = new RESTDataProvider<ActivityItem['id'], ActivityItem>({
       keyAttributes: 'id',
       url: baseServiceUrl,
       transforms: {
          fetchFirst: {
             request: null!,
             response: (): any => {
             return { data: [] };
             },
          },
       },
       });
    
  5. store_data.jsonファイルからデータを読み取る変数を作成し、MutableArrayDataProviderの初期インスタンスを作成した既存のコードをコメント・アウトまたは削除します。

       // const activityData = JSON.parse(storeData);
       // let activityItemsArray = activityData[0].items;
    
       // // Create data provider instance for the array of activity items for the selected activity
       // const INIT_DATAPROVIDER = new MutableArrayDataProvider<ActivityItem["id"], ActivityItem>(activityItemsArray, {
       //   keyAttributes: "id",
       // })
    
  6. ParentContainer2ファンクションで、MutableArrayDataProviderのインスタンスを管理する既存のuseEffectフックを、選択したアクティビティIDに対応するアクティビティ・アイテムのRESTDataProviderを作成する新しい定義に置き換えます。この新しい定義には、アクティビティ・アイテムのnameフィールドでフィルタするテキスト・フィルタも含まれています。

    const ParentContainer2 = (props: Props) => {
    . . .
    useEffect(() => {
       setactivityItemDP(
          new RESTDataProvider<ActivityItem["id"], ActivityItem>({
          keyAttributes: "id",
          capabilities: {
             filter: {
                textFilter: true,
             },
          },
          url: baseServiceUrl + "/" + props.activity?.id + "/items/",
          textFilterAttributes: ["name"],
          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));
                const filterCriterion = options.fetchParameters
                   .filterCriterion as TextFilter<Item>;
                const { textFilterAttributes } = options.fetchOptions;
                if (
                   filterCriterion &&
                   filterCriterion.text &&
                   textFilterAttributes
                ) {
                   const { text } = filterCriterion;
                   textFilterAttributes.forEach((attribute) => {
                      url.searchParams.set(attribute, text);
                   });
                }
    
                return new Request(url.href);
                },
                response: async ({ body }) => {
                const { items, totalSize, hasMore } = body;
                return { data: items, totalSize, hasMore };
                },
             },
          },
          })
       );
    }, [props.activity]);
    
    return (
    . . .
    
  7. ParentContainer2.tsxファイルを保存します。

    ParentContainer2.tsxファイルは、ParentContainer2.tsx.txtのようになります。

  8. JET-Virtual-DOM-app/src/components/ActivityItemディレクトリに移動し、エディタでActivityItemContainer.tsxファイルを開きます。

  9. ActivityItemContainer.tsxファイルの先頭で、RESTDataProviderモジュールをインポートし、MutableArrayDataProviderモジュールのインポート文をコメント化または削除します。

    import { h, ComponentProps } from "preact";
    . . .
    // import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider");
    import { RESTDataProvider } from "ojs/ojrestdataprovider";
    . . .
    
  10. Props型の別名で、dataプロパティを変更して、既存の型MutableArrayDataProvider<Activity["id"], Activity>ではなくRESTDataProvider型を参照します。

    type Props = {
      // data?: MutableArrayDataProvider<ActivityItem["id"], ActivityItem>;
      data?: RESTDataProvider<ActivityItem['id'], ActivityItem>;
      selectedActivity: Item | null;
      onItemChanged: (item: Item) => void;
    };
    
  11. ActivityItemContainer.tsxファイルを保存します。

    ActivityItemContainer.tsxファイルは、ActivityItemContainer.tsx.txtのようになります。

タスク6: 仮想DOMアプリケーションのテスト

  1. ターミナル・ウィンドウで、JET-Virtual-DOM-appディレクトリに移動し、仮想DOMアプリケーションを実行します。

    npx ojet serve
    
  2. ブラウザ・ウィンドウで、仮想DOMアプリケーションの動的変更を表示します。

    「フェッチされたレコード」画面

  3. 実行中の仮想DOMアプリケーションを表示するブラウザ・ウィンドウまたはタブを閉じます。

  4. ターミナル・ウィンドウでCtrl+Cを押し、プロンプトが表示されたら、yと入力してOracle JETツール・バッチ・ジョブを終了します。

  5. 端末ウィンドウで、次の追加のコマンドライン引数を使用して仮想DOMアプリケーションを実行します。

    npx ojet serve --server-port=8144 --livereload-port=8145
    

    この場合、仮想DOMアプリケーションは、ojet serveコマンドがデフォルト(8000)で使用するサーバー・ポート上のリクエストのみにアクセスするようにトリスするRESTサービスによって受け入れられるため、RESTサービスからのフェッチがRESTDataProviderによって失敗したため、次のメッセージを表示します。

    Sorry that we couldn't get your product information right now. Please contact your system administrator.
    

次のステップ

このモジュールの次のチュートリアルに進みます。

このチュートリアルは、「RESTサービスを使用したCRUD操作」モジュールの一部です。

仮想DOM学習パスのメイン・ページに戻ると、仮想DOMアプリケーションの構築に関するすべてのモジュールにアクセスできます。

その他の学習リソース

docs.oracle.com/learnで他のラボを確認するか、Oracle Learning YouTubeチャネルで無料のラーニング・コンテンツにアクセスしてください。また、education.oracle.com/learning-explorerにアクセスして、Oracle Learning Explorerになります。

製品ドキュメントについては、Oracle Help Centerを参照してください。