AEM Sites の概要(第 6 章)- 新しい AEM コンポーネントを作成

作成したコンテンツを表示するカスタム署名コンポーネントのエンドツーエンドの作成について扱います。ビジネスロジックをカプセル化して署名コンポーネントおよび対応する HTL を入力し、コンポーネントをレンダリングする Sling Model の開発を含みます。

前提条件

これは、複数のパートで構成されているチュートリアルの第 6 章です。第 5 章はこちらから概要はこちらから確認できます。

GitHub で完成済みコードを参照するか、ソリューションパッケージをダウンロードできます。

ダウンロード

署名コンポーネント

WKND チュートリアルのこのパートでは、記事の寄稿者に関する作成情報を表示する記事ページで使用するための署名コンポーネントを作成します。

WKND デザインチームが提供する署名コンポーネントの視覚的デザイン

この新しいコンポーネントの実装には、署名コンテンツを収集するダイアログが含まれ、HTL スクリプトで表示するために、カスタム Sling Model が署名の以下のものを取得します。

  • 名前
  • 画像
  • 職業

 

署名コンポーネントの分解

署名コンポーネントの作成

最初に、署名コンポーネントノード構造を作成し、ダイアログを定義します。これは、AEM のコンポーネントを表し、JCR 内のその場所によってコンポーネントのリソースタイプを暗黙的に定義します。

ダイアログは、コンテンツ作成者が提供できるインターフェイスを表示します。この実装では、AEM WCM コアコンポーネントの画像コンポーネントを署名の画像のオーサリングとレンダリングの処理に活用し、アドビのコンポーネントの sling:resourceSuperType として設定します。

  1. ui .apps モジュール(/apps/wknd/components/content の下にある)に、byline という名前でタイプが cq :Component の新しいノードを作成します。

  2. 署名コンポーネントの cq:Component ノードに以下のプロパティを追加します。

    jcr:title = Byline 
    jcr:description = Displays a contributor's byline. 
    componentGroup = WKND.Content 
    sling:resourceSuperType =  core/wcm/components/image/v2/image

    以下の XML が得られます。

    <?xml version="1.0" encoding="UTF-8"?> 
    <jcr:root 
        xmlns:sling="http://sling.apache.org/jcr/sling/1.0" 
        xmlns:jcr="http://www.jcp.org/jcr/1.0" 
        jcr:primaryType="cq:Component" 
        jcr:title="Byline" 
        jcr:description="Displays a contributor's byline." 
        componentGroup="WKND.Content" 
        sling:resourceSuperType="core/wcm/components/image/v2/image"/>

HTL スクリプトの作成

  1. byline cq:Component ノードの下に、byline.html という名前の新しいファイルを追加します。これは、コンポーネントの HTML プレゼンテーションを担当します。Sling がこのリソースタイプをレンダリングするために使用するデフォルトのスクリプトになるので、cq:Component ノードと同じファイル名を付けることは重要です。

  2. 以下のコードを byline.html に追加します。

    <div data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html"> 
    </div> 
    <sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=true}"></sly>

この byline.html は、このパートの後半で Sling Model を作成した後に再検討します。HTL ファイルの現在の状態により、コンポーネントをページにドラッグ&ドロップした際に、AEM Sites のページエディターに表示できます。

ダイアログ定義の作成

次に、以下のフィールドを含む、署名コンポーネント用のダイアログを定義します。

  • 名前:寄稿者の名前のテキストフィールド。
  • 画像:寄稿者の自己紹介写真への参照。
  • 職業:寄稿者に起因する職業のリスト。職業は、アルファベットの昇順(a~z)で並べ替えられる必要があります。
  1. 署名コンポーネントノードの下に、cq:dialog という名前でタイプが nt:unstructured の新しいノードを作成します。

  2. cq:dialog を以下の XML で更新します。.content.xml を開いて、以下の XML をコピー&ペーストすると簡単です。

    <?xml version="1.0" encoding="UTF-8"?> 
    <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:granite="http://www.adobe.com/jcr/granite/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" xmlns:nt="http://www.jcp.org/jcr/nt/1.0" 
        jcr:primaryType="nt:unstructured" 
        jcr:title="Byline" 
        sling:resourceType="cq/gui/components/authoring/dialog"> 
        <content 
            jcr:primaryType="nt:unstructured" 
            sling:resourceType="granite/ui/components/coral/foundation/container"> 
            <items jcr:primaryType="nt:unstructured"> 
                <tabs 
                    jcr:primaryType="nt:unstructured" 
                    sling:resourceType="granite/ui/components/coral/foundation/tabs" 
                    maximized="{Boolean}false"> 
                    <items jcr:primaryType="nt:unstructured"> 
                        <!-- This allows the Core Components' Image component's asset definition tab --> 
                        <asset  
                            jcr:primaryType="nt:unstructured" 
                            sling:hideResource="{Boolean}false"/> 
                        <!-- This hides the Core Components' Image component's metadata tab --> 
                        <metadata  
                            jcr:primaryType="nt:unstructured" 
                            sling:hideResource="{Boolean}true"/>     
                        <properties 
                            jcr:primaryType="nt:unstructured" 
                            jcr:title="Properties" 
                            sling:resourceType="granite/ui/components/coral/foundation/container" 
                            margin="{Boolean}true"> 
                            <items jcr:primaryType="nt:unstructured"> 
                                <columns 
                                    jcr:primaryType="nt:unstructured" 
                                    sling:resourceType="granite/ui/components/coral/foundation/fixedcolumns" 
                                    margin="{Boolean}true"> 
                                    <items jcr:primaryType="nt:unstructured"> 
                                        <column 
                                            jcr:primaryType="nt:unstructured" 
                                            sling:resourceType="granite/ui/components/coral/foundation/container"> 
                                            <items jcr:primaryType="nt:unstructured"> 
                                                <name 
                                                    jcr:primaryType="nt:unstructured" 
                                                    sling:resourceType="granite/ui/components/coral/foundation/form/textfield" 
                                                    fieldLabel="Name" 
                                                    fieldDescription="The contributor's name to display." 
                                                    emptyText="Enter the contributor's name to display."             name="./name" 
                                                    required="{Boolean}true"/> 
                                          <occupations 
                                                  jcr:primaryType="nt:unstructured" 
                                                  fieldLabel="Occupations" 
                                                  fieldDescription="A list of the contributor's occupations." 
                                                  required="{Boolean}false" 
                                                  sling:resourceType="granite/ui/components/coral/foundation/form/multifield"> 
                                               <field 
                                                      jcr:primaryType="nt:unstructured" 
                                                   name="./occupations" 
                                                      emptyText="Enter an occupation" 
                                                      sling:resourceType="granite/ui/components/coral/foundation/form/textfield"/> 
                                          </occupations> 
                                            </items> 
                                        </column> 
                                    </items> 
                                </columns> 
                            </items> 
                        </properties> 
                    </items> 
                </tabs> 
            </items> 
        </content> 
    </jcr:root>

    上の 15~22 行に注意してください。これらのノード定義は、Sling Resource Merger を使用して、どのダイアログタブが sling:resourceSuperType コンポーネントから継承されるかを制御します(この場合、コアコンポーネントの画像コンポーネント)。

ポリシーダイアログの作成

ダイアログ作成と同じ方法でポリシーダイアログ(以前のデザインダイアログ)を作成して、コアコンポーネントの画像コンポーネントから継承されたポリシー設定の不要なフィールドを非表示にします。

  1. 署名 cq :Component ノードの下に、 cq :design_dialog という名前でタイプが nt :unstructured の新しいノードを作成します。

  2. cq :design_dialog を以下の XML で更新します。.content.xml を開いて、以下の XML をコピー&ペーストすると簡単です。

    <?xml version="1.0" encoding="UTF-8"?> 
    <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" 
              xmlns:nt="http://www.jcp.org/jcr/nt/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" 
              xmlns:granite="http://www.adobe.com/jcr/granite/1.0" 
        jcr:primaryType="nt:unstructured" 
        jcr:title="Byline" 
        sling:resourceType="cq/gui/components/authoring/dialog"> 
        <content 
            jcr:primaryType="nt:unstructured"> 
            <items jcr:primaryType="nt:unstructured"> 
                <tabs 
                    jcr:primaryType="nt:unstructured"> 
                    <items jcr:primaryType="nt:unstructured"> 
                        <properties 
                            jcr:primaryType="nt:unstructured"> 
                            <items jcr:primaryType="nt:unstructured"> 
                                <content 
                                    jcr:primaryType="nt:unstructured"> 
                                    <items jcr:primaryType="nt:unstructured"> 
                                        <decorative 
                                            jcr:primaryType="nt:unstructured" 
                                            sling:hideResource="{Boolean}true"/> 
                                        <altValueFromDAM 
                                            jcr:primaryType="nt:unstructured" 
                                            sling:hideResource="{Boolean}true"/> 
                                        <titleValueFromDAM 
                                            jcr:primaryType="nt:unstructured" 
                                            sling:hideResource="{Boolean}true"/> 
                                        <displayCaptionPopup 
                                            jcr:primaryType="nt:unstructured" 
                                            sling:hideResource="{Boolean}true"/> 
                                        <disableUuidTracking 
                                            jcr:primaryType="nt:unstructured" 
                                            sling:hideResource="{Boolean}true"/>                                 
                                    </items> 
                                </content> 
                            </items> 
                        </properties> 
                        <features 
                            jcr:primaryType="nt:unstructured"> 
                            <items jcr:primaryType="nt:unstructured"> 
                                <content 
                                    jcr:primaryType="nt:unstructured"> 
                                    <items jcr:primaryType="nt:unstructured"> 
                                        <accordion 
                                            jcr:primaryType="nt:unstructured"> 
                                            <items jcr:primaryType="nt:unstructured"> 
                                                <orientation 
                                                    jcr:primaryType="nt:unstructured" 
                                                    sling:hideResource="{Boolean}true"/> 
                                                <crop 
                                                    jcr:primaryType="nt:unstructured" 
                                                    sling:hideResource="{Boolean}true"/> 
                                            </items> 
                                        </accordion> 
                                    </items> 
                                </content> 
                            </items> 
                        </features> 
                    </items> 
                </tabs> 
            </items> 
        </content> 
    </jcr:root>

    前のポリシーダイアログ XML の基礎は、コアコンポーネントの画像(v2)コンポーネントから取得されました。

    ダイアログ設定と同様に、Sling Resource Merger を使用して、無関係なフィールドを非表示にします。そうしなければ、無関係のフィールドが sling:resourceSuperType から継承されます。これは、 jcr :primaryType および sling:hideResource="{Boolean}true" プロパティのみを含むノード定義による場合と同じです。

コードのデプロイ

コードを AEM にデプロイします。

コンポーネントの記事ページへの追加

署名コンポーネントを現在の状態で記事ページに追加して、ノード定義が正しいこと、AEM から新しいコンポーネント定義を確認できること、コンポーネントのダイアログがオーサリングで機能することを検証します。

記事ページテンプレート上の署名コンポーネントの有効化

  1. AEM/ツール/一般/テンプレート/WKND サイト/記事ページテンプレートに移動し、テンプレートを編集します。

  2. 構造モードでメインレイアウトコンテナを選択し、ポリシーアイコンをタップしてポリシーを編集します。

  3. WKND.Content コンポーネントグループで、「署名」コンポーネントをチェックしてこのレイアウトコンテナに追加できるようにし、右上にあるチェックをタップして変更を保存します。

コンポーネントをページにドラッグ&ドロップする

  1. AEM/Sites/WKND サイト/ホーム/スポーツ/LA のスケートボード場完全ガイドの記事ページに移動し、編集して開きます。

  2. 左側のサイドバーで、署名コンポーネントを開いた記事ページのレイアウトコンテナの下部にドラッグ&ドロップします。

コンポーネントの作成

 AEM 作成者は、ダイアログを使用してコンポーネントを設定および作成します。  署名コンポーネント開発のこの時点では、ダイアログは、データの収集に含まれますが、作成したコンテンツをレンダリングするためのロジックはまだ追加されていません。

  1. 左側のサイドバーが開いて表示されたことと、アセットファインダーが選択されていることを確認します。

  2. 署名コンポーネントプレースホルダーを選択すると、アクションバーが表示されるので、レンチアイコンをタップしてダイアログを開きます。

  3. ダイアログが開いて最初のタブ(アセット)がアクティブになるので、左側のサイドバーを開いて、画像をアセットファインダーから画像ドロップゾーンにドラッグ&ドロップします。

    任意の AEM のアセットを使用できますが、このチュートリアルのものと同じ自己紹介写真を使用するには、添付した画像をダウンロードして AEM Assets にアップロードします(画像は @staceygabby at unsplash.com の厚意によって使用を許可されたものです)。

    ダウンロード

  4. 画像を追加したら、「プロパティ」タブをクリックして「名前」および「職業」に入力します。

    職業を入力する場合、逆アルファベット順に入力します(それにより、Sling Model で実装するアルファベット順のビジネスロジックが見てすぐにわかります)。

    右上にあるチェックアイコンをタップして、変更を保存します。

  5. ダイアログを保存したら、CRXDE Lite に移動して、コンポーネントのコンテンツが AEM ページの下にある署名コンポーネントコンテンツノードにどのように保存されるかを確認します。

    jcr:content/root/responsivegrid/responsivegrid ノードの下の署名コンポーネントコンテンツノードを探します(例:/content/wknd/en/sports/la-skateparks/jcr:content/root/responsivegrid/responsivegrid/byline)。

    名前職業および fileReference のプロパティ名が署名ノードに保存されていることを確認します。

    また、ノードの sling:resourceType がこのコンテンツノードを署名コンポーネント実装にバインドする wknd/components/content/byline に設定されていることを確認します。

    /content/wknd/en/art/exhibitions/jcr:content/root/responsivegrid/byline

次に、データモデルとして機能し、署名コンポーネントのビジネスロジックを格納する Sling Model を作成します。

Sling Model は、注釈駆動の Java「POJO」(Plain Old Java Object)で、JCR から Java 変数へのデータのマッピングを促進し、AEM 関連の開発時に数多くの他の機能を提供します。

リアクターおよびコア pom.xml の更新

Sling Model を最も効率的に使用するために、プロジェクトの POM を更新する必要があります。リアクターおよびコア pom.xml ファイルを更新して、最新の標準および慣行を使用します。また、この機会を利用して、AEM が提供し、非常に便利なユーティリティクラスを提供する Apache Commons-Lang3 library を活用します。

  1. 次の親リアクター POM を編集します。 aem -guides-wknd/pom.xml

  2. uber-jar 依存関係が少なくとも 6.3.2 であることを確認します。

    <dependencies> 
    ... 
        <dependency> 
            <groupId>com.adobe.aem</groupId> 
            <artifactId>uber-jar</artifactId> 
            <version>6.3.2</version> 
            <classifier>apis</classifier> 
            <scope>provided</scope> 
         </dependency> 
    ... 
    </dependencies>
  3. コアコンポーネント - コアの依存関係を追加します。これは、コアコンポーネントに関連付けられた Sling Model の依存関係です。

    <dependencies> 
        ... 
        <dependency> 
            <groupId>com.adobe.cq</groupId> 
            <artifactId>core.wcm.components.core</artifactId> 
            <version>2.2.0</version> 
            <scope>provided</scope> 
    </dependency> 
    ... 
    </dependencies>

    コアバンドルで公開された)画像 Sling Model は、後でこのチュートリアルの署名 Sling Model で活用されます。

  4. Apache commons-lang3 の依存関係を追加します。これは、数多くの便利なユーティリティクラスを提供します。

    ``` 
    <dependencies> 
        ... 
        <dependency> 
            <groupId>org.apache.commons</groupId> 
            <artifactId>commons-lang3</artifactId> 
            <version>3.6</version> 
            <scope>provided</scope> 
        </dependency> 
        ... 
    </dependencies> 
    ```

    リアクター pom.xml の更新に関するすべてのコンテンツについては、こちらを参照してください。

    <scope>provided</scope> は、AEM がデプロイ時にこの依存関係を満たすことを示します。バージョン 3.6 は、これが AEM によって公開されたバージョンであるものとして使用され、AEM Web コンソールの Dependency Finder (http://localhost:4502/system/console/depfinder)で書き出されたバージョンを検索することで判別できます。

  5. aem -guides-wknd/core/pom.xml を編集して、core.wcm.components.core エントリに合致する依存関係を追加し、コアプロジェクトが認識できるようにします。バージョンおよび範囲はここでは指定されず、リアクター pom の定義から派生されることに注意してください。

    <dependencies> 
        ... 
        <dependency> 
     <groupId>com.adobe.cq</groupId> 
            <artifactId>core.wcm.components.core</artifactId> 
       </dependency> 
        ... 
    </dependencies>
  6. aem -guides-wknd/core/pom.xml を編集して、commons-lang3. エントリに合致する依存関係を追加します。

    <dependencies> 
        ... 
         <dependency> 
            <groupId>org.apache.commons</groupId> 
            <artifactId>commons-lang3</artifactId> 
        </dependency>     
        ... 
    </dependencies>
  7. Sling ModelsScannerPlugin を使用するために、コア pom.xmlmaven-bundle-plugin を更新します。

    maven-bundle-plugin の説明で、設定 <Sling-Model-Packages>...</Sling-Model-Packages> を削除し、<_plugin>org.apache.sling.bnd.models.ModelsScannerPlugin</_plugin> で置き換えます。

    置き換えた設定はプロジェクトを必要とするので、Sling Model を含むパッケージを明示的に指定します。Sling ModelsScannerPlugin を使用すると、任意の Java パッケージの Sling Model を作成できるので、より柔軟性が得られます。

    <plugins> 
        ... 
        <plugin> 
            <groupId>org.apache.felix</groupId> 
            <artifactId>maven-bundle-plugin</artifactId> 
            <extensions>true</extensions> 
            <configuration> 
                <instructions> 
                <Import-Package>javax.inject;version=0.0.0,*</Import-Package> 
                <Bundle-SymbolicName>com.adobe.aem.guides.wknd.core</Bundle-SymbolicName> 
                <_plugin>org.apache.sling.bnd.models.ModelsScannerPlugin</_plugin> 
                </instructions> 
            </configuration> 
            <dependencies> 
                <dependency> 
                    <groupId>org.apache.sling</groupId> 
                    <artifactId>org.apache.sling.bnd.models</artifactId> 
                    <version>1.0.0</version> 
                </dependency> 
            </dependencies> 
        </plugin> 
        ... 
    </plugins>

    core/pom.xml の完全な更新は、こちらを参照してください。

Byline インターフェイス

次に、署名用のパブリック Java インターフェイスを作成します。Byline.java は、byline.html HTL スクリプトを実行するために必要なパブリックメソッドを定義します。

  1. src/main/java の下の wknd-sites-guide.core モジュール内で右クリックして、com.adobe.aem.guides.wknd.core.components という名前の新しい Java パッケージを作成します。新しい Java パッケージダイアログで、「package-info.java を作成」をチェックします。

  2. com.adobe.aem.guides.wknd.components の下で、package-info.java を以下で更新します。

    @Version("0.0.1") 
    package com.adobe.aem.guides.wknd.core.components;   
     
    import org.osgi.annotation.versioning.Version;

    ベストプラクティスとしては、パブリックインターフェイスを公開する Java パッケージのバージョンにすることが推奨されます。これは、公開されたメソッドが変更された場合にコードの使用を警告するために使用でき、様々なチームがこのインターフェイスに依存している場合に最も役立ちます。

  3. com.adobe.aem.guides.wknd.core.components パッケージを右クリックし、新規/インターフェイス を選択して、Byline.java という名前の新しい Java インターフェイスを作成します。インターフェイス名として "Byline" と入力し、「完了」をクリックします。

  4. 以下のメソッドで Byline.java を更新します。

    package com.adobe.aem.guides.wknd.core.components; 
     
    import java.util.List; 
     
    /**  
     * Represents the Byline AEM Component for the WKND Site project. 
     **/ 
    public interface Byline { 
        /*** 
         * @return a string to display as the name. 
         */ 
        String getName(); 
      
        /*** 
         * Occupations are to be sorted alphabetically in a descending order. 
         *  
         * @return a list of occupations. 
         */ 
        List<String> getOccupations(); 
      
        /*** 
         * @return a boolean if the component has content to display. 
         */ 
        boolean isEmpty(); 
    }

    最初の 2 つのメソッドは、署名コンポーネントの名前職業の値を公開します。

    isEmpty() メソッドは、コンポーネントにレンダリングするコンテンツがあるかどうか、または設定されるのを待っているかどうかを判定するのに使用されます。

    画像用のメソッドがないことに注意してください。その理由については後述します。

署名実装

BylineImpl.java は、既に定義した Byline.java インターフェイスを実装する Sling Model の実装です。BylineImpl.java の完全なコードは、この節の最後に記載しています。

  1. src/main/java の下の core モジュール内で右クリックして、com.adobe.aem.guides.wknd.core.components.impl という名前の新しい Java パッケージを作成します。このとき、Create package-info.java のチェックをオフのままにします。これは、 impl パッケージ名で示されたプライベートパッケージであるためです。

  2. com.adobe.aem.guides.wknd.core.components.impl パッケージを右クリックし、新規/クラス を選択して、BylineImpl.java という名前の新しいクラスファイルを作成します。

    名前として、"BylineImpl" と入力します。Byline インターフェイスを実装インターフェイスとして追加します。

  3. BylineImpl.java を開きます。インターフェイス Byline.java で定義されたすべてのメソッドを使用して自動的に入力されています。

    package com.adobe.aem.guides.wknd.core.components.impl; 
     
    import java.util.List; 
    import com.adobe.aem.guides.wknd.core.components.Byline; 
     
    public class BylineImpl implements Byline { 
     
     @Override 
     public String getName() { 
      // TODO Auto-generated method stub 
      return null; 
     } 
     
     @Override 
     public List<String> getOccupations() { 
      // TODO Auto-generated method stub 
      return null; 
     } 
     
     @Override 
     public boolean isEmpty() { 
      // TODO Auto-generated method stub 
      return false; 
     } 
    }
  4. 以下のクラスレベルの注釈で BylineImpl.java を更新して、Sling Model 注釈を追加します。この @Model(..) 注釈は、クラスを Sling Model に変換するものです。

    import org.apache.sling.api.SlingHttpServletRequest; 
    import org.apache.sling.models.annotations.Model; 
    import org.apache.sling.models.annotations.DefaultInjectionStrategy; 
    ... 
    @Model( 
            adaptables = {SlingHttpServletRequest.class}, 
            adapters = {Byline.class}, 
            resourceType = {BylineImpl.RESOURCE_TYPE}, 
            defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL 
    ) 
    public class BylineImpl implements Byline { 
        protected static final String RESOURCE_TYPE = "wknd/components/content/byline"; 
        ... 
    }

    この注釈とそのパラメーターを確認してみましょう 。

    • @Model 注釈は、AEM にデプロイされる際に、BylineImpl を Sling Model として登録します。
    • adaptables パラメーターは、このモデルがリクエストによって適合できることを指定します。
    • adapters パラメーターを使用すると、実装クラスを Byline インターフェイスの下に登録できます。これによって、 HTL スクリプトでインターフェイスで Sling Model を呼び出すことができます(直接 impl ではなく)。adapters について詳しくは、こちらを参照してください。
    • resourceType は、(既に作成した)署名コンポーネントリソースタイプを指し、複数の実装がある場合に正しいモデルを解決するのに役立ちます。モデルクラスのリソースタイプとの関連付けについて詳しくは、こちらを参照してください。

Sling Model メソッドの実装

getName() の実装

最初に取り組むメソッドは、getName() です。これは、単に署名の JCR コンテンツノードの name プロパティに保存された値を返します。

この場合、@ValueMapValue Sling Model 注釈を使用して、Sling Model の Java フィールドに値を挿入できます。

...  
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;  
...  
public class BylineImpl implements Byline { 
    ... 
 
    @ValueMapValue 
    private String name; 
 
    ... 
    @Override 
    public String getName() { 
        return name; 
    } 
    ... 
}

JCR プロパティは Java フィールドと同じ名前を共有する(両方とも「name」)ので、@ValueMapValue は自動的にこの関連付けを解決し、プロパティ名の値を Java フィールドに挿入します。

JCR プロパティに異なる名前が設定されていた場合、追加の注釈を使用して値の挿入を強制できます。例えば、JCR プロパティが fullName という名前だった場合、以下は Java フィールド name にその値を挿入します。

import org.apache.sling.models.annotations.injectorspecific.ValueMapValue; 
import javax.inject.Named; 
... 
 
@ValueMapValue 
@Named("fullName") 
private String name;

getOccupations() の実装

次に実装するメソッドは、getOccupations() です。このメソッドは、JCR プロパティ occupations に保存されたすべての職業を収集し、それらのコレクションを(アルファベット順に)並べ替えて返す必要があります。

getName() で説明したのと同じ手法を使用して、プロパティ値を Sling Model に挿入できます。

@ValueMapValue 
private List<String> occupations;

挿入した Java フィールド「occupations」によって JCR プロパティ値が Sling Model で使用できるようになり、並べ替えられたビジネスロジックを getOccupations() メソッドに適用できるようになりました。

import java.util.Collections; 
... 
 
public class BylineImpl implements Byline { 
... 
 
public List<String> getOccupations() { 
    if (occupations != null) { 
        Collections.sort(occupations); 
        return occupations; 
    } else { 
        return Collections.emptyList(); 
    } 
} 
... 
} 
...

isEmpty() の実装

最後のパブリックメソッドは、isEmpty() です。これによって、コンポーネント自体がレンダリングするのに十分な状態かどうかを検討すべきタイミングを判定します。

このコンポーネントの場合、名前、画像および職業の 3 つすべてのフィールドに入力する必要があるというビジネス要件があります。

import org.apache.commons.lang3.StringUtils; 
... 
public class BylineImpl implements Byline { 
... 
@Override 
public boolean isEmpty() { 
    if (StringUtils.isBlank(name)) { 
        // Name is missing, but required 
        return true; 
    } else if (occupations == null || occupations.isEmpty()) { 
        // At least one occupation is required 
        return true; 
    } else if (/* image is not null*/) { 
        // A valid image is required 
        return true; 
    } else { 
  // Everything is populated, so this component is not considered empty 
        return false; 
    } 
} 
... 
 
}

「画像の問題」への対処

名前および職業の条件を確認することは単純です(Apache Commons Lang3 は常に便利な StringUtils クラスを提供します)が、コアコンポーネントの画像コンポーネントを使用して画像を表示しているので、画像の存在を検証できる方法が明確でないという問題があります。

これに対処するには、2 つの方法があります。

1.fileReference JCR プロパティがアセットを解決しているかを確認する。

または

2.このリソースをコアコンポーネントの画像 Sling Model に変換し、getSrc() メソッドが空でないことを確認する。

ここでは、2 番目の方法を選択します。最初の方法でおそらく十分ですが、このチュートリアルでは、後者を使用して Sling Model の他の機能を確認します。

最初に、画像を取得するプライベートメソッドを作成します。このメソッドをプライベートにしておくのは、HTL 自体の画像オブジェクトを公開する必要がなく、isEmpty() を実行するためにのみ使用するからです。

import com.adobe.cq.wcm.core.components.models.Image; 
... 
private Image getImage() {  
    Image image = null; 
    // Figure out how to populate the image variable! 
    return image; 
}

画像 Sling Model を取得するには、2 つの方法があります。

1.@Self 注釈を使用し、現在のリクエストを Image.class に自動的に適応します。
@Self
private Image image;

または

2.  Apache Sling ModelFactory OSGi サービスを使用します。これは非常に便利なサービスで、Java コードで他のタイプの Sling Model を作成するのに役立ちます。

ここでは、2 番目の方法を選択します。

注意:

実際の実装では、よりシンプルで洗練されたソリューションである、@Self を使用した 1 番目の方法をお勧めします。このチュートリアルでは、Sling Model のより多くのファセットを説明する必要があるので、2 番目の方法を使用します。

Sling Model は Java POJO であり、OSGi サービスではないので、通常の OSGi 挿入注釈( @Reference など)は使用できません。つまり、以下は機能しません(modelFactory が null になる)。

import org.apache.sling.models.factory.ModelFactory; 
... 
public class BylineImpl implements Byline { 
    ... 
     // will be null, so will not use @Reference annotation 
    @Reference 
    private ModelFactory modelFactory; 
} 

代わりに、Sling Model は、同様の機能を持つ特別な @OSGiService 注釈を提供します。

import org.apache.sling.models.factory.ModelFactory; 
import org.apache.sling.models.annotations.injectorspecific.OSGiService; 
 
public class BylineImpl implements Byline { 
    ... 
    @OSGiService 
    private ModelFactory modelFactory; 
}

ModelFactory が使用できるので、コアコンポーネントの画像 Sling Model は、modelFactory.getModelFromWrappedRequest(SlingHttpServletRequest request, Resource resource, java.lang.Class<T> targetClass) を使用して作成できますが、このメソッドにはリクエストとリソースの両方が必要で、どちらもまだ Sling Model で使用できません。

このため、より多くの Sling Model 注釈が使用されます。

現在のリクエストを取得するには、@Self 注釈 を 使用して、adaptable@Model(..)SlingHttpServletRequest.class として定義)を Java クラスフィールドに挿入します。

import org.apache.sling.models.annotations.injectorspecific.Self; 
... 
@Self 
private SlingHttpServletRequest request;

前述のとおり、@Self Image 画像を使用してコアコンポーネントの画像 Sling Model を挿入することは、オプションです。@Self 注釈は、適応可能なオブジェクト(この場合 SlingHttpServletRequest)を挿入し、注釈フィールドタイプに適応しようとします。コアコンポーネントの画像 Sling Model は SlingHttpServletRequest オブジェクトから適応可能なので、これなら機能したはずです。また、説明的な 2 番目の方法よりも少ないコードで済みます。

これで、ModelFactory API で画像モデルをインスタンス化するために必要な変数を挿入できました。次に、Sling Model の @PostConstruct 注釈を使用して、Sling Model をインスタンス化した後、このオブジェクトを取得します。

@PostConstruct は、非常に便利で、コンストラクターと同じ機能で動作しますが、クラスがインスタンス化され、すべての注釈が設定された Java フィールドが挿入された後で呼び出されます。他の Sling Model 注釈が Java クラスフィールド(変数)に注釈を付けるのに対して、@PostConstruct は、通常は init() という名前(ただしどのような名前でも可)の無効なゼロパラメーターメソッドに注釈を付けます。

import javax.annotation.PostConstruct; 
... 
public class BylineImpl implements Byline { 
         ... 
 private Image image; 
 
 @PostConstruct 
 private void init() { 
  image = modelFactory.getModelFromWrappedRequest(request, request.getResource(), Image.class); 
 } 
    ... 
} 

Sling Model は OSGi サービスではないので、クラスの状態を安全に管理できます。多くの場合、@PostConstruct は、プレーンコンストラクターと同様、後で使用するために Sling Model クラス状態を派生および設定します。

@PostConstruct メソッドが例外をスローする場合、Sling Model はインスタンス化されない(null になる)ことに注意してください。

getImage() が更新されて、画像オブジェクトを返すことができるようになりました。

/** 
 * @return the Image Sling Model of this resource, or null if the resource cannot create a valid Image Sling Model.  
 */ 
@Nullable 
private Image getImage() {  
 return image; 
}

次に isEmpty() に戻り、実装を完了させます。

@Override 
public boolean isEmpty() {     
    if (StringUtils.isBlank(name)) { 
        // Name is missing, but required 
        return true; 
    } else if (occupations == null || occupations.isEmpty()) { 
        // At least one occupation is required 
        return true; 
    } else if (getImage() == null || StringUtils.isBlank(getImage().getSrc())) { 
        // A valid image is required 
        return true; 
    } else { 
        // Everything is populated, so this component is not considered empty 
        return false; 
    } 
}

getImage() に対する複数の呼び出しは、初期化された image クラス変数が返され、modelFactory.getModelFromWrappedRequest(...) を呼び出さないので、それほどコストがかかるわけではなく、問題になりませんが、不要な呼び出しは避けるべきです。

最終的な BylineImpl.java は以下のようになります。

package com.adobe.aem.guides.wknd.core.components.impl; 
 
import java.util.Collections; 
import java.util.List; 
 
import javax.annotation.PostConstruct; 
 
import org.apache.commons.lang3.StringUtils; 
import org.apache.sling.api.SlingHttpServletRequest; 
import org.apache.sling.models.annotations.DefaultInjectionStrategy; 
import org.apache.sling.models.annotations.Model; 
import org.apache.sling.models.annotations.injectorspecific.OSGiService; 
import org.apache.sling.models.annotations.injectorspecific.Self; 
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue; 
import org.apache.sling.models.factory.ModelFactory; 
 
import com.adobe.aem.guides.wknd.core.components.Byline; 
import com.adobe.cq.wcm.core.components.models.Image; 
import com.drew.lang.annotations.Nullable; 
 
@Model( 
        adaptables = {SlingHttpServletRequest.class}, 
        adapters = {Byline.class}, 
        resourceType = {BylineImpl.RESOURCE_TYPE}, 
        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL 
) 
public class BylineImpl implements Byline { 
    protected static final String RESOURCE_TYPE = "wknd/components/content/byline"; 
 
    @Self 
    private SlingHttpServletRequest request; 
     
    @OSGiService 
    private ModelFactory modelFactory; 
     
    @ValueMapValue 
    private String name; 
     
    @ValueMapValue 
    private List<String> occupations; 
     
    private Image image; 
     
    @PostConstruct 
    private void init() { 
        image = modelFactory.getModelFromWrappedRequest(request, request.getResource(), Image.class); 
    } 
     
    @Override 
    public String getName() { 
        return name; 
    } 
     
    @Override 
    public List<String> getOccupations() { 
         if (occupations != null) { 
             Collections.sort(occupations); 
             return occupations; 
         } else { 
             return Collections.emptyList(); 
         } 
    } 
 
    @Override 
    public boolean isEmpty() { 
        final Image image = getImage(); 
         
        if (StringUtils.isBlank(name)) { 
            // Name is missing, but required 
            return true; 
        } else if (occupations == null || occupations.isEmpty()) { 
            // At least one occupation is required 
            return true; 
        } else if (image == null || StringUtils.isBlank(image.getSrc())) { 
            // A valid image is required 
            return true; 
        } else { 
            // Everything is populated, so this component is not considered empty 
            return false; 
        } 
    } 
     
    /** 
     * @return the Image Sling Model of this resource, or null if the resource cannot create a valid Image Sling Model.  
     */ 
    @Nullable 
    private Image getImage() { 
        return image; 
    } 
}

署名 HTL

ui.apps モジュールで、以前の AEM コンポーネントの設定で作成した /apps/wknd/components/content/byline/byline.html を開きます。

<div data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html"> 
</div> 
<sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=false}"></sly>

この HTL スクリプトでおこなうことを確認しましょう。

  1. placeholderTemplate は、コアコンポーネントのプレースホルダーを指します。これは、コンポーネントが完全に設定されていない場合に表示されます。これは、AEM Sites ページエディターで、コンポーネントタイトルを含むグレーのボックスとしてレンダリングされます(前述の cq :Component の jcr :title プロパティを参照)。
  2. data-sly-call="${placeholderTemplate.placeholder @ isEmpty=false} は、既に定義した placeholderTemplate を読み込み、ブール値(現在は false にハードコードされている)でプレースホルダーテンプレートに渡します。isEmpty が true の場合、プレースホルダーテンプレートは、グレーのボックスをレンダリングし、そうでない場合は何もレンダリングしません。

以下のスケルタル HTML 構造で byline.html を更新します。

<div data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html"      
     class="cmp-byline"> 
        <div class="cmp-byline__image"> 
            <!-- Include the Core Components Image Component --> 
        </div> 
        <h2 class="cmp-byline__name"><!-- Include the name --></h2> 
        <p class="cmp-byline__occupations"><!-- Include the occupations --></p> 
</div> 
<sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=true}"></sly>

CSS クラスは BEM 命名規則に従うことに注意してください。BEM 規則の使用は必須ではありませんが、コアコンポーネント CSS クラスで使用され、一般的にクリーンで読みやすい CSS ルールになるので、BEM をお勧めします。

HTL での Sling Model オブジェクトのインスタンス化

Use ブロックステートメント は、HTL スクリプトで Sling Model オブジェクトのインスタンス化に使用され、HTL 変数に割り当てられるので、最も一般的なものの一つです。

data-sly-use.byline="com.adobe.aem.guides.wknd.components.Byline" は、BylineImpl によって実装された Byline インターフェイス(com.adobe.aem.guides.wknd.components.Byline)を使用し、現在の SlingHttpServletRequest を適応させて、結果を HTL 変数名署名(data-sly-use.<variable-name>)に保存します。

<div data-sly-use.byline="com.adobe.aem.guides.wknd.core.components.Byline" 
     data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html"      
     class="cmp-byline">...</div>

Sling Model メソッドへのアクセス

HTL は、 JSTL から借りて、同じ Java getter メソッド名の短縮形を使用します。

例えば、署名 Sling Model の getName() メソッドの呼び出しは byline.name に短縮でき、同様に、byline.isEmpty の代わりに byline.empty に短縮できます。完全なメソッド名の byline.getNamebyline.isEmpty も機能します。HTL でメソッドを呼び出すのに () は使用されないことに注意してください(JSTL と同様)。

パラメーターを必要とする Java メソッドは HTL では使用できません。これは、HTL のロジックをシンプルにするための設計によるものです。

署名の名前は、署名 Sling Model の getName() メソッドを呼び出すか、HTL で ${byline.name} を呼び出すことで、コンポーネントに追加できます。

<h2 class="cmp-byline__name">${byline.name}</h2> 

HTL 式のオプションの使用

HTL 式のオプションは、 HTL のコンテンツの修飾子として機能し、日付形式から多言語翻訳まで多岐にわたります。また、式は、リストの結合や値の配列に使用でき、職業をコンマ区切り形式で表示するのに必要です。

式は、HTL 式の @ 演算子を使用して追加されます。職業のリストを「,」で結合するには、以下のコードを使用します。

<p class="cmp-byline__occupations">${byline.occupations @ join=', '}</p> 

プレースホルダーの条件付き表示

AEM コンポーネントのほとんどの HTL スクリプトでは、プレースホルダーパラダイムを活用して、コンポーネントが間違って作成され、AEM パブリッシュに表示されないことを作成者に視覚的に提示します。この判断を推進するには、コンポーネントの背後の Sling Model のメソッド(この場合 Byline.isEmpty())を実装する必要があります。

isEmpty() は、署名 Sling Model で呼び出され、結果(否定の場合は「!」演算子を使用)は、hasContent という名前の HTL 変数に保存されます。

<div data-sly-use.byline="com.adobe.aem.guides.wknd.core.components.Byline" 
     data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html"      
     data-sly-test.hasContent="${!byline.empty}" 
     ...>

HTL test ブロックの data-sly-test の使用は、両方とも HTL 変数を設定し、HTL 式が真かどうかの結果に基づいて HTML 要素をレンダリングする/しないという点で、興味深いものです。真の場合は HTML 要素をレンダリングし、そうでない場合はレンダリングしません。

この HTL 変数 hasContent は、条件付きでプレースホルダーを表示/非表示するために再利用できます。

<sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=!hasContent}"></sly>

コアコンポーネントの画像コンポーネントを使用した画像の表示

ここまでで byline.html の HTL スクリプトは、画像がないだけで、ほとんど完成しています。

<div data-sly-use.byline="com.adobe.aem.guides.wknd.core.components.Byline" 
     data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html" 
     data-sly-test.hasContent="${!byline.empty}" 
     class="cmp-byline"> 
        <div class="cmp-byline__image"> 
            <!-- Include the Core Components Image component --> 
        </div> 
        <h2 class="cmp-byline__name">${byline.name}</h2> 
        <p class="cmp-byline__occupations">${byline.occupations @ join=', '}</p> 
</div> 
<sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=!hasContent}"></sly>

コアコンポーネントの画像コンポーネント sling:resourceSuperTyped を使用して画像のオーサリングを提供しているので、コアコンポーネントの画像コンポーネントを使用して画像をレンダリングすることもできます。

この場合、現在の署名リソースを含める必要がありますが、リソースタイプ core/ wcm /components/image/v2/image を使用して、コアコンポーネントの画像コンポーネントのリソースタイプを強制します。これは、コンポーネントをお互いに再利用するための強力な手本となります。この場合、HTL の data-sly-resource ブロックが使用されます。

<div class="cmp-byline__image" 
     data-sly-resource="${ '.' @ resourceType = 'core/wcm/components/image/v2/image' }"></div>

この data-sly-resource は、相対パス「.」を使用して現在のリソースを含み、core/wcm/components/image/v2/image の resourceType を含むことを強制します。

コアコンポーネントのリソースタイプは、スクリプト内で使用され、コンテンツに対して保持されないので、プロキシ経由ではなく直接使用されます。

完成した byline.html を以下に示します。

<div data-sly-use.byline="com.adobe.aem.guides.wknd.core.components.Byline" 
     data-sly-use.placeholderTemplate="core/wcm/components/commons/v1/templates.html" 
     data-sly-test.hasContent="${!byline.empty}" 
     class="cmp-byline"> 
       <div class="cmp-byline__image" 
       data-sly-resource="${ '.' @ resourceType = 'core/wcm/components/image/v2/image' }"> 
      </div> 
        <h2 class="cmp-byline__name">${byline.name}</h2> 
        <p class="cmp-byline__occupations">${byline.occupations @ join=','}</p> 
</div> 
<sly data-sly-call="${placeholderTemplate.placeholder @ isEmpty=!hasContent}"></sly>

コードのデプロイ

更新したコードを AEM にデプロイします。

スタイルが設定されていない署名コンポーネントの確認

更新をデプロイしたら、画像が表示され、スタイルは設定されていないものの機能する署名コンポーネントが表示されます。

Sling Model 登録の確認

AEM Web コンソールの Sling Models Status 表示には、AEM に登録されたすべての Sling Model が表示されます。署名 Sling Model は、このリストを確認することで、インストールされ、認識されていることを検証できます。

BylineImpl がこのリストに表示されない場合、Sling Model の注釈またはコア pom.xml の SlingModelsScanner モジュール設定に問題がある可能性があります。

http://localhost:4502/system/console/status-slingmodels

署名のスタイル

署名コンポーネントは、署名コンポーネントのクリエイティブデザインに沿ってスタイルを設定する必要があります。これは、クライアントライブラリを通じて AEM によるサポートを提供する LESS を使用して実現します。LESS は、署名コンポーネント用の適切なデフォルトスタイルを提供するのに必要な CSS を生成します。

スタイル設定後、署名コンポーネントは以下のような外観になります。

WKND クリエイティブチームによって定義されたとおりの署名コンポーネントデザイン

署名コンポーネントのデフォルトスタイルを追加します。/apps/wknd/clientlibs/clientlib-site/components の下の ui.apps プロジェクトで、次の手順をおこないます。

  1. byline という名前の新しいフォルダーを作成します。

  2. byline フォルダーの下に byline.less という名前の新しいファイルを作成します。

  3. byline フォルダーの下に styles という名前の新しいフォルダーを作成します。

  4. byline および styles フォルダーの下に default.less という名前の新しいファイルを作成します。

  5. 最初に、byline.less に以下を入力してデフォルトスタイルを含めます。

    /* WKND Byline styles */ 
     
    @import (once) "styles/default.less";
  6. ステップテキスト

    byline.html の要素に適用した BEM クラスから「スケルトン」を作成します。BEM クラスのみをスタイル設定し、ルールの重みの競合を避けるために、CSS セレクタールールの深さを浅くする必要があります。

.cmp-byline { 
    .cmp-byline__image { 
        // Align to the left of Name and Occupations 
 
        .cmp-image__image { 
            // This is the BEM class applied by the embedded Core Image component to the actual img tag 
            // Create a 60px x 60px, circular image 
        } 
    } 
 
    .cmp-byline__name { 
        // Default text color 
        // Right of image 
    } 
 
    .cmp-byline__occupations { 
        // Color grey 
        // All uppercase 
        // Align right of image 
    } 
}

CSS クラスのこのスケルタルフレームワークから始めることで、クリーンな CSS をより簡単に記述できるようにし、 非 BEM CSS クラスやむき出しの要素でスタイル設定する誘惑を減らします。

/apps/wknd/clientlibs/clientlib-site/site/less/variables.less から以前定義した LESS 変数をスタイル実装にドロップして再利用します。

/* WKND Byline - default.less*/ 
  
.cmp-byline { 
    @imageSize: 60px; 
 
    .cmp-byline__image { 
  float: left; 
   
  /* This class targets a Core Component Image CSS class */ 
     .cmp-image__image { 
   width: @imageSize; 
   height: @imageSize; 
   border-radius: @imageSize / 2; 
   object-fit: cover; 
  } 
    } 
 
    .cmp-byline__name { 
     margin-left: @imageSize + 20px; 
     margin-bottom: .25rem; 
    } 
 
    .cmp-byline__occupations { 
     margin-left: @imageSize + 20px; 
  color: @gray-light; 
  font-size: @font-size-small; 
  text-transform: uppercase; 
  font-weight: 600; 
    } 
}

署名スタイルを WKND サイトのクライアントライブラリに含める

/apps/wknd/clientlibs/clientlib-site/main.less を更新して、byline.less を含めます。

読み込みステートメントを追加して、コンポーネントスタイルの他の読み込みの下に byline .less を含めます。

/* WKND main.less */ 
... 
/* Import WKND Components styles */ 
... 
@import "components/byline/byline.less";

コードのデプロイ

コードを AEM にデプロイし、ブラウザーキャッシュをクリアして古くなった CSS が提供されないようにし、スタイルが完全に適用されるように署名コンポーネントを含むページを更新します。

まとめ

以下に、完全に作成され、スタイル設定された署名コンポーネントが AEM ページでどのように見えるかを示します。

第 6 章の確認

WKND チュートリアルのこのパートで作成したものをすばやく確認するには、以下のビデオをご覧ください。

ヘルプ

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

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

次の手順

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

ダウンロード

トラブルシューティング

Eclipse で src/main/java ソースフォルダーが表示されない場合、src を右クリックして main および java のフォルダーを追加することで、フォルダーを追加できます。フォルダーを追加したら、src/main/java パッケージが表示されます。

コアプロジェクトに追加された新しい依存関係用に未解決のパッケージを読み込む場合、wknd-sites-guide.core Maven プロジェクトを更新してみてください。wknd-sites-guide.core を右クリックして、Maven/Update Project を選択することで、これをおこなうことができます。

アドビのロゴ

アカウントにログイン