Modificare l’app React con Universal Editor

In questo capitolo imparerai a rendere modificabile l'app React integrata nel capitolo precedente tramite AEM Universal Editor. Universal Editor consente agli autori di contenuti di modificare i contenuti direttamente nel contesto dell’esperienza dell’app React, mantenendo al contempo l’esperienza fluida di un’applicazione headless.

Editor universale

Universal Editor offre un modo efficace per abilitare la modifica contestuale per qualsiasi applicazione Web, consentendo agli autori di modificare il contenuto senza passare da un’interfaccia di authoring all’altra.

Prerequisiti

Obiettivi

Scopri come:

  • Aggiungi la strumentazione Universal Editor all’app React.
  • Configura l’app React per l’editor universale.
  • Consente di modificare i contenuti direttamente nell’interfaccia dell’app React utilizzando l’editor universale.

Strumentazione editor universale

Universal Editor richiede attributi HTML e metatag per identificare il contenuto modificabile e stabilire la connessione tra l'interfaccia utente e il contenuto AEM.

Aggiungere tag dell’editor universale

Innanzitutto, aggiungi i metatag necessari per identificare l’app React come compatibile con Universal Editor.

  1. Apri public/index.html nella tua app React.

  2. Aggiungi i metatag Universal Editor e lo script CORS nella sezione <head> dell'app React:

    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. Aggiornare il file .env dell'app React in modo da includere l'host del servizio AEM Author per supportare write-back in Universal Editor (utilizzato nel valore del tag metat urn:adobe:aue:system:aemconnection).

    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
    

Strumentazione del componente team

Ora aggiungi gli attributi dell’Editor universale per rendere modificabile il componente Team.

  1. Apri src/components/Teams.js.

  2. Aggiornare il componente Team per includere gli attributi dei dati dell'editor universale:

    Quando imposti l’attributo data-aue-resource, accertati che il percorso AEM del frammento di contenuto restituito da AEM Content Fragment Delivery con API OpenAPI sia postfissato al percorso secondario della variante del frammento di contenuto; in questo caso /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;
    

Strumento del componente persona

Allo stesso modo, aggiungi gli attributi dell’Editor universale al componente Persona.

  1. Apri src/components/Person.js.

  2. Aggiorna il componente per includere attributi dati dell'editor universale:

    Quando imposti l’attributo data-aue-resource, accertati che il percorso AEM del frammento di contenuto restituito da AEM Content Fragment Delivery con API OpenAPI sia postfissato al percorso secondario della variante del frammento di contenuto; in questo caso /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>
        );
    }
    

Ottieni il codice completato

Il codice sorgente completo per questo capitolo è disponibile su Github.com.

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

Test dell’integrazione con Universal Editor

Ora testa gli aggiornamenti della compatibilità con Universal Editor aprendo l’app React in Universal Editor.

Avviare l’app React

  1. Assicurati che l’app React sia in esecuzione:

    code language-bash
    $ cd ~/Code/aem-guides-wknd-openapi/basic-tutorial
    $ npm install
    $ npm start
    
  2. Verificare che l'app si carichi in http://localhost:3000 e visualizzi il contenuto dei team e delle persone.

Esegui proxy SSL locale

Universal Editor richiede che l'applicazione modificabile venga caricata su HTTPS.

  1. Per eseguire l'app React locale su HTTPS, utilizzare il modulo local-ssl-proxy npm dalla riga di comando.

    code language-bash
    $ npm install -g local-ssl-proxy
    $ local-ssl-proxy --source 3001 --target 3000
    
  2. Apri https://localhost:3001 nel browser Web

  3. Accettare il certificato autofirmato.

  4. Verifica il caricamento dell’app React.

Apri in Editor universale

Apri app in Universal Editor

  1. Passa a Editor universale.
  2. Nel campo URL sito, immettere l'URL dell'app React HTTPS: https://localhost:3001.
  3. Seleziona Fai Clic Su Apri.

L’editor universale deve caricare l’app React con le funzionalità di modifica abilitate.

Verifica funzionalità di modifica

Modifica in Universal Editor

  1. Nell’editor universale, passa il cursore del mouse sugli elementi modificabili nell’app React.

  2. Per spostarti all'interno dell'app React, attiva e disattiva di nuovo la modalità Anteprima per modificarla. Ricorda che Anteprima non ha nulla a che fare con il servizio Anteprima di AEM, ma attiva e disattiva la funzione di modifica di Chrome in Universal Editor.

  3. Dovresti vedere come modificare gli indicatori ed essere in grado di fare clic sui vari elementi modificabili dell’app React.

  4. Prova a modificare il titolo di un team:

    • Fai clic sul titolo di un team
    • Modificare il testo nel pannello delle proprietà
    • Salva le modifiche
  5. Prova a modificare l’immagine del profilo di una persona:

    • Fai clic sull’immagine del profilo di una persona
    • Seleziona una nuova immagine dal selettore risorse
    • Salva le modifiche
  6. Premi Pubblica in alto a destra nell'Editor universale per pubblicare le modifiche sul servizio di pubblicazione (o anteprima) di AEM, in modo che vengano applicate nell'app React nell'Editor universale.

Attributi dei dati dell’Editor universale

Per la documentazione completa sulla strumentazione di un'applicazione per Universal Editor, fare riferimento alla documentazione di Universal Editor.

Congratulazioni.

Congratulazioni Hai integrato correttamente Universal Editor con la tua app React. Gli autori dei contenuti ora possono modificare i frammenti di contenuto direttamente nell’interfaccia dell’app React, fornendo un’esperienza di authoring fluida mantenendo al contempo i vantaggi di un’architettura headless.

Ricorda che è sempre possibile ottenere il codice sorgente finale per questa esercitazione dal ramo main dell'archivio GitHub.com.

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