使用AEM的內容片段傳送OpenAPI建置React應用程式

在本章中,您將探索使用OpenAPI傳送AEM內容片段如何推動外部應用程式中的體驗。

一個簡單的React應用程式可用來請求和顯示透過OpenAPI的AEM內容片段傳遞公開的​ 團隊 ​和​ 人員 ​內容。 React的使用基本上不重要,只要可以向AEM as a Cloud Service發出HTTP請求,消費的外部應用程式便可以在任何平台的任何框架中撰寫。

先決條件

假設已完成此多部分教學課程前幾部分所概述的步驟。

必須安裝下列軟體:

目標

瞭解如何:

  • 下載並啟動範例React應用程式。
  • 使用OpenAPI叫用AEM內容片段傳送,以取得團隊及其參考成員的清單。
  • 使用OpenAPI叫用AEM內容片段傳送,以擷取團隊成員的詳細資訊。

在AEM as a Cloud Service上設定CORS

此範例React應用程式會在本機(在http://localhost:3000)執行,並使用OpenAPI API連線至AEM Publish服務的AEM內容片段傳送。 若要允許此連線,必須在AEM Publish (或Preview)服務上設定CORS (跨原始資源共用)。

依照設定在上執行的SPA的http://localhost:3000指示,允許CORS要求傳送至AEM Publish服務

本機CORS Proxy

或者,若要進行開發,請執行本機CORS Proxy,以提供AEM的CORS友好連線。

$ npm install --global lcp
$ lcp --proxyUrl https://publish-p<PROGRAM_ID>-e<ENVIRONMENT_ID>.adobeaemcloud.com

--proxyUrl值更新至您的AEM發佈(或預覽) URL。

在本機CORS Proxy執行下,存取位於http://localhost:8010/proxy的AEM內容片段傳送API以避免CORS問題。

複製範例React應用程式

實作一個現成的範例React應用程式,其中包含與OpenAPI的AEM內容片段傳送互動所需的程式碼,並顯示從這些API取得的團隊和人員資料。

範例React應用程式原始碼為可在Github.com上取得。

若要取得React應用程式:

  1. Github.comheadless_open-api_basic標籤複製範例WKND OpenAPI React應用程式。

    code language-shell
    $ cd ~/Code
    $ git clone git@github.com:adobe/aem-tutorials.git
    $ cd aem-tutorials
    $ git fetch --tags
    $ git tag
    $ git checkout tags/headless_open-api_basic
    
  2. 導覽至headless/open-api/basic資料夾,並在IDE中開啟。

    code language-shell
    $ cd ~/Code/aem-tutorials/headless/open-api/basic
    $ code .
    
  3. 更新.env以連線至AEM as a Cloud Service Publish服務,因為這是發佈內容片段的位置。 如果您想要使用AEM預覽服務測試應用程式(且內容片段會發佈在那裡),這可以指向AEM預覽服務。

    code language-none
    # AEM Publish (or Preview) service that provides Content Fragments
    REACT_APP_HOST_URI=https://publish-p123-e456.adobeaemcloud.com
    

    使用本機CORS Proxy時,將REACT_APP_HOST_URI設為http://localhost:8010/proxy

    code language-none
    # AEM Publish (or Preview) service that provides Content Fragments
    REACT_APP_HOST_URI=http://localhost:8010/proxy
    
  4. 啟動React應用程式

    code language-shell
    $ cd ~/Code/aem-tutorials/headless/open-api/basic
    $ npm install
    $ npm start
    
  5. React應用程式會在http://localhost:3000/上以開發模式啟動。 在教學課程中對React應用程式所做的變更,會立即反映在網頁瀏覽器中。

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

React應用程式剖析

範例React應用程式有三個主要部分需要更新。

  1. .env檔案包含AEM發佈(或預覽)服務URL。
  2. src/components/Teams.js會顯示團隊及其成員的清單。
  3. src/components/Person.js會顯示單一團隊成員的詳細資料。

實作團隊功能

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

  • 新的自訂React useEffect勾點會透過擷取要求叫用​ 列出所有內容片段API,然後取得每個fullNameteamMember值以供顯示。

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

團隊檢視

  1. 開啟 src/components/Teams.js

  2. 實作​ Teams ​元件以從List all Content Fragments API擷取Teams清單,並轉譯Teams內容。 此程式分為下列步驟:

  3. 建立叫用AEM的useEffect列出所有內容片段​ API並將資料儲存到React元件狀態的 ​連結。

  4. 針對傳回的每個​ 團隊 ​內容片段,叫用​ 取得內容片段 API以擷取團隊的完整水合詳細資訊,包括其成員及其fullNames

  5. 使用Team函式轉譯Teams資料。

    code language-javascript
    import { useEffect, useState } from "react";
    import { Link } from "react-router-dom";
    import "./Teams.scss";
    
    function Teams() {
    
      // The teams folder is the only folder-tree that is allowed to contain Team Content Fragments.
      const TEAMS_FOLDER = '/content/dam/my-project/en/teams';
    
      // State to store the teams data
      const [teams, setTeams] = useState(null);
    
      useEffect(() => {
        /**
        * Fetches all teams and their associated member details
        * This is a two-step process:
        * 1. First, get all team content fragments from the specified folder
        * 2. Then, for each team, fetch the full details including hydrated references to get the team member names
        */
        const fetchData = async () => {
          try {
            // Step 1: Fetch all teams from the teams folder
            const response = await fetch(
              `${process.env.REACT_APP_HOST_URI}/adobe/contentFragments?path=${TEAMS_FOLDER}`
            );
            const allTeams = (await response.json()).items || [];
    
            // Step 2: Fetch detailed information for each team with hydrated references
            const hydratedTeams = [];
            for (const team of allTeams) {
              const hydratedTeamResponse = await fetch(
                `${process.env.REACT_APP_HOST_URI}/adobe/contentFragments/${team.id}?references=direct-hydrated`
              );
              hydratedTeams.push(await hydratedTeamResponse.json());
            }
    
            setTeams(hydratedTeams);
          } catch (error) {
            console.error("Error fetching content fragments:", error);
          }
        };
    
        fetchData();
      }, [TEAMS_FOLDER]);
    
      // Show loading state while teams data is being fetched
      if (!teams) {
        return <div>Loading teams...</div>;
      }
    
      // Render the teams
      return (
        <div className="teams">
          {teams.map((team, index) => {
            return (
              <Team
                key={index}
                {..team}
              />
            );
          })}
        </div>
      );
    }
    
    /**
    * Team component - renders a single team with its details and members
    * @param {string} fields - The authorable fields
    * @param {Object} references - Hydrated references containing member details such as fullName
    */
    function Team({ fields, references, path }) {
      if (!fields.title || !fields.teamMembers) {
        return null;
      }
    
      return (
        <div className="team">
          <h2 className="team__title">{fields.title}</h2>
          {/* Render description as HTML using dangerouslySetInnerHTML */}
          <p
            className="team__description"
            dangerouslySetInnerHTML={{ __html: fields.description.value }}
          />
          <div>
            <h4 className="team__members-title">Members</h4>
            <ul className="team__members">
              {/* Render each team member as a link to their detail page */}
              {fields.teamMembers.map((teamMember, index) => {
                return (
                  <li key={index} className="team__member">
                    <Link to={`/person/${teamMember}`}>
                      {/* Display the full name from the hydrated reference */}
                      {references[teamMember].value.fields.fullName}
                    </Link>
                  </li>
                );
              })}
            </ul>
          </div>
        </div>
      );
    }
    
    export default Teams;
    

實作人員功能

完成團隊功能後,請實作該功能以處理團隊成員或個人詳細資訊上的顯示。

人員檢視

若要這麼做:

  1. 開啟src/components/Person.js

  2. Person React元件中,剖析id路由引數。 請注意,React應用程式的路由先前已設定為接受id URL引數(請參閱/src/App.js)。

  3. 從AEM擷取人員資料取得內容片段API

    code language-javascript
    import "./Person.scss";
    import { useEffect, useState } from "react";
    import { useParams } from "react-router-dom";
    
    /**
    * Person component - displays detailed information about a single person
    * Fetches person data from AEM using the ID from the URL parameters
    */
    function Person() {
      // Get the person ID from the URL parameter
      const { id } = useParams();
    
      // State to store the person data
      const [person, setPerson] = useState(null);
    
      useEffect(() => {
        /**
        * Fetches person data from AEM Content Fragment Delivery API
        * Uses the ID from URL parameters to get the specific person's details
        */
        const fetchData = async () => {
          try {
            /* Hydrate references for access to profilePicture asset path */
            const response = await fetch(
              `${process.env.REACT_APP_HOST_URI}/adobe/contentFragments/${id}?references=direct-hydrated`
            );
            const json = await response.json();
            setPerson(json || null);
          } catch (error) {
            console.error("Error fetching person data:", error);
          }
        };
        fetchData();
      }, [id]); // Re-fetch when ID changes
    
      // Show loading state while person data is being fetched
      if (!person) {
        return <div>Loading person...</div>;
      }
    
      return (
        <div className="person">
          {/* Person profile image - Look up the profilePicture reference in the references object */}
          <img
            className="person__image"
            src={process.env.REACT_APP_HOST_URI + person.references[person.fields.profilePicture].value.path}
            alt={person.fields.fullName}
          />
          {/* Display person's occupations */}
          <div className="person__occupations">
            {person.fields.occupation.map((occupation, index) => {
              return (
                <span key={index} className="person__occupation">
                  {occupation}
                </span>
              );
            })}
          </div>
    
          {/* Person's main content: name and biography */}
          <div className="person__content">
            <h1 className="person__full-name">{person.fields.fullName}</h1>
            {/* Render biography as HTML content */}
            <div
              className="person__biography"
              dangerouslySetInnerHTML={{ __html: person.fields.biographyText.value }}
            />
          </div>
        </div>
      );
    }
    
    export default Person;
    

取得完成的程式碼

本章的完整原始碼為,可在Github.com上取得。

$ git fetch --tags
$ git tag
$ git checkout tags/headless_open-api_basic_4-end

試用應用程式

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

內部運作原理

當您與React應用程式互動時,開啟瀏覽器的​ 開發人員工具>網路 ​主控台和​ 篩選 ​以擷取/adobe/contentFragments請求。

恭喜!

恭喜!您已成功建立React應用程式,以便透過OpenAPI使用及顯示AEM內容片段傳送的內容片段。

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