建立使用AEM GraphQL API的React應用程式

在本章中,您將探索AEM的GraphQL API如何推動外部應用程式中的體驗。

使用簡單的React應用程式來查詢及顯示AEM的GraphQL API所公開的​ 團隊 ​和​ 人員 ​內容。 React的使用在很大程度上不重要,而且消費的外部應用程式可以寫入任何平台的任何框架中。

先決條件

我們已假設完成此多部分教學課程前幾部分所概述的步驟,或已在您的AEM as a Cloud Service作者和Publish服務上安裝basic-tutorial-solution.content.zip

本章中的​ IDE熒幕擷取畫面來自Visual Studio Code

必須安裝下列軟體:

目標

瞭解如何:

  • 下載並啟動範例React應用程式
  • 使用AEM Headless JS SDK查詢AEM的GraphQL端點
  • 查詢AEM以取得團隊及其參照成員的清單
  • 查詢AEM以取得團隊成員的詳細資訊

取得範例React應用程式

在本章中,使用與AEM的GraphQL API互動所需的程式碼實作一個草稿範例React應用程式,並顯示從這些API取得的團隊和人員資料。

範例React應用程式原始碼可在Github.com上的https://github.com/adobe/aem-guides-wknd-graphql/tree/main/basic-tutorial取得

若要取得React應用程式:

  1. Github.com複製範例WKND GraphQL React應用程式。

    code language-shell
    $ cd ~/Code
    $ git clone git@github.com:adobe/aem-guides-wknd-graphql.git
    
  2. 導覽至basic-tutorial資料夾,並在IDE中開啟。

    code language-shell
    $ cd ~/Code/aem-guides-wknd-graphql/basic-tutorial
    $ code .
    

    在VSCode中 React應用程式

  3. 更新.env.development以連線至AEM as a Cloud Service Publish服務。

    • REACT_APP_HOST_URI的值設為您的AEM as a Cloud Service Publish URL (例如 REACT_APP_HOST_URI=https://publish-p123-e456.adobeaemcloud.com)和REACT_APP_AUTH_METHOD的值至none
    note note
    NOTE
    請確定您已發佈專案設定、內容片段模型、編寫的內容片段、GraphQL端點,以及先前步驟中的持續查詢。
    如果您在本機AEM Author SDK上執行上述步驟,您可以指向http://localhost:4502並將REACT_APP_AUTH_METHOD的值指向basic
  4. 從命令列,前往aem-guides-wknd-graphql/basic-tutorial資料夾

  5. 啟動React應用程式

    code language-shell
    $ cd ~/Code/aem-guides-wknd-graphql/basic-tutorial
    $ npm install
    $ npm start
    
  6. React應用程式會在http://localhost:3000/上以開發模式啟動。 在教學課程中對React應用程式所做的變更會立即反映出來。

部分實作的React應用程式

IMPORTANT
此React應用程式已部分實作。 依照本教學課程中的步驟完成實作。 需要實施作業的JavaScript檔案有下列註解,請務必使用本教學課程中指定的程式碼,新增/更新這些檔案中的程式碼。
// ​**​**​**​**​**​**​**​**​**​**​**​**​**​***
// TODO請依照AEM Headless教學課程中的步驟來實作此專案
// ​**​**​**​**​**​**​**​**​**​**​**​**​**​***

React應用程式剖析

範例React應用程式有三個主要部分:

  1. src/api資料夾包含用來向AEM發出GraphQL查詢的檔案。

    • src/api/aemHeadlessClient.js會初始化並匯出用來與AEM通訊的AEM Headless使用者端
    • src/api/usePersistedQueries.js實作自訂React鉤點從AEM GraphQL傳回資料到Teams.jsPerson.js檢視元件。
  2. src/components/Teams.js檔案使用清單查詢來顯示團隊及其成員的清單。

  3. src/components/Person.js檔案使用引數化的單一結果查詢,顯示單一人員的詳細資料。

檢閱AEMHeadless物件

檢閱aemHeadlessClient.js檔案,瞭解如何建立用來與AEM通訊的AEMHeadless物件。

  1. 開啟src/api/aemHeadlessClient.js

  2. 複查明細行1-40:

    • AEM Headless Client for JavaScript匯入AEMHeadless宣告,第11行。

    • 根據.env.development、第14-22行中定義的變數以及箭號函式運算式setAuthorization、第31-40行所定義的授權組態。

    • 內含開發Proxy組態的serviceUrl設定,第27行。

  3. 第42到49行是最重要的,因為它們會將AEMHeadless使用者端例項化,並將其匯出以供在整個React應用程式中使用。

// Initialize the AEM Headless Client and export it for other files to use
const aemHeadlessClient = new AEMHeadless({
  serviceURL: serviceURL,
  endpoint: REACT_APP_GRAPHQL_ENDPOINT,
  auth: setAuthorization(),
});

export default aemHeadlessClient;

實作以執行AEM GraphQL持續查詢

若要實作泛型fetchPersistedQuery(..)函式以執行AEM GraphQL持續查詢,請開啟usePersistedQueries.js檔案。 fetchPersistedQuery(..)函式使用aemHeadlessClient物件的runPersistedQuery()函式以非同步、承諾型行為執行查詢。

稍後,自訂React useEffect連結呼叫此函式以從AEM擷取特定資料。

  1. src/api/usePersistedQueries.js中​ 更新 fetchPersistedQuery(..),第35行,代碼如下。
/**
 * Private, shared function that invokes the AEM Headless client.
 *
 * @param {String} persistedQueryName the fully qualified name of the persisted query
 * @param {*} queryParameters an optional JavaScript object containing query parameters
 * @returns the GraphQL data or an error message
 */
async function fetchPersistedQuery(persistedQueryName, queryParameters) {
  let data;
  let err;

  try {
    // AEM GraphQL queries are asynchronous, either await their return or use Promise-based syntax
    const response = await aemHeadlessClient.runPersistedQuery(
      persistedQueryName,
      queryParameters
    );
    // The GraphQL data is stored on the response's data field
    data = response?.data;
  } catch (e) {
    // An error occurred, return the error messages
    err = e
      .toJSON()
      ?.map((error) => error.message)
      ?.join(", ");
    console.error(e.toJSON());
  }

  // Return the GraphQL and any errors
  return { data, err };
}

實作團隊功能

接下來,建置可在React應用程式的主要檢視上顯示團隊及其成員的功能。 此功能需要:

  • src/api/usePersistedQueries.js中的新自訂React useEffect勾點,它會叫用my-project/all-teams持續查詢,並傳回AEM中的Team內容片段清單。
  • 位於src/components/Teams.js的React元件會叫用新的自訂React useEffect鉤點,並轉譯Teams資料。

完成後,應用程式的主要檢視會填入來自AEM的團隊資料。

團隊檢視

步驟

  1. 開啟src/api/usePersistedQueries.js

  2. 找到函式useAllTeams()

  3. 若要建立透過fetchPersistedQuery(..)叫用持續查詢my-project/all-teamsuseEffect連結,請新增下列程式碼。 此掛接也只會從data?.teamList?.items的AEM GraphQL回應傳回相關資料,讓React檢視元件與父JSON結構無關。

    code language-javascript
    /**
     * Custom hook that calls the 'my-project/all-teams' persisted query.
     *
     * @returns an array of Team JSON objects, and array of errors
     */
    export function useAllTeams() {
      const [teams, setTeams] = useState(null);
      const [error, setError] = useState(null);
    
      // Use React useEffect to manage state changes
      useEffect(() => {
        async function fetchData() {
          // Call the AEM GraphQL persisted query named "my-project/all-teams"
          const { data, err } = await fetchPersistedQuery(
            "my-project/all-teams"
          );
          // Sets the teams variable to the list of team JSON objects
          setTeams(data?.teamList?.items);
          // Set any errors
          setError(err);
        }
        // Call the internal fetchData() as per React best practices
        fetchData();
      }, []);
    
      // Returns the teams and errors
      return { teams, error };
    }
    
  4. 開啟src/components/Teams.js

  5. Teams React元件中,使用useAllTeams()勾點從AEM擷取團隊清單。

    code language-javascript
    import { useAllTeams } from "../api/usePersistedQueries";
    ...
    function Teams() {
      // Get the Teams data from AEM using the useAllTeams
      const { teams, error } = useAllTeams();
      ...
    }
    
  6. 執行以檢視為基礎的資料驗證,根據傳回的資料顯示錯誤訊息或載入指標。

    code language-javascript
    function Teams() {
      const { teams, error } = useAllTeams();
    
      // Handle error and loading conditions
      if (error) {
        // If an error ocurred while executing the GraphQL query, display an error message
        return <Error errorMessage={error} />;
      } else if (!teams) {
        // While the GraphQL request is executing, show the Loading indicator
        return <Loading />;
      }
      ...
    }
    
  7. 最後,轉譯團隊資料。 從GraphQL查詢傳回的每個團隊都會使用提供的Team React子元件轉譯。

    code language-javascript
    import React from "react";
    import { Link } from "react-router-dom";
    import { useAllTeams } from "../api/usePersistedQueries";
    import Error from "./Error";
    import Loading from "./Loading";
    import "./Teams.scss";
    
    function Teams() {
      const { teams, error } = useAllTeams();
    
      // Handle error and loading conditions
      if (error) {
        return <Error errorMessage={error} />;
      } else if (!teams) {
        return <Loading />;
      }
    
      // Teams have been populated by AEM GraphQL query. Display the teams.
      return (
        <div className="teams">
          {teams.map((team, index) => {
            return <Team key={index} {...team} />;
          })}
        </div>
      );
    }
    
    // Render single Team
    function Team({ title, shortName, description, teamMembers }) {
      // Must have title, shortName and at least 1 team member
      if (!title || !shortName || !teamMembers) {
        return null;
      }
    
      return (
        <div className="team">
          <h2 className="team__title">{title}</h2>
          <p className="team__description">{description.plaintext}</p>
          <div>
            <h4 className="team__members-title">Members</h4>
            <ul className="team__members">
              {/* Render the referenced Person models associated with the team */}
              {teamMembers.map((teamMember, index) => {
                return (
                  <li key={index} className="team__member">
                    <Link to={`/person/${teamMember.fullName}`}>
                      {teamMember.fullName}
                    </Link>
                  </li>
                );
              })}
            </ul>
          </div>
        </div>
      );
    }
    
    export default Teams;
    

實作人員功能

完成團隊功能後,讓我們實作這項功能以處理團隊成員或個人詳細資訊上的顯示。

此功能需要:

  • src/api/usePersistedQueries.js中的新自訂React useEffect勾點,它會叫用引數化my-project/person-by-name持續查詢,並傳回單一人員記錄。

  • 位於src/components/Person.js的React元件使用個人的全名作為查詢引數,叫用新的自訂React useEffect連結,並呈現個人資料。

完成後,在「團隊」檢視中選取人員名稱,即可呈現人員檢視。

人員

  1. 開啟src/api/usePersistedQueries.js

  2. 找到函式usePersonByName(fullName)

  3. 若要建立透過fetchPersistedQuery(..)叫用持續查詢my-project/all-teamsuseEffect連結,請新增下列程式碼。 此掛接也只會從data?.teamList?.items的AEM GraphQL回應傳回相關資料,讓React檢視元件與父JSON結構無關。

    code language-javascript
    /**
     * Calls the 'my-project/person-by-name' and provided the {fullName} as the persisted query's `name` parameter.
     *
     * @param {String!} fullName the full
     * @returns a JSON object representing the person
     */
    export function usePersonByName(fullName) {
      const [person, setPerson] = useState(null);
      const [errors, setErrors] = useState(null);
    
      useEffect(() => {
        async function fetchData() {
          // The key is the variable name as defined in the persisted query, and may not match the model's field name
          const queryParameters = { name: fullName };
    
          // Invoke the persisted query, and pass in the queryParameters object as the 2nd parameter
          const { data, err } = await fetchPersistedQuery(
            "my-project/person-by-name",
            queryParameters
          );
    
          if (err) {
            // Capture errors from the HTTP request
            setErrors(err);
          } else if (data?.personList?.items?.length === 1) {
            // Set the person data after data validation
            setPerson(data.personList.items[0]);
          } else {
            // Set an error if no person could be found
            setErrors(`Cannot find person with name: ${fullName}`);
          }
        }
        fetchData();
      }, [fullName]);
    
      return { person, errors };
    }
    
  4. 開啟src/components/Person.js

  5. Person React元件中,剖析fullName路由引數,並使用usePersonByName(fullName)勾點從AEM擷取人員資料。

    code language-javascript
    import { useParams } from "react-router-dom";
    import { usePersonByName } from "../api/usePersistedQueries";
    ...
    function Person() {
      // Read the person's `fullName` which is the parameter used to query for the person's details
      const { fullName } = useParams();
    
      // Query AEM for the Person's details, using the `fullName` as the filtering parameter
      const { person, error } = usePersonByName(fullName);
      ...
    }
    
  6. 執行以檢視為基礎的資料驗證,根據傳回的資料顯示錯誤訊息或載入指標。

    code language-javascript
    function Person() {
      // Read the person's `fullName` which is the parameter used to query for the person's details
      const { fullName } = useParams();
    
      // Query AEM for the Person's details, using the `fullName` as the filtering parameter
      const { person, error } = usePersonByName(fullName);
    
      // Handle error and loading conditions
      if (error) {
        return <Error errorMessage={error} />;
      } else if (!person) {
        return <Loading />;
      }
      ...
    }
    
  7. 最後,呈現人員資料。

    code language-javascript
    import React from "react";
    import { useParams } from "react-router-dom";
    import { usePersonByName } from "../api/usePersistedQueries";
    import { mapJsonRichText } from "../utils/renderRichText";
    import Error from "./Error";
    import Loading from "./Loading";
    import "./Person.scss";
    
    function Person() {
      // Read the person's `fullName` which is the parameter used to query for the person's details
      const { fullName } = useParams();
    
      // Query AEM for the Person's details, using the `fullName` as the filtering parameter
      const { person, error } = usePersonByName(fullName);
    
      // Handle error and loading conditions
      if (error) {
        return <Error errorMessage={error} />;
      } else if (!person) {
        return <Loading />;
      }
    
      // Render the person data
      return (
        <div className="person">
          <img
            className="person__image"
            src={process.env.REACT_APP_HOST_URI+person.profilePicture._path}
            alt={person.fullName}
          />
          <div className="person__occupations">
            {person.occupation.map((occupation, index) => {
              return (
                <span key={index} className="person__occupation">
                  {occupation}
                </span>
              );
            })}
          </div>
          <div className="person__content">
            <h1 className="person__full-name">{person.fullName}</h1>
            <div className="person__biography">
              {/* Use this utility to transform multi-line text JSON into HTML */}
              {mapJsonRichText(person.biographyText.json)}
            </div>
          </div>
        </div>
      );
    }
    
    export default Person;
    

試用應用程式

檢閱應用程式http://localhost:3000/並按一下​ 成員 ​連結。 您也可以在AEM中新增內容片段,以新增更多團隊和/或成員至團隊Alpha。

IMPORTANT
若要驗證您的實作變更,或是在上述變更後無法讓應用程式正常運作,請參閱basic-tutorial解決方案分支。

潛藏在引擎蓋下

開啟瀏覽器的​ 開發人員工具 > 網路 ​和​ 篩選器,以處理all-teams請求。 請注意,GraphQL API要求/graphql/execute.json/my-project/all-teams是針對http://localhost:3000而提出的,且​ NOT ​是針對REACT_APP_HOST_URI的值,例如<https://publish-pxxx-exxx.adobeaemcloud.com。 這些要求是針對React應用程式的網域所發出,因為Proxy設定是使用http-proxy-middleware模組來啟用。

透過Proxy的 GraphQL API要求

檢閱主要../setupProxy.js檔案,並在../proxy/setupProxy.auth.**.js個檔案中注意到/content/graphql路徑是如何進行代理的,並表明它不是靜態資產。

module.exports = function(app) {
  app.use(
    ['/content', '/graphql'],
  ...

使用本機Proxy不適合用於生產部署,您可以在​ 生產部署 ​區段找到更多詳細資料。

恭喜! congratulations

恭喜!您已成功建立React應用程式,以使用並顯示AEM的GraphQL API資料,作為基本教學課程的一部分!

recommendation-more-help
e25b6834-e87f-4ff3-ba56-4cd16cdfdec4