擴充核心元件 extend-component

IMPORTANT
新專案中已棄用 SPA 編輯器。Adobe 仍支援現有專案使用此編輯器,但不應用於新專案。目前,AEM 中用於管理 Headless 內容的首選編輯器為:

瞭解如何擴充要與AEM SPA Editor搭配使用的現有核心元件。 瞭解如何擴充現有元件是一項強大的技術,可自訂和擴充AEM SPA Editor實作的功能。

目標

  1. 使用其他屬性和內容擴充現有的核心元件。
  2. 瞭解使用sling:resourceSuperType的元件繼承基本知識。
  3. 瞭解如何為Sling模型使用委派模式以重複使用現有邏輯和功能。

您將要建置的內容

在本章中,已建立新的Card元件。 Card元件會擴充影像核心元件,新增其他內容欄位,例如「標題」和「Call to action」按鈕,以針對SPA內的其他內容執行Teaser角色。

卡片元件的最終製作

NOTE
在真實世界的實作中,視專案需求而定,可能更適合使用Teaser元件,而不是擴充影像核心元件以產生Card元件。 建議您儘可能直接使用核心元件

先決條件

檢閱設定本機開發環境所需的工具與指示。

取得程式碼

  1. 透過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
    
  2. 使用Maven將程式碼庫部署到本機AEM執行個體:

    code language-shell
    $ mvn clean install -PautoInstallSinglePackage
    

    如果使用AEM 6.x,請新增classic設定檔:

    code language-shell
    $ mvn clean install -PautoInstallSinglePackage -Pclassic
    
  3. 安裝傳統WKND參考站台的完成套件。 由WKND參考網站提供的影像會在WKND SPA上重複使用。 可以使用AEM的封裝管理員來安裝封裝。

    封裝管理員安裝wknd.all

您隨時可以在 GitHub 上檢視完成的程式碼,或透過切換到分支 Angular/extend-component-solution 在本機查看程式碼。

檢查初始卡片實施

章節起始程式碼已提供初始卡片元件。 檢查卡片實作的起點。

  1. 在您選擇的IDE中,開啟ui.apps模組。

  2. 瀏覽至ui.apps/src/main/content/jcr_root/apps/wknd-spa-angular/components/card並檢視.content.xml檔案。

    卡片元件AEM定義開始

    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指向wknd-spa-angular/components/image,表示Card元件繼承了WKND SPA影像元件的功能。

  3. 檢查檔案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影像元件繼承了核心元件影像的功能。

    也稱為Proxy模式 Sling資源繼承是一種強大的設計模式,可讓子元件繼承功能並在需要時擴充/覆寫行為。 Sling繼承支援多個層級的繼承,所以新Card元件最終會繼承核心元件影像的功能。

    許多開發團隊都會努力做到自我(請勿重複這點)。 Sling繼承可讓AEM實現此目標。

  4. card資料夾下,開啟檔案_cq_dialog/.content.xml

    此檔案是Card元件的元件對話方塊定義。 如果使用Sling繼承,則可以使用Sling資源合併器的功能來覆寫或擴充對話方塊的部分。 在此範例中,對話方塊中已新增索引標籤,以從作者擷取其他資料並填入卡片元件。

    sling:orderBefore之類的屬性可讓開發人員選擇插入新標籤或表單欄位的位置。 在此情況下,Text索引標籤會插入asset索引標籤之前。 若要充分利用Sling Resource Merger,請務必瞭解影像元件對話方塊的原始對話方塊節點結構。

  5. card資料夾下,開啟檔案_cq_editConfig.xml。 此檔案會指定AEM編寫UI中的拖放行為。 擴充影像元件時,資源型別必須符合元件本身。 檢閱<parameters>節點:

    code language-xml
    <parameters
        jcr:primaryType="nt:unstructured"
        sling:resourceType="wknd-spa-angular/components/card"
        imageCrop=""
        imageMap=""
        imageRotate=""/>
    

    大部分元件不需要cq:editConfig,影像和影像元件的子系下階是例外。

  6. 在IDE切換至ui.frontend模組,瀏覽至ui.frontend/src/app/components/card

    Angular元件開始

  7. 檢查檔案 card.component.ts

    元件已經使用標準MapTo函式截斷,以對應至AEM Card元件。

    code language-js
    MapTo('wknd-spa-angular/components/card')(CardComponent, CardEditConfig);
    

    檢閱類別中srcalttitle的三個@Input引數。 這些是AEM元件中對應至Angular元件的預期JSON值。

  8. 開啟檔案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元件,需要更新範本原則。

  1. 將入門程式碼部署到AEM的本機執行個體(如果尚未部署):

    code language-shell
    $ cd aem-guides-wknd-spa
    $ mvn clean install -PautoInstallSinglePackage
    
  2. 導覽至http://localhost:4502/editor.html/conf/wknd-spa-angular/settings/wcm/templates/spa-page-template/structure.html的SPA頁面範本。

  3. 更新配置容器的原則以將新的Card元件新增為允許的元件:

    更新配置容器原則

    儲存原則的變更,並將Card元件視為允許的元件:

    卡片元件為允許的元件

作者初始卡片元件

接下來,使用AEM SPA編輯器編寫Card元件。

  1. 導覽至http://localhost:4502/editor.html/content/wknd-spa-angular/us/en/home.html

  2. Edit模式中,將Card元件新增至Layout Container

    插入新元件

  3. 將影像從資產尋找器拖放至Card元件上:

    新增影像

  4. 開啟Card元件對話方塊並注意已新增​ 文字 ​索引標籤。

  5. 在​ 文字 ​索引標籤上輸入下列值:

    文字元件索引標籤

    卡片路徑 — 在SPA首頁下方選擇頁面。

    CTA文字 - 「瞭解詳情」

    卡片標題 — 留空

    從連結的頁面取得標題 — 勾選核取方塊以表示True。

  6. 更新​ 資產中繼資料 ​索引標籤以新增​ 替代文字 ​和​ 標題 ​的值。

    更新對話方塊後,目前沒有其他變更顯示。 若要將新欄位公開給Angular元件,我們需要更新Card元件的Sling模型。

  7. 開啟新索引標籤並導覽至CRXDE-Lite。 檢查/content/wknd-spa-angular/us/en/home/jcr:content/root/responsivegrid下的內容節點以尋找Card元件內容。

    CRXDE-Lite component properties

    Observe that properties cardPath, ctaText, titleFromPage are persisted by the dialog.

Update Card Sling Model

To ultimately expose the values from the component dialog to the Angular component, we need to update the Sling Model that populates the JSON for the Card component. We also have the opportunity to implement two pieces of business logic:

  • If titleFromPage to true, return the title of the page specified by cardPath otherwise return the value of cardTitle textfield.
  • Return the last modified date of the page specified by cardPath.

Return to the IDE of your choice and open the core module.

  1. core/src/main/java/com/adobe/aem/guides/wknd/spa/angular/core/models/Card.java開啟檔案Card.java

    Observe that the Card interface currently extends com.adobe.cq.wcm.core.components.models.Image and therefore inherits the methods of the Image interface. The Image interface already extends the ComponentExporter interface which allows the Sling Model to be exported as JSON and mapped by the SPA editor. Therefore we do not need to explicitly extend ComponentExporter interface like we did in the Custom Component chapter.

  2. Add the following methods to the interface:

    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();
    }
    

    These methods are exposed via the JSON model API and passed to the Angular component.

  3. 開啟 CardImpl.java。 This is the implementation of Card.java interface. This implementation has been partially stubbed out to accelerate the tutorial. Notice the use of the @Model and @Exporter annotations to ensure that the Sling Model is able to be serialized as JSON via the Sling Model Exporter.

    CardImpl.java also uses the Delegation pattern for Sling Models to avoid rewriting the logic from the Image Core Component.

  4. Observe the following lines:

    code language-java
    @Self
    @Via(type = ResourceSuperType.class)
    private Image image;
    

    The above annotation instantiates an Image object named image based on the sling:resourceSuperType inheritance of the Card component.

    code language-java
    @Override
    public String getSrc() {
        return null != image ? image.getSrc() : null;
    }
    

    It is then possible to simply use the image object to implement methods defined by the Image interface, without having to write the logic ourselves. This technique is used for getSrc(), getAlt(), and getTitle().

  5. Next, implement the initModel() method to initiate a private variable cardPage based on the value of cardPath

    code language-java
    @PostConstruct
    public void initModel() {
        if(StringUtils.isNotBlank(cardPath) && pageManager != null) {
            cardPage = pageManager.getPage(this.cardPath);
        }
    }
    

    The @PostConstruct initModel() is called when the Sling Model is initialized, therefore it is a good opportunity to initialize objects that may be used by other methods in the model. The pageManager is one of several Java™ backed global objects made available to Sling Models via the @ScriptVariable annotation. getPage方法接受路徑並傳回AEM Page物件,如果路徑未指向有效頁面,則傳回null。

    這會初始化cardPage變數,其他新方法會使用此變數來傳回基礎連結頁面的相關資料。

  6. 檢閱已對映至作者對話方塊所儲存JCR屬性的全域變數。 @ValueMapValue註解是用來自動執行對應。

    code language-java
    @ValueMapValue
    private String cardPath;
    
    @ValueMapValue
    private String ctaText;
    
    @ValueMapValue
    private boolean titleFromPage;
    
    @ValueMapValue
    private String cardTitle;
    

    這些變數可用來實作Card.java介面的其他方法。

  7. 實作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。
  8. 開啟終端機視窗,並使用core目錄中的Maven autoInstallBundle設定檔僅部署core模組的更新。

    code language-shell
    $ cd core/
    $ mvn clean install -PautoInstallBundle
    

    如果使用AEM 6.x,請新增classic設定檔。

  9. 檢視JSON模型回應: http://localhost:4502/content/wknd-spa-angular/us/en.model.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模型已填入ctaLinkURLctaTextcardTitlecardLastModified的新屬性,我們可以更新Angular元件以顯示這些屬性。

  1. 返回IDE並開啟ui.frontend模組。 您可以選擇從新的終端機視窗啟動webpack開發伺服器,即時檢視變更:

    code language-shell
    $ cd ui.frontend
    $ npm install
    $ npm start
    
  2. 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;
    
  3. 新增檢查Call to action是否準備就緒的方法,以及根據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;
        }
        ...
    }
    
  4. 開啟card.component.html並新增下列標籤以顯示標題、call to action和上次修改日期:

    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規則,以設定標題、call to action和上次修改日期的樣式。

    note note
    NOTE
    您可以在此檢視已完成的Angular卡元件程式碼
  5. 使用Maven從專案的根將完整變更部署到AEM:

    code language-shell
    $ cd aem-guides-wknd-spa
    $ mvn clean install -PautoInstallSinglePackage
    
  6. 導覽至http://localhost:4502/editor.html/content/wknd-spa-angular/us/en/home.html檢視更新的元件:

    已在AEM中更新卡片元件

  7. 您應該能夠重新編寫現有內容以建立類似下列的頁面:

    卡片元件的最終製作

恭喜! congratulations

Congratulations, you learned how to extend an AEM component and how Sling Models and dialogs work with the JSON model.

您隨時可以在 GitHub 上檢視完成的程式碼,或透過切換到分支 Angular/extend-component-solution 在本機查看程式碼。

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