從 Oracle JET 虛擬 DOM App 中的 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 敘述句。

       import { h, ComponentProps } from "preact";
       . . .
       // import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider");
       import { RESTDataProvider } from "ojs/ojrestdataprovider";
       . . .
    
    
  8. Props 類型別名中,修改選擇性的 data 特性以參照 RESTDataProvider 類型,而非現有的類型 MutableArrayDataProvider<Activity["id"], Activity>

       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. 在參照 RESTDataProvideractivityDataProvider 變數中,新增 error 選項及其所呼叫回呼函數的參照 (fetchErrorHandler)。

    const ParentContainer1 = () => {
    
       const activityDataProvider = useMemo(() => new RESTDataProvider<Activity["id"], Activity>({
          keyAttributes: keyAttributes,
          url: restServerURLActivities,
          error: fetchErrorHandler,
          transforms: {
          . . .
    
  3. activityDataProvider 變數之前,新增 fetchErrorHandler 的程式碼,以及我們用來判斷是否嘗試擷取資料的鉤點 (useStateuseRef)。

    . . .
    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 類型別名之後,建立一個 baseServiceUrl 變數來參照您將傳送至下一個步驟中建立之 RESTDataProvider 執行處理的 REST 端點。

    type Item = {
       . . .
     };
    
    const baseServiceUrl =
      "https://apex.oracle.com/pls/apex/oraclejet/lp/activities/";
    
  4. 建立 RESTDataProvider 的初始例項,您會在後續步驟中傳遞至 useStateuseEffect Preact 鉤點。

       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 敘述句。

    import { h, ComponentProps } from "preact";
    . . .
    // import MutableArrayDataProvider = require("ojs/ojmutablearraydataprovider");
    import { RESTDataProvider } from "ojs/ojrestdataprovider";
    . . .
    
  10. Props 類型別名中,修改 data 特性以參照 RESTDataProvider 類型,而非現有的類型 MutableArrayDataProvider<Activity["id"], Activity>

    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 應用程式會顯示下列訊息,因為其嘗試存取的 REST 服務僅接受 ojet serve 命令預設使用之伺服器連接埠 (8000) 的要求,因此 RESTDataProvider 嘗試從 REST 服務擷取失敗。

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

下一步

繼續本模組的下一個教學課程。

本教學課程是 CRUD Operations Using a REST Service 模組的一部分。

您可以返回虛擬 DOM 學習路徑的主頁面,存取建置虛擬 DOM 應用程式的所有模組。

其他學習資源

docs.oracle.com/learn 上探索其他實驗室,或在 Oracle Learning YouTube 頻道上存取更多免費學習內容。此外,請造訪 education.oracle.com/learning-explorer 以成為 Oracle Learning Explorer。

如需產品文件,請造訪 Oracle Help Center