コアコンポーネントの拡張 extend-component
既存のコアコンポーネントを拡張して、AEM SPA エディターで使用する方法を説明します。 既存のコンポーネントの拡張方法を理解することは、AEM SPA エディター実装の機能をカスタマイズおよび拡張するための強力な手法です。
目的
- 追加のプロパティやコンテンツを使用して、既存のコアコンポーネントを拡張する
sling:resourceSuperType
を使用したコンポーネントの継承の基本を理解する- Sling モデルでデリゲーションパターンを使用して既存のロジックと機能を再利用する方法を学ぶ
作成する内容
この章では、新しい Card
コンポーネントが作成されます。 Card
コンポーネントは 画像コアコンポーネントを拡張し、タイトルやコールトゥアクションボタンなどのコンテンツフィールドを追加して、SPA 内の他のコンテンツのティーザーの役割を果たします。
Card
コンポーネントを作成するよりも、単純にティーザーコンポーネントを使用する方が適切な場合があります。可能であれば、コア コンポーネントを直接使用することを常にお勧めします。前提条件
ローカル開発環境の設定に必要なツールと手順を確認します。
コードの取得
-
このチュートリアルの出発点となるものを Git からダウンロードします。
code language-shell $ git clone git@github.com:adobe/aem-guides-wknd-spa.git $ cd aem-guides-wknd-spa $ git checkout Angular/extend-component-start
-
Maven を使用してコードベースをローカルの AEM インスタンスにデプロイします。
code language-shell $ mvn clean install -PautoInstallSinglePackage
AEM 6.x を使用する場合は、以下の
classic
プロファイルを追加します。code language-shell $ mvn clean install -PautoInstallSinglePackage -Pclassic
-
従来の WKND リファレンスサイトの完成したパッケージをインストールします。WKND リファレンスサイトから提供された画像は、WKND SPA で再利用されます。パッケージは、AEM のパッケージマネージャーを使用してインストールできます。
いつでも、完成したコードを GitHub で確認したり、ブランチ Angular/extend-component-solution
に切り替えてコードをローカルにチェックアウトしたりできます。
初期カード実装の調査
最初のカードコンポーネントは、章のスターターターコードによって提供されています。カード実装の出発点を調べます。
-
任意の IDE で、
ui.apps
モジュールを開きます。 -
ui.apps/src/main/content/jcr_root/apps/wknd-spa-angular/components/card
に移動して.content.xml
ファイルを表示します。code language-xml <?xml version="1.0" encoding="UTF-8"?> <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" jcr:primaryType="cq:Component" jcr:title="Card" sling:resourceSuperType="wknd-spa-angular/components/image" componentGroup="WKND SPA Angular - Content"/>
プロパティ
sling:resourceSuperType
は、Card
コンポーネントが WKND SPA 画像コンポーネントから機能を継承することを示すwknd-spa-angular/components/image
をポイントします。 -
ui.apps/src/main/content/jcr_root/apps/wknd-spa-angular/components/image/.content.xml
ファイルを調べます。code language-xml <?xml version="1.0" encoding="UTF-8"?> <jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0" xmlns:cq="http://www.day.com/jcr/cq/1.0" xmlns:jcr="http://www.jcp.org/jcr/1.0" jcr:primaryType="cq:Component" jcr:title="Image" sling:resourceSuperType="core/wcm/components/image/v2/image" componentGroup="WKND SPA Angular - Content"/>
sling:resourceSuperType
がcore/wcm/components/image/v2/image
をポイントしていることに注目してください。これは、WKND SPA 画像コンポーネントがコアコンポーネントの画像から機能を継承していることを示しています。プロキシパターンとも呼ばれ、Sling リソースの継承は、子コンポーネントが必要に応じて機能を継承し、動作を拡張/上書きできるようにするための強力なデザインパターンです。 Sling 継承は複数のレベルの継承をサポートするので、最終的に
Card
コンポーネントは、コアコンポーネントの画像の機能を継承します。多くの開発チームは、D.R.Y. (don't repeat yourself:繰り返しを避ける)を目指しています。AEM では、Sling の継承によってこれを実現できます。
-
card
フォルダーの下で、_cq_dialog/.content.xml
ファイルを開きます。このファイルは、
Card
コンポーネントのコンポーネントダイアログ定義です。Sling 継承を使用している場合、Sling Resource Merger 機能を使用して、ダイアログの一部を上書きまたは拡張することができます。このサンプルでは、作成者から追加データをキャプチャしてカードコンポーネントに入力するための新しいタブがダイアログに追加されました。sling:orderBefore
のようなプロパティを使用すると、デベロッパーは新しいタブまたはフォームフィールドを挿入する場所を選択できます。この場合、「Text
」タブは「asset
」タブの前に挿入されます。Sling Resource Merger を最大限に活用するには、画像コンポーネントダイアログの元のダイアログノード構造を知っておくことが重要です。 -
card
フォルダーの下の、_cq_editConfig.xml
ファイルを開きます。このファイルは、AEM オーサリング UI でのドラッグ&ドロップ動作を示します。 Image コンポーネントを拡張する場合、リソースタイプがコンポーネント自体に一致することが重要です。<parameters>
ノードを確認します。code language-xml <parameters jcr:primaryType="nt:unstructured" sling:resourceType="wknd-spa-angular/components/card" imageCrop="" imageMap="" imageRotate=""/>
ほとんどのコンポーネントは
cq:editConfig
を必要としません。画像、および画像コンポーネントの子の子孫は例外です。 -
IDE で
ui.frontend
モジュールに切り替え、ui.frontend/src/app/components/card
に移動します。 -
card.component.ts
ファイルを調べます。コンポーネントは、標準の
MapTo
関数を使用して AEMCard
コンポーネントにマッピングするために既にスタブ化されています。code language-js MapTo('wknd-spa-angular/components/card')(CardComponent, CardEditConfig);
クラスで、
src
、alt
、title
の 3 つの@Input
パラメーターを確認してください。これらは、Angular コンポーネントにマッピングされる AEM コンポーネントからの期待される JSON 値です。 -
card.component.html
ファイルを開きます。code language-html <div class="card" *ngIf="hasContent"> <app-image class="card__image" [src]="src" [alt]="alt" [title]="title"></app-image> </div>
この例では、
card.component.ts
から@Input
パラメーターを渡すだけで、既存の Angular 画像コンポーネントapp-image
を再利用することを選択しました。チュートリアルの後半で、追加のプロパティが追加および表示されます。
テンプレートポリシーの更新
この最初の Card
の実装では、AEM SPA エディターで機能を確認します。 最初の Card
コンポーネントを表示するには、テンプレートポリシーを更新する必要があります。
-
スターターコードを AEM のローカルインスタンスにデプロイします(まだデプロイしていない場合)。
code language-shell $ cd aem-guides-wknd-spa $ mvn clean install -PautoInstallSinglePackage
-
http://localhost:4502/editor.html/conf/wknd-spa-angular/settings/wcm/templates/spa-page-template/structure.html にある SPA ページテンプレートに移動します。 .
-
レイアウトコンテナのポリシーを更新して、新しい
Card
コンポーネントを許可されたコンポーネントとして追加します。変更をポリシーに保存し、
Card
コンポーネントが許可されたコンポーネントになっていることを確認します。
最初のカードコンポーネントの作成
次に、AEM SPA エディターを使用して Card
コンポーネントを作成します。
-
http://localhost:4502/editor.html/content/wknd-spa-angular/us/en/home.html に移動します。
-
Edit
モードで、Card
コンポーネントをLayout Container
に追加します。 -
アセットファインダーから
Card
コンポーネントに画像をドラッグ&ドロップします。 -
Card
コンポーネントダイアログを開き、「テキスト」タブが追加されていることを確認してください。 -
次の値を「テキスト」タブに入力します。
カードのパス - SPA ホームページの下のページを選択します。
CTA テキスト - 「続きを読む」
カードタイトル - 空白のまま
リンクされたページからタイトルを取得する - true を示すチェックボックスをオンにします。
-
「アセットメタデータ」タブを更新して、「代替テキスト」と「キャプション」の値を追加します。
現在は、ダイアログの更新後に追加の変更は表示されません。 新しいフィールドを Angular コンポーネントに公開するには、
Card
コンポーネントの Sling モデルを更新する必要があります。 -
新しいタブを開き、CRXDE-Lite に移動します。
/content/wknd-spa-angular/us/en/home/jcr:content/root/responsivegrid
の下のコンテンツノードを調べて、Card
コンポーネントコンテンツを見つけます。プロパティ
cardPath
、ctaText
、titleFromPage
がダイアログによって保持されていることを確認します。
カード Sling モデルを更新する
コンポーネントダイアログの値を最終的に Angular コンポーネントに公開するには、Card
コンポーネントの JSON を入力する Sling モデルを更新する必要があります。また、次の 2 つのビジネスロジックを実装することもできます。
titleFromPage
が true の場合、cardPath
で指定されたページのタイトルを返します。それ以外の場合は、cardTitle
テキストフィールドの値を返します。cardPath
で指定されたページの最終更新日を返します。
任意の IDE に戻り、core
モジュールを開きます。
-
core/src/main/java/com/adobe/aem/guides/wknd/spa/angular/core/models/Card.java
のCard.java
ファイルを開きます。Card
インターフェイスは現在com.adobe.cq.wcm.core.components.models.Image
を拡張しているため、Image
インターフェイスのメソッドを継承していることに注意してください。Image
インターフェイスはすでにComponentExporter
インターフェイスを拡張しているため、Sling モデルを JSON としてエクスポートし、SPA エディターでマッピングできます。したがって、カスタムコンポーネントの章で行ったように、ComponentExporter
インターフェイスを明示的に拡張する必要はありません。 -
インターフェイスに次のメソッドを追加します。
code language-java @ProviderType public interface Card extends Image { /*** * The URL to populate the CTA button as part of the card. * The link should be based on the cardPath property that points to a page. * @return String URL */ public String getCtaLinkURL(); /*** * The text to display on the CTA button of the card. * @return String CTA text */ public String getCtaText(); /*** * The date to be displayed as part of the card. * This is based on the last modified date of the page specified by the cardPath * @return */ public Calendar getCardLastModified(); /** * Return the title of the page specified by cardPath if `titleFromPage` is set to true. * Otherwise return the value of `cardTitle` * @return */ public String getCardTitle(); }
これらのメソッドは、JSON モデル API を介して公開され、Angular コンポーネントに渡されます。
-
CardImpl.java
を開きます。これは、Card.java
インターフェイスの実装です。この実装は、チュートリアルを高速化するために部分的にスタブ化されました。@Model
および@Exporter
アノテーションを使用して、Sling モデルエクスポーターを介して Sling モデルを JSON としてシリアル化できることを確認してください。また、
CardImpl.java
は Sling モデルの委任パターンを使用して、画像コアコンポーネントからのロジックの書き換えを回避します。 -
次の行をご覧ください。
code language-java @Self @Via(type = ResourceSuperType.class) private Image image;
上記のアノテーションは、
Card
コンポーネントのsling:resourceSuperType
継承に基づいてimage
という名前の画像オブジェクトをインスタンス化します。code language-java @Override public String getSrc() { return null != image ? image.getSrc() : null; }
その場合、
image
オブジェクトを使用してImage
インターフェイスで定義されたメソッドを実装することができるので、自分でロジックを書く必要はありません。この手法は、getSrc()
、getAlt()
、getTitle()
に使用されます。 -
次に、
cardPath
の値に基づいてプライベート変数cardPage
を開始するinitModel()
メソッドを実装します。code language-java @PostConstruct public void initModel() { if(StringUtils.isNotBlank(cardPath) && pageManager != null) { cardPage = pageManager.getPage(this.cardPath); } }
@PostConstruct initModel()
は Sling モデルが初期化されるときに呼び出されるため、モデル内の他のメソッドで使用される可能性があるオブジェクトを初期化する良い機会です。pageManager
は、@ScriptVariable
アノテーションを介して Sling モデルで使用できる、Java™ でサポートされたグローバルオブジェクトの 1 つです。getPage メソッドはパスを受け取り、AEM Page オブジェクトを返します(または、パスが有効なページを指していない場合は null を返します)。これにより
cardPage
変数が初期化されます。この変数は、他の新しいメソッドによって使用され、基になるリンクされたページに関するデータが返されます。 -
作成者ダイアログで保存された JCR プロパティにすでにマップされているグローバル変数を確認します。
@ValueMapValue
アノテーションは、マッピングを自動的に実行するために使用されます。code language-java @ValueMapValue private String cardPath; @ValueMapValue private String ctaText; @ValueMapValue private boolean titleFromPage; @ValueMapValue private String cardTitle;
これらの変数は、
Card.java
インターフェイスの追加メソッドを実装するために使用されます。 -
Card.java
インターフェイスで定義された追加のメソッドを実装します。code language-java @Override public String getCtaLinkURL() { if(cardPage != null) { return cardPage.getPath() + ".html"; } return null; } @Override public String getCtaText() { return ctaText; } @Override public Calendar getCardLastModified() { if(cardPage != null) { return cardPage.getLastModified(); } return null; } @Override public String getCardTitle() { if(titleFromPage) { return cardPage != null ? cardPage.getTitle() : null; } return cardTitle; }
note note NOTE 完成した CardImpl.java はこちらで確認できます。 -
ターミナルウィンドウを開き、
core
ディレクトリの MavenautoInstallBundle
プロファイルを使用してcore
モジュールへの更新のみをデプロイします。code language-shell $ cd core/ $ mvn clean install -PautoInstallBundle
AEM 6.x を使用している場合は、
classic
プロファイルを追加します。 -
http://localhost:4502/content/wknd-spa-angular/us/en.model.json で JSON モデルの応答を確認し、
wknd-spa-angular/components/card
を検索します。code language-json "card": { "ctaText": "Read More", "cardTitle": "Page 1", "title": "Woman chillaxing with river views in Australian bushland", "src": "/content/wknd-spa-angular/us/en/home/_jcr_content/root/responsivegrid/card.coreimg.jpeg/1595190732886/adobestock-216674449.jpeg", "alt": "Female sitting on a large rock relaxing in afternoon dappled light the Australian bushland with views over the river", "cardLastModified": 1591360492414, "ctaLinkURL": "/content/wknd-spa-angular/us/en/home/page-1.html", ":type": "wknd-spa-angular/components/card" }
CardImpl
Sling モデルのメソッドを更新した後、追加のキーと値のペアで JSON モデルが更新されていることを確認してください。
Angular コンポーネントを更新する
JSON モデルに ctaLinkURL
、ctaText
、cardTitle
、cardLastModified
の新しいプロパティが入力されたので、これらを表示するように Angular コンポーネントを更新できます。
-
IDE に戻り、
ui.frontend
モジュールを開きます。必要に応じて、新しいターミナルウィンドウから webpack 開発サーバーを起動し、変更をリアルタイムで確認します。code language-shell $ cd ui.frontend $ npm install $ npm start
-
ui.frontend/src/app/components/card/card.component.ts
でcard.component.ts
を開きます。新しいモデルをキャプチャするために@Input
アノテーションを追加します。code language-diff export class CardComponent implements OnInit { @Input() src: string; @Input() alt: string; @Input() title: string; + @Input() cardTitle: string; + @Input() cardLastModified: number; + @Input() ctaLinkURL: string; + @Input() ctaText: string;
-
コールトゥアクションの準備ができているかどうかを確認するメソッドと、
cardLastModified
入力に基づいて日時文字列を返すメソッドを追加します。code language-js export class CardComponent implements OnInit { ... get hasCTA(): boolean { return this.ctaLinkURL && this.ctaLinkURL.trim().length > 0 && this.ctaText && this.ctaText.trim().length > 0; } get lastModifiedDate(): string { const lastModifiedDate = this.cardLastModified ? new Date(this.cardLastModified) : null; if (lastModifiedDate) { return lastModifiedDate.toLocaleDateString(); } return null; } ... }
-
card.component.html
を開き、次のマークアップを追加して、タイトル、行動を促すフレーズ、最終更新日を表示するようにします。code language-html <div class="card" *ngIf="hasContent"> <app-image class="card__image" [src]="src" [alt]="alt" [title]="title"></app-image> <div class="card__content"> <h2 class="card__title"> {{cardTitle}} <span class="card__lastmod" *ngIf="lastModifiedDate">{{lastModifiedDate}}</span> </h2> <div class="card__action-container" *ngIf="hasCTA"> <a [routerLink]="ctaLinkURL" class="card__action-link" [title]="ctaText"> {{ctaText}} </a> </div> </div> </div>
card.component.scss
には、タイトル、最終更新日のスタイルを設定する Sass ルールが既に追加されています。note note NOTE 完了した Angular カードコンポーネントのコードはこちらで確認できます。 -
Maven を使用して、プロジェクトのルートから、すべての変更を AEM にデプロイします。
code language-shell $ cd aem-guides-wknd-spa $ mvn clean install -PautoInstallSinglePackage
-
http://localhost:4502/editor.html/content/wknd-spa-angular/us/en/home.html に移動して、更新されたコンポーネントを確認します。
-
次のようなページを作成するには、既存のコンテンツを再オーサリングできるはずです。
おめでとうございます。 congratulations
これで、AEM コンポーネントの拡張方法と、Sling モデルとダイアログが JSON モデルと連携する仕組みを学びました。
完成したコードは、GitHubで確認できます。また、Angular/extend-component-solution
ブランチに切り替えて、コードをローカルでチェックアウトすることもできます。