Bearbeiten der React-App mit dem universellen Editor

In diesem Kapitel erfahren Sie, wie Sie die im vorherigen Kapitel erstellte React-App mit dem universellen Editor von AEM bearbeitbar machen. Mit dem universellen Editor können Inhaltsautorinnen und -autoren Inhalte direkt im Kontext des React-App-Erlebnisses bearbeiten, während das nahtlose Erlebnis einer Headless-Anwendung erhalten bleibt.

Universeller Editor

Der universelle Editor bietet eine leistungsstarke Möglichkeit, die kontextbezogene Bearbeitung für jede Web-Anwendung zu aktivieren, sodass Autorinnen und Autoren Inhalte bearbeiten können, ohne zwischen verschiedenen Authoring-Oberflächen zu wechseln.

Voraussetzungen

Ziele

Erfahren Sie mehr über:

  • Hinzufügen des Instrumentariums des universellen Editors zur React-App.
  • Konfigurieren der React-App für den universellen Editor.
  • Aktivieren der Inhaltsbearbeitung direkt in der React-App-Oberfläche mit dem universellen Editor.

Instrumentarium des universellen Editors

Der universelle Editor erfordert HTML-Attribute und Meta-Tags, um bearbeitbare Inhalte zu identifizieren und die Verbindung zwischen der Benutzeroberfläche und AEM-Inhalten herzustellen.

Hinzufügen von Tags des universellen Editors

Fügen Sie zunächst die erforderlichen Meta-Tags hinzu, um die React-App als mit dem universellen Editor kompatibel zu identifizieren.

  1. Öffnen Sie public/index.html in Ihrer React-App.

  2. Fügen Sie im Abschnitt <head> der React-App die Meta-Tags und das CORS-Skript des universellen Editors hinzu:

    code language-html
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="utf-8" />
        <link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
        <meta name="viewport" content="width=device-width, initial-scale=1" />
        <meta name="theme-color" content="#000000" />
        <meta name="description" content="WKND Teams React App" />
    
        <!-- Universal Editor meta tags and CORS script -->
        <meta name="urn:adobe:aue:system:aemconnection" content="aem:%REACT_APP_AEM_AUTHOR_HOST_URI%" />
        <script src="https://universal-editor-service.adobe.io/cors.js"></script>
    
        <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
        <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
        <title>WKND Teams</title>
    </head>
    <body>
        <noscript>You need to enable JavaScript to run this app.</noscript>
        <div id="root"></div>
    </body>
    </html>
    
  3. Aktualisieren Sie die Datei .env der React-App so, dass sie den Host des AEM-Autoren-Service enthält, um Writebacks im universellen Editor zu unterstützen (der im Wert des Meta-Tags urn:adobe:aue:system:aemconnection verwendet wird).

    code language-bash
    # The AEM Publish (or Preview) service
    REACT_APP_HOST_URI=https://publish-p123-e456.adobeaemcloud.com
    
    # The AEM Author service
    REACT_APP_AEM_AUTHOR_HOST_URI=https://author-p123-e456.adobeaemcloud.com
    

Instrumentieren der Teams-Komponente

Fügen Sie jetzt Attribute des universellen Editors hinzu, damit die Teams-Komponente bearbeitet werden kann.

  1. Öffnen Sie src/components/Teams.js.

  2. Aktualisieren Sie die Komponente Team, um Datenattribute des universellen Editors einzuschließen:

    Stellen Sie beim Festlegen des Attributs data-aue-resource sicher, dass der AEM-Pfad zum Inhaltsfragment, wie er von der AEM-Inhaltsfragmentbereitstellung mit APIs von OpenAPI zurückgegeben wird, mit dem Unterpfad zur Inhaltsfragmentvariante versehen ist. In diesem Fall /jcr:content/data/master.

    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 - renders a single team with its details and members
    * @param {Object} fields - The authored Content Fragment fields
    * @param {Object} references - Hydrated references containing member details such as fullName
    * @param {string} path - Path of the team content fragment
    */
    function Team({ fields, references, path }) {
    if (!fields.title || !fields.teamMembers) {
        return null;
    }
    
    return (
        <>
        {/* Specify the correct Content Fragment variation path suffix in the data-aue-resource attribute */}
        <div className="team"
            data-aue-resource={`urn:aemconnection:${path}/jcr:content/data/master`}
            data-aue-type="component"
            data-aue-label={fields.title}>
    
            <h2 className="team__title"
            data-aue-prop="title"
            data-aue-type="text"
            data-aue-label="Team Title">{fields.title}</h2>
            <p className="team__description"
            data-aue-prop="description"
            data-aue-type="richtext"
            data-aue-label="Team Description"
            dangerouslySetInnerHTML={{ __html: fields.description.value }}
            />
            <div>
            <h4 className="team__members-title">Members</h4>
            <ul className="team__members">
                {fields.teamMembers.map((teamMember, index) => {
                return (
                    <li key={index} className="team__member">
                    <Link to={`/person/${teamMember}`}>
                        {references[teamMember].value.fields.fullName}
                    </Link>
                    </li>
                );
                })}
            </ul>
            </div>
        </div>
        </>
    );
    }
    
    export default Teams;
    

Instrumentieren der Personen-Komponente

Fügen Sie auf ähnliche Weise der Personen-Komponente Attribute des universellen Editors hinzu.

  1. Öffnen Sie src/components/Person.js.

  2. Aktualisieren Sie die Komponente, um Datenattribute des universellen Editors einzuschließen:

    Stellen Sie beim Festlegen des Attributs data-aue-resource sicher, dass der AEM-Pfad zum Inhaltsfragment, wie er von der AEM-Inhaltsfragmentbereitstellung mit APIs von OpenAPI zurückgegeben wird, mit dem Unterpfad zur Inhaltsfragmentvariante versehen ist. In diesem Fall /jcr:content/data/master.

    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() {
        const { id } = useParams();
        const [person, setPerson] = useState(null);
    
        useEffect(() => {
            const fetchData = async () => {
            try {
                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]);
    
        if (!person) {
            return <div>Loading person...</div>;
        }
    
        /* Add the Universal Editor data-aue-* attirbutes to the rendered HTML */
        return (
            <div className="person"
                data-aue-resource={`urn:aemconnection:${person.path}/jcr:content/data/master`}
                data-aue-type="component"
                data-aue-label={person.fields.fullName}>
                <img className="person__image"
                    src={process.env.REACT_APP_HOST_URI + person.references[person.fields.profilePicture].value.path}
                    alt={person.fields.fullName}
                    data-aue-prop="profilePicture"
                    data-aue-type="media"
                    data-aue-label="Profile Picture"
                />
                <div className="person__occupations">
                    {person.fields.occupation.map((occupation, index) => {
                    return (
                        <span key={index} className="person__occupation">
                            {occupation}
                        </span>
                    );
                    })}
                </div>
    
                <div className="person__content">
                    <h1 className="person__full-name"
                        data-aue-prop="fullName"
                        data-aue-type="text"
                        data-aue-label="Full Name">
                        {person.fields.fullName}
                    </h1>
                    <div className="person__biography"
                        data-aue-prop="biographyText"
                        data-aue-type="richtext"
                        data-aue-label="Biography"
                        dangerouslySetInnerHTML={{ __html: person.fields.biographyText.value }}
                    />
                </div>
            </div>
        );
    }
    

Abrufen des vollständigen Codes

Der vollständige Quell-Code für dieses Kapitel ist auf Github.com verfügbar.

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

Testen der Integration des universellen Editors

Testen Sie jetzt die Kompatibilitätsaktualisierungen für den universellen Editor, indem Sie die React-App im universellen Editor öffnen.

Starten der React-App

  1. Stellen Sie sicher, dass Ihre React-App ausgeführt wird:

    code language-bash
    $ cd ~/Code/aem-guides-wknd-openapi/basic-tutorial
    $ npm install
    $ npm start
    
  2. Überprüfen Sie, ob die App unter http://localhost:3000 geladen wird und die Inhalte von Teams und Personen anzeigt.

Ausführen des lokalen SSL-Proxys

Für den universellen Editor muss die bearbeitbare Anwendung über HTTPS geladen werden.

  1. Um die lokale React-App über HTTPS auszuführen, verwenden Sie das npm-Modul local-ssl-proxy über die Befehlszeile.

    code language-bash
    $ npm install -g local-ssl-proxy
    $ local-ssl-proxy --source 3001 --target 3000
    
  2. Öffnen Sie https://localhost:3001 in einem Webbrowser.

  3. Akzeptieren Sie das selbstsignierte Zertifikat.

  4. Überprüfen Sie, ob die React-App geladen wird.

Öffnen im universellen Editor

App im universellen Editor öffnen

  1. Navigieren Sie zu Universeller Editor.
  2. Geben Sie im Feld Site-URL die URL der HTTPS-React-App ein: https://localhost:3001.
  3. Klicken Sie auf Öffnen.

Der universelle Editor sollte Ihre React-App mit aktivierten Bearbeitungsfunktionen laden.

Testen der Bearbeitungsfunktion

Im universellen Editor bearbeiten

  1. Bewegen Sie im universellen Editor den Mauszeiger über bearbeitbare Elemente in Ihrer React-App.

  2. Um in der React-App zu navigieren, aktivieren Sie den Vorschau-Modus und deaktivieren Sie ihn wieder, um die Bearbeitung zu ermöglichen. Denken Sie daran dass Vorschau nichts mit dem AEM-Vorschau-Service zu tun hat, sondern den Bearbeitungsmodus im universellen Editor ein- und ausschaltet.

  3. Sie sollten Bearbeitungsindikatoren sehen und in der Lage sein, auf die verschiedenen bearbeitbaren Elemente der React-App zu klicken.

  4. Bearbeiten Sie einen Team-Titel:

    • Klicken Sie auf einen Team-Titel
    • Bearbeiten Sie den Text im Panel „Eigenschaften“
    • Speichern Sie die Änderungen
  5. Bearbeiten Sie das Profilbild einer Person:

    • Klicken Sie auf das Profilbild einer Person
    • Wählen Sie ein neues Bild aus der Asset-Auswahl aus
    • Speichern Sie die Änderungen
  6. Klicken Sie oben rechts im universellen Editor auf Veröffentlichen, um Änderungen im Veröffentlichungs- (oder Vorschau-) Service von AEM zu veröffentlichen, sodass sie in der React-App im universellen Editor angezeigt werden.

Datenattribute des universellen Editors

Die vollständige Dokumentation zum Instrumentieren einer Anwendung für den universellen Editor finden Sie in der Dokumentation zum universellen Editor.

Herzlichen Glückwunsch!

Herzlichen Glückwunsch! Sie haben den universellen Editor erfolgreich in Ihre React-App integriert. Inhaltsautorinnen und -autoren können Inhaltsfragmente jetzt direkt in der React-App-Benutzeroberfläche bearbeiten, was ein nahtloses Authoring-Erlebnis bietet und gleichzeitig die Vorteile einer Headless-Architektur bietet.

Denken Sie daran, dass Sie den endgültigen Quell-Code für dieses Tutorial immer aus dem Zweig main des Repositorys auf GitHub.com abrufen können.

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