第 1 章では、AEM SPA Editor JS SDK のインストールと、AEM テキストおよび画像コンポーネントをマッピングする 2 つの React コンポーネントの実装について説明します。AEM と React の間の統合をおこなう JSON モデルについて確認します。

チュートリアルの目次

前提条件

これは、複数のパートで構成されているチュートリアルの第 1 章です。第 0 章を修了し、ローカル開発環境用に必要なツールのインストールが完了していることを想定しています。

GitHub で第 0 章のソリューションを参照するか、以下でパッケージをダウンロードできます。

ダウンロード

AEM SPA Editor JS SDK のインストール

担当者:フロントエンド開発者

AEM SPA Editor JS SDK とは

一言でいえば、SPA Editor JS SDK は、作者が AEM にデプロイされたシングルページアプリケーションのコンテンツを編集できるフレームワークを提供する、オープンソース JavaScript ライブラリを集めたものです。AEM は JSON の形でコンテンツを提供し、SPA Editor JS SDK は JSON を React コンポーネントへマッピングします。SPA Editor の仕組みの包括的な概要については、SPA Editor の概要をお読みください。

AEM SPA Editor JS SDK は 3 つの NPM モジュールから入手できます。

  • @adobe/cq-spa-component-mapping - AEM コンポーネントを SPA コンポーネントにマッピングする際に役立つヘルパーを提供します。このモジュールは、特定の SPA フレームワークとは結び付けられていません。
  • @adobe/cq-spa-page-model-manager - AEM ページのモデル表現を管理する API を提供します。この表現は SPA の作成に使用されます。このモジュールは、特定の SPA フレームワークとは結び付けられていません。
  • @adobe/cq-react-editable-components - AEM オーサリングをサポートする汎用 React ヘルパーおよびコンポーネントを提供します。また、このモジュールは cq-spa-page-model-manager and cq-spa-component-mapping をラップして、これらを React フレームワークで使用できるようにします。

次に、npm を使用して、react-app プロジェクトの一部として AEM SPA Editor JS SDK をインストールします。

  1. 新しいターミナルウィンドウを開き、react-app ディレクトリに移動します。

    $ cd <src>/aem-guides-wknd-events/react-app
  2. アドビによって提供された次の NPM モジュールをインストールします。

    $ npm install @adobe/cq-spa-component-mapping
    $ npm install @adobe/cq-spa-page-model-manager
    $ npm install @adobe/cq-react-editable-components
  3. ピア依存関係をインストールします。

    $ npm install react-fast-compare
    $ npm install ajv --save-dev
    $ npm install clone --save-dev
  4. react-app/package.json は次のようになります。

    依存関係のバージョンは、このチュートリアルの開始時期によってわずかに異なる場合があります。

    //package.json
    
    {
        "name": "react-app",
        "version": "0.1.0",
        "private": true,
        "dependencies": {
            "@adobe/cq-react-editable-components": "^1.0.3",
            "@adobe/cq-spa-component-mapping": "^1.0.3",
            "@adobe/cq-spa-page-model-manager": "^1.0.1",
            "react": "^16.5.2",
            "react-dom": "^16.5.2",
            "react-fast-compare": "^2.0.2",
            "react-scripts": "2.0.4"
        },
        ...
    
        "devDependencies": {
            "aem-clientlib-generator": "^1.4.1",
            "ajv": "^6.5.4",
            "clone": "^2.1.2"
        }
    }

AEM SPA Editor JS SDK の統合

担当者:フロントエンド開発者

次に、React アプリを AEM SPA Editor JS SDK と統合します。これが完了すると、React アプリは AEM からの JSON モデルで駆動するようになります。

任意のエディターで、aem-guides-wknd-events/react-app の下にある react-app を開きます。

  1. AEM の JSON を使用してアプリケーションの初期化を開始します。

    react-app/src の下にある index.js ファイルを、以下を使用して更新します。

    // src/index.js
    
    import React from 'react';
    import ReactDOM from 'react-dom';
    import { ModelManager, Constants } from '@adobe/cq-spa-page-model-manager';
    import App from './App';
    
    function render(model) {
        ReactDOM.render((
            <App cqChildren={ model[Constants.CHILDREN_PROP] } cqItems={ model[Constants.ITEMS_PROP] } cqItemsOrder={ model[Constants.ITEMS_ORDER_PROP] }
                cqPath={ ModelManager.rootPath } locationPathname={ window.location.pathname }/>), document.getElementById('root'));
    }
    
    ModelManager.initialize({ path: process.env.REACT_APP_PAGE_MODEL_PATH }).then(render);
  2. react-app/src の下にある App.js ファイルを、以下を使用して更新します。

    // src/App.js
    
    import React from 'react';
    import { Page, withModel, EditorContext, Utils } from '@adobe/cq-react-editable-components';
    
    // This component is the application entry point
    class App extends Page {
    
        render() {
            return (
                <div className="App">
                    <header className="App-header">
                        <h1>Welcome to AEM + React</h1>
                    </header>
                    <EditorContext.Provider value={ Utils.isInEditor() }>
                        { this.childComponents }
                        { this.childPages }
                    </EditorContext.Provider>
                </div>
            );
        }
    }
    
    export default withModel(App);

    App.js は現在でもアプリケーションのエントリポイントとなっていますが、@adobe/cq-react-editable-components から Page クラスを拡張できるようになりました。render() 関数 this.childComponents および this.childPages には、JSON モデルで駆動する React アプリが含まれます。

    EditorContext ラッパーは、AEM エディターと React アプリの間の通信モデルを促します。これにはアプリケーションの編集可能な部分を示すインジケーター(青いボックス)が含まれます。AEM で作成者が React コンポーネントを更新すると、そのコンポーネントが自動的に更新されます。EditorContext は、Utils.isInEditor() の値に基づいて、AEM オーサリング環境でのみアクティブになります。

  3. react-app/src の下に、/components/page のフォルダー構造を作成します。page フォルダーの下で、2 つのファイル(Page.js Page.css)を更新します。

    /react-app
        /src
            /components
                /page
                    Page.js
                    Page.css

    作成するすべての React コンポーネントは、/components の下の専用フォルダー内に保存されます。

  4. 以下のように Page.js を設定します。

    /*
    Page.js
    
    - WKND specific implementation of Page
    - Maps to wknd-events/components/structure/page
    */
    
    import {Page, MapTo, withComponentMappingContext } from "@adobe/cq-react-editable-components";
    require('./Page.css');
    // This component is a variant of a React Page component mapped to the "structure/page" resource type
    // No functionality is changed other than to add an app specific CSS class
    class WkndPage extends Page {
    
        get containerProps() {
            let attrs = super.containerProps;
            attrs.className = (attrs.className || '') + ' WkndPage ' + (this.props.cssClassNames || '');
            return attrs
        }
    }
    
    MapTo('wknd-events/components/structure/page')(withComponentMappingContext(WkndPage));

    ここで、AEM コンポーネントを React コンポーネントへマッピングする MapTo 関数を初めて使います。この場合、リソースタイプ wknd-events/components/structure/page を AEM から React コンポーネント WKNDPage へマッピングします。

  5.  以下のように Page.css を設定します。

    /* Center and max-width the content */
    .WkndPage {
        max-width: 1200px;
        margin: 0 auto;
        padding: 12px;
        padding: 0;
        float: unset !important; 
    }
  6. /react-app/src/components の下に MappedComponents.js という名前のファイルを作成します。以下のように MappedComponents.js を設定します。

    /*
    Dedicated file to include all React components that map to an AEM component
    */
    
    require('./page/Page');
  7. /react-app/src の下の index.js を更新し、MappedComponents.js を含めるようにします。

    // src/index.js
        ...
        import App from './App';
        //include Mapped Components
    +  import "./components/MappedComponents";
    
        ...
  8. プロジェクト aem-guides-wknd-events のルートから、Maven コマンドを実行して、AEM にアップデートをビルドおよびデプロイします。

    $ cd <src>/aem-guides-wknd-events
    $ mvn -PautoInstallPackage -Padobe-public clean install
  9. http://localhost:4502/content/wknd-events/react/home.html に移動します。<h1> Welcome to AEM + React </h1> と表示されているのが確認できます。

    ブラウザーの開発者ツールを使用して、React アプリでレンダリングされた HTML を調べます。React アプリで生成された空の div がいくつかあるのがわかります。div の 1 つには、手順 4 で作成した WkndPage css クラスが含まれているはずです。

    empty-divs

    ページソースを閲覧しても同じマークアップは表示されません。これは、このマークアップがアプリケーションで生成されたためです。

     

テキストコンポーネント

担当者:フロントエンド開発者

次に、ユーザーがアプリケーションの一部としてリッチテキストを作成できるよう、AEM テキストコンポーネントを React コンポーネントにマッピングします。

任意のエディターで、aem-guides-wknd-events/react-app の下にある react-app を開きます。

  1. react-app/src/components の下に、text という名前のサブフォルダーを作成します。text フォルダーの下に、別のファイル Text.js を作成します。

    /react-app
        /src
            /components
                /text
                    Text.js
  2. 以下のように Text.js を設定します。

    /*
    Text.js
    
    Maps to wknd-events/components/content/text
    */
    
    import React, {Component} from 'react';
    import {MapTo} from '@adobe/cq-react-editable-components';
    /**
    * Default Edit configuration for the Text component that interact with the Core Text component and sub-types
    *
    * @type EditConfig
    */
    const TextEditConfig = {
    
        emptyLabel: 'Text',
    
        isEmpty: function(props) {
            return !props || !props.text || props.text.trim().length < 1;
        }
    };
    
    /**
    * Text React component
    */
    class Text extends Component {
    
        get richTextContent() {
            return <div dangerouslySetInnerHTML={{__html:  this.props.text}}/>;
        }
    
        get textContent() {
            return <div>{this.props.text}</div>;
        }
    
        render() {
            return this.props.richText ? this.richTextContent : this.textContent;
        }
    }
    
    MapTo('wknd-events/components/content/text')(Text, TextEditConfig);
  3. Text.js を要求するよう react-app/src/components/MappedComponents.js を更新します。

    // src/components/MappedComponents.js
    
    require('./page/Page');
    require('./text/Text');
  4. プロジェクト aem-guides-wknd-events のルートから、次のコマンドを実行して、AEM にアップデートをビルドおよびデプロイします。

    $ cd <src>/aem-guides-wknd-events
    $ mvn -PautoInstallPackage -Padobe-public clean install
  5. http://localhost:4502/editor.html/content/wknd-events/react/home.html に移動します。空のテキストコンポーネントとプレースホルダーが表示されます。コンポーネントを編集して新しいメッセージを追加できます。また、ページに新しいテキストコンポーネントを追加することもできます。

    wrench-dialog
  6. ブラウザーの別のタブで http://localhost:4502/content/wknd-events/react/home.model.json を表示します。wknd-events/components/content/text を検索して、AEM テキストコンポーネントで書き出された JSON を見つけます。

    //partial contents of /content/wknd-events/react/home.model.json
    ...
    
    "text": {
        "text": "<h1>Hello World</h1>\r\n",
        "richText": true,
        ":type": "wknd-events/components/content/text"
    }
    
    ...

    @adobe/cq-react-editable-components によって提供された MapTo 機能は、:type 値に基づいて Text JSON コンポーネントを React Text コンポーネントにマッピングします。また、MapTo は、テキストやリッチテキストの値を React props として Text コンポーネントで利用できるようにします。

    this.props.text === "<h1>Hello World</h1>\r\n"
    this.props.richText === true

画像コンポーネント

担当者:フロントエンド開発者

次に、ユーザーがアプリケーションの一部として画像を含めることができるよう、AEM 画像コンポーネントを React コンポーネントにマッピングします。

任意のエディターで、aem-guides-wknd-events/react-app の下にある react-app を開きます。

  1. react-app/src/components の下に、image という名前のサブフォルダーを作成します。image フォルダーの下に、Image.js という名前の新しいファイルを作成します。

    /react-app
        /src
            /components
                /image
                    Image.js
  2. 次のように Image.js を設定します。

    import React, {Component} from 'react';
    import {MapTo} from '@adobe/cq-react-editable-components';
    
    /**
    * Default Edit configuration for the Image component that interact with the Core Image component and sub-types
    *
    * @type EditConfig
    */
    const ImageEditConfig = {
    
        emptyLabel: 'Image',
    
        isEmpty: function(props) {
            return !props || !props.src || props.src.trim().length < 1;
        }
    };
    
    /**
    * Image React Component
    * 
    */
    class Image extends Component {
    
        get content() {
            return <img src={this.props.src} alt={this.props.alt}
                title={this.props.displayPopupTitle && this.props.title}/>
        }
    
        render() {
            return (<div className="Image">
                    {this.content}
                </div>);
        }
    }
    
    MapTo('wknd-events/components/content/image')(Image, ImageEditConfig);
  3. src/components/MappedComponents.js を更新して、Image.js を要求するようにします。

    // src/components/MappedComponents.js
    
    require('./page/Page');
    require('./text/Text');
    require('./image/Image');
  4. プロジェクト aem-guides-wknd-events のルートから、次のコマンドを実行して、AEM にアップデートをビルドおよびデプロイします。

    $ cd <src>/aem-guides-wknd-events
    $ mvn -PautoInstallPackage -Padobe-public clean install
  5. http://localhost:4502/editor.html/content/wknd-events/react/home.html に移動します。空の画像コンポーネントとプレースホルダーが表示されます。コンポーネントを編集して新しい画像を追加できます。また、画像をドラッグアンドドロップでアプリケーションに追加することもできます。

    image-cmp
  6. ブラウザーの別のタブで http://localhost:4502/content/wknd-events/react/home.model.json を表示します。wknd-events/components/content/image を検索して、AEM 画像コンポーネントで書き出された JSON を見つけます。

    // partial contents /content/wknd-events/react/home.model.json
    ...
    
    "image": {
        "alt": "Alternative Text here",
        "title": "This is a caption",
        "src": "/content/wknd-events/react/home/_jcr_content/root/responsivegrid/image.coreimg.jpeg/1539196394835.jpeg",
        "srcUriTemplate": "/content/wknd-events/react/home/_jcr_content/root/responsivegrid/image.coreimg{.width}.jpeg/1539196394835.jpeg",
        "lazyEnabled": false,
        "widths": [],
        ":type": "wknd-events/components/content/image"
    }
    ...

    テキストコンポーネントと同様に、MapTo 機能では、React 画像コンポーネントで使用できる "all"、"title"、"src" の JSON 値が提供されます。

    この時点ではスタイルについて心配する必要はありません。次の章では、CSS の追加を開始し、フロントエンドの開発者サイクルを確認します。

次の手順

チュートリアルの次のパート:

GitHub で第 1 章のソリューションを参照してください。

チュートリアルのこのパートの完成済みパッケージをダウンロード:

ダウンロード

(ボーナス)HierarchyPage Sling Model

担当者:バックエンド開発者

AEM SPA JS SDK は、JSON スキーマを JavaScript モデルに解析するよう作られています。Sling Model「HierarchyPage.java」は、スタータープロジェクトに含まれます。このプロジェクトは期待されるスキーマと一致する JSON として、AEM 内でコンテンツを提供します。HierarchyPageImpl によって書き出された JSON の主な機能として、複数の AEM ページのコンテンツを 1 回の要求で提供できます。これにより、SPA はアプリケーションのほとんどのコンテンツを使用して初期化できるので、ユーザーがアプリケーションへ移動した際に、後で要求する必要がなくなります。

任意のエディターで、<src>/aem-guides-wknd-events/core モジュールを開きます。

  1. core/src/main/java/com/adobe/aem/guides/wkndevents/core/models/HierarchyPage.java を開きます。

     

    package com.adobe.aem.guides.wkndevents.core.models;
    
    import com.adobe.cq.export.json.ContainerExporter;
    import com.adobe.cq.export.json.hierarchy.HierarchyNodeExporter;
    import com.fasterxml.jackson.annotation.JsonIgnore;
    import com.fasterxml.jackson.annotation.JsonProperty;
    
    public interface HierarchyPage extends HierarchyNodeExporter, ContainerExporter {
    
        ...
    }
    

    インターフェイス HierarchyPage は 2 つのインターフェイスに拡張されます。

    • ContainerExporter - ページ、レスポンシブグリッド、parsys などのコンテナコンポーネントの JSON を定義します。
    • HierarchyNodeExporter - ルートページやその子ページなどの階層ノードの JSON を定義します。

     

  2. core/src/main/java/com/adobe/aem/guides/wkndevents/core/models/impl/HierarchyPageImpl.java を開きます。

    これは、HierarchyPage インターフェイスの実装です。

    注意:

    現在、HierarchyPageImpl はプロジェクトにコピーされています。近い将来、コアコンポーネントからデフォルトの HieararchyPageImpl を利用できるようになる予定です。開発者は引き続き拡張するオプションを利用できますが、実装の管理を担当する必要はなくなります。後日、最新情報を確認するようにしてください。

    @Model(adaptables = SlingHttpServletRequest.class, adapters = {HierarchyPage.class, ContainerExporter.class}, resourceType = HierarchyPageImpl.RESOURCE_TYPE)
    @Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
    public class HierarchyPageImpl implements HierarchyPage {
    
    /**
     * Resource type of associated with the current implementation
     */
    protected static final String RESOURCE_TYPE = "wknd-events/components/structure/page";

    HierarchyPageImpl は、wknd-events/components/structure/pageresource タイプの Sling Model エクスポーターとして登録されています。カスタムプロジェクトを実装する場合は、RESOURCE_TYPE を更新して、プロジェクトのベースページコンポーネントを指すようにします。

    メソッド methods getRoodModel() and getRootPage() は、アプリケーションの「ルート」と見なされるものを見つけて返すために使用されます。アプリケーションテンプレートのポリシーには、コンテンツの収集に使用される 3 つのプロパティが保存されます。

    1. PR_IS_ROOT = "isRoot" - アプリケーションの rootPage を特定するのに役立ちます。rootPage はアプリケーションのすべての子ページを収集する出発点として使用されます。
    2. STRUCTURE_DEPTH_PN = "structureDepth" - 子ページを収集する階層内の深さを特定します。
    3. STRUCTURE_PATTERNS_PN = "structurePatterns" - 特定のページが自動的に収集されるのを無視/除外する正規表現です。
  3. CRXDE-Lite を開きます。

    /conf/wknd-events/settings/wcm/policies/wknd-events/components/structure/app/default に移動します。これは、wknd-events-app-template テンプレート用ポリシーです。isRootstructureDepthstructurePatterns のプロパティに注意します。

    app-template-policy

    警告:

    このチュートリアルの作成時点では、SPA Editor は UI で編集可能テンプレートをサポートしていません。編集可能テンプレートの完全なサポートは近い将来実装される予定です。それまでの間、テンプレートを更新するには、CRXDE-Lite 経由でおこなうか、ui.content モジュールで XML を変更して実行します。

  4. http://localhost:4502/content/wknd-events/react.html で React ルートページを開きます。

    この時点では、ページは空白にレンダリングされる場合があります。このページは、wknd-events-app-template を使用して構築されます。

  5. 拡張子を model.json に変更します。http://localhost:4502/content/wknd-events/react.model.json

    現在のページのコンテンツが公開され、子ページのコンテンツは以下に配置されます。 /content/wknd-events/react/home

    // /content/wknd-events/react.model.json 
    
    {
    ":type": "wknd-events/components/structure/app",
    ":items": {},
    ":itemsOrder": [],
    ":children": {
    "/content/wknd-events/react/home": {
        ":type": "wknd-events/components/structure/page",
        ":items": { ... },
        ":itemsOrder": [
        "root"
        ],
        ":path": "/content/wknd-events/react/home",
        ":hierarchyType": "page",
        "title": "Home"
        }
    },
    ":path": "/content/wknd-events/react",
    ":hierarchyType": "page",
    "title": "React App"
    }

ヘルプ

行き詰まったり、追加の質問がある場合は、AEM 用 Experience League フォーラムを確認するか、既存の GitHub の問題を参照してください。

探していた情報が見つからなかった場合やエラーが見つかった場合は、WKND Events プロジェクトに関する GitHub の問題を報告してください。

本作品は Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License によってライセンス許可を受けています。  Twitter™ および Facebook の投稿には、Creative Commons の規約内容は適用されません。

リーガルノーティス   |   プライバシーポリシー