핵심 구성 요소 확장

마지막 업데이트: 2024-01-25
  • 작성 대상:
  • Beginner
    Developer

AEM SPA 편집기에서 사용할 기존 핵심 구성 요소를 확장하는 방법을 알아봅니다. 기존 구성 요소를 확장하는 방법을 이해하는 것은 AEM SPA Editor 구현의 기능을 사용자 정의하고 확장하는 강력한 기술입니다.

목표

  1. 추가 속성 및 콘텐츠를 사용하여 기존 핵심 구성 요소를 확장합니다.
  2. 을 사용하여 구성 요소 상속의 기본 사항 이해 sling:resourceSuperType.
  3. 을(를) 활용하는 방법 알아보기 전달 패턴 기존 논리 및 기능을 재사용하는 슬링 모델용

빌드할 내용

이 장에서는 표준에 추가 속성을 추가하는 데 필요한 추가 코드를 보여 줍니다 Image 새 제품에 대한 요구 사항을 충족시키기 위한 구성 요소 Banner 구성 요소. 다음 Banner 구성 요소에 표준과 동일한 속성이 모두 포함됩니다 Image 구성 요소이지만 사용자가 을 채울 추가 속성을 포함합니다. 배너 텍스트.

최종 작성 배너 구성 요소

사전 요구 사항

설정에 필요한 도구 및 지침 검토 로컬 개발 환경. 이 시점에서 자습서에서는 사용자가 AEM SPA Editor 기능을 확실히 이해하고 있다고 가정합니다.

Sling 리소스 슈퍼 유형을 통한 상속

기존 구성 요소를 확장하려면 다음 이름의 속성을 설정합니다. sling:resourceSuperType 구성 요소 정의에 따라 달라집니다. sling:resourceSuperType다음 값: 속성 다른 구성 요소를 가리키는 AEM 구성 요소의 정의에 설정할 수 있습니다. 이렇게 하면 구성 요소가 로 식별된 구성 요소의 모든 기능을 상속하도록 명시적으로 설정됩니다. sling:resourceSuperType.

를 연장하려면 Image 구성 요소 wknd-spa-react/components/image 의 코드를 업데이트해야 합니다. ui.apps 모듈.

  1. 아래에 새 폴더 만들기 ui.apps 모듈 banner 위치: ui.apps/src/main/content/jcr_root/apps/wknd-spa-react/components/banner.

  2. 아래에 banner 구성 요소 정의 만들기(.content.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="Banner"
        sling:resourceSuperType="wknd-spa-react/components/image"
        componentGroup="WKND SPA React - Content"/>
    

    다음을 설정합니다. wknd-spa-react/components/banner 의 모든 기능을 상속하려면 wknd-spa-react/components/image.

cq:editConfig

다음 _cq_editConfig.xml 파일은 AEM 작성 UI의 드래그 앤 드롭 동작을 지시합니다. 이미지 구성 요소를 확장할 때 리소스 유형이 구성 요소 자체와 일치해야 합니다.

  1. 다음에서 ui.apps 모듈 아래에 다른 파일 만들기 banner 명명된 _cq_editConfig.xml.

  2. 채우기 _cq_editConfig.xml 다음 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" xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
        jcr:primaryType="cq:EditConfig">
        <cq:dropTargets jcr:primaryType="nt:unstructured">
            <image
                jcr:primaryType="cq:DropTargetConfig"
                accept="[image/gif,image/jpeg,image/png,image/webp,image/tiff,image/svg\\+xml]"
                groups="[media]"
                propertyName="./fileReference">
                <parameters
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="wknd-spa-react/components/banner"
                    imageCrop=""
                    imageMap=""
                    imageRotate=""/>
            </image>
        </cq:dropTargets>
        <cq:inplaceEditing
            jcr:primaryType="cq:InplaceEditingConfig"
            active="{Boolean}true"
            editorType="image">
            <inplaceEditingConfig jcr:primaryType="nt:unstructured">
                <plugins jcr:primaryType="nt:unstructured">
                    <crop
                        jcr:primaryType="nt:unstructured"
                        supportedMimeTypes="[image/jpeg,image/png,image/webp,image/tiff]"
                        features="*">
                        <aspectRatios jcr:primaryType="nt:unstructured">
                            <wideLandscape
                                jcr:primaryType="nt:unstructured"
                                name="Wide Landscape"
                                ratio="0.6180"/>
                            <landscape
                                jcr:primaryType="nt:unstructured"
                                name="Landscape"
                                ratio="0.8284"/>
                            <square
                                jcr:primaryType="nt:unstructured"
                                name="Square"
                                ratio="1"/>
                            <portrait
                                jcr:primaryType="nt:unstructured"
                                name="Portrait"
                                ratio="1.6180"/>
                        </aspectRatios>
                    </crop>
                    <flip
                        jcr:primaryType="nt:unstructured"
                        supportedMimeTypes="[image/jpeg,image/png,image/webp,image/tiff]"
                        features="-"/>
                    <map
                        jcr:primaryType="nt:unstructured"
                        supportedMimeTypes="[image/jpeg,image/png,image/webp,image/tiff,image/svg+xml]"
                        features="*"/>
                    <rotate
                        jcr:primaryType="nt:unstructured"
                        supportedMimeTypes="[image/jpeg,image/png,image/webp,image/tiff]"
                        features="*"/>
                    <zoom
                        jcr:primaryType="nt:unstructured"
                        supportedMimeTypes="[image/jpeg,image/png,image/webp,image/tiff]"
                        features="*"/>
                </plugins>
                <ui jcr:primaryType="nt:unstructured">
                    <inline
                        jcr:primaryType="nt:unstructured"
                        toolbar="[crop#launch,rotate#right,history#undo,history#redo,fullscreen#fullscreen,control#close,control#finish]">
                        <replacementToolbars
                            jcr:primaryType="nt:unstructured"
                            crop="[crop#identifier,crop#unlaunch,crop#confirm]"/>
                    </inline>
                    <fullscreen jcr:primaryType="nt:unstructured">
                        <toolbar
                            jcr:primaryType="nt:unstructured"
                            left="[crop#launchwithratio,rotate#right,flip#horizontal,flip#vertical,zoom#reset100,zoom#popupslider]"
                            right="[history#undo,history#redo,fullscreen#fullscreenexit]"/>
                        <replacementToolbars jcr:primaryType="nt:unstructured">
                            <crop
                                jcr:primaryType="nt:unstructured"
                                left="[crop#identifier]"
                                right="[crop#unlaunch,crop#confirm]"/>
                            <map
                                jcr:primaryType="nt:unstructured"
                                left="[map#rectangle,map#circle,map#polygon]"
                                right="[map#unlaunch,map#confirm]"/>
                        </replacementToolbars>
                    </fullscreen>
                </ui>
            </inplaceEditingConfig>
        </cq:inplaceEditing>
    </jcr:root>
    
  3. 파일의 고유한 측면은 <parameters> resourceType을 로 설정하는 노드 wknd-spa-react/components/banner.

    <parameters
        jcr:primaryType="nt:unstructured"
        sling:resourceType="wknd-spa-react/components/banner"
        imageCrop=""
        imageMap=""
        imageRotate=""/>
    

    대부분의 구성 요소에는 _cq_editConfig. 이미지 구성 요소와 하위 항목은 예외입니다.

대화 상자 확장

당사 Banner 구성 요소를 캡처하려면 대화 상자에 추가 텍스트 필드가 필요합니다. bannerText. Sling 상속을 사용하고 있으므로 의 기능을 사용할 수 있습니다. Sling 리소스 병합 를 클릭하여 대화 상자의 일부를 재정의하거나 확장합니다. 이 샘플에서는 작성자의 추가 데이터를 캡처하여 카드 구성 요소를 채우기 위한 새 탭이 대화 상자에 추가되었습니다.

  1. 다음에서 ui.apps 모듈, 아래 banner 폴더, 다음 이름의 폴더 만들기 _cq_dialog.

  2. 아래에 _cq_dialog 대화 상자 정의 파일 만들기 .content.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="Banner"
        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">
                        <text
                            jcr:primaryType="nt:unstructured"
                            jcr:title="Text"
                            sling:orderBefore="asset"
                            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">
                                                <textGroup
                                                    granite:hide="${cqDesign.titleHidden}"
                                                    jcr:primaryType="nt:unstructured"
                                                    sling:resourceType="granite/ui/components/coral/foundation/well">
                                                    <items jcr:primaryType="nt:unstructured">
                                                        <bannerText
                                                            jcr:primaryType="nt:unstructured"
                                                            sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                                            fieldDescription="Text to display on top of the banner."
                                                            fieldLabel="Banner Text"
                                                            name="./bannerText"/>
                                                    </items>
                                                </textGroup>
                                            </items>
                                        </column>
                                    </items>
                                </columns>
                            </items>
                        </text>
                    </items>
                </tabs>
            </items>
        </content>
    </jcr:root>
    

    위의 XML 정의에서는 이름이 인 새 탭을 만듭니다. 텍스트 주문하기 다음 이전 기존 자산 탭. 단일 필드가 포함됩니다. 배너 텍스트.

  3. 대화 상자는 다음과 같이 표시됩니다.

    배너 최종 대화 상자

    에 대한 탭을 정의할 필요가 없었는지 확인합니다. 자산 또는 메타데이터. 다음을 통해 상속됩니다. sling:resourceSuperType 속성.

    대화 상자를 미리 보려면 먼저 SPA 구성 요소 및 MapTo 함수.

SPA 구성 요소 구현

SPA 편집기와 함께 배너 구성 요소를 사용하려면 매핑할 새 SPA 구성 요소를 만들어야 합니다 wknd-spa-react/components/banner. 이 작업은 다음에서 수행됩니다. ui.frontend 모듈.

  1. 다음에서 ui.frontend 모듈 새 폴더 만들기 Banner 위치: ui.frontend/src/components/Banner.

  2. 이름이 인 새 파일 만들기 Banner.js 아래에 Banner 폴더를 삭제합니다. 다음을 사용하여 채웁니다.

    import React, {Component} from 'react';
    import {MapTo} from '@adobe/aem-react-editable-components';
    
    export const BannerEditConfig = {
        emptyLabel: 'Banner',
    
        isEmpty: function(props) {
            return !props || !props.src || props.src.trim().length < 1;
        }
    };
    
    export default class Banner extends Component {
    
        get content() {
            return <img
                    className="Image-src"
                    src={this.props.src}
                    alt={this.props.alt}
                    title={this.props.title ? this.props.title : this.props.alt} />;
        }
    
        // display our custom bannerText property!
        get bannerText() {
            if(this.props.bannerText) {
                return <h4>{this.props.bannerText}</h4>;
            }
    
            return null;
        }
    
        render() {
            if (BannerEditConfig.isEmpty(this.props)) {
                return null;
            }
    
            return (
                <div className="Banner">
                    {this.bannerText}
                    <div className="BannerImage">{this.content}</div>
                </div>
            );
        }
    }
    
    MapTo('wknd-spa-react/components/banner')(Banner, BannerEditConfig);
    

    이 SPA 구성 요소는 AEM 구성 요소에 매핑됩니다. wknd-spa-react/components/banner 이(가) 이전에 만들어졌습니다.

  3. 업데이트 import-components.js 위치: ui.frontend/src/components/import-components.js 새 항목 포함 Banner SPA 구성 요소:

      import './ExperienceFragment/ExperienceFragment';
      import './OpenWeather/OpenWeather';
    + import './Banner/Banner';
    
  4. 이 시점에서 프로젝트를 AEM에 배포하고 대화 상자를 테스트할 수 있습니다. Maven 기술을 사용하여 프로젝트를 배포합니다.

    $ cd aem-guides-wknd-spa.react
    $ mvn clean install -PautoInstallSinglePackage
    
  5. SPA 템플릿의 정책을 업데이트하여 추가 Banner 구성 요소를 로 허용된 구성 요소.

  6. SPA 페이지로 이동하고 Banner 구성 요소를 SPA 페이지 중 하나로 만듭니다.

    배너 구성 요소 추가

    노트

    대화 상자에서에 대한 값을 저장할 수 있습니다. 배너 텍스트 그러나 이 값은 SPA 구성 요소에 반영되지 않습니다. 활성화하려면 구성 요소에 대한 슬링 모델 을 확장해야 합니다.

Java 인터페이스 추가

궁극적으로 구성 요소 대화 상자의 값을 React 구성 요소에 노출하려면 의 JSON을 채우는 Sling 모델을 업데이트해야 합니다. Banner 구성 요소. 이 작업은 다음에서 수행됩니다. core SPA 프로젝트에 대한 모든 Java 코드를 포함하는 모듈입니다.

먼저 용 새 Java 인터페이스를 만듭니다. Banner 를 확장합니다. Image Java 인터페이스.

  1. 다음에서 core 모듈 이름이 인 새 파일 만들기 BannerModel.java 위치: core/src/main/java/com/adobe/aem/guides/wkndspa/react/core/models.

  2. 채우기 BannerModel.java 다음을 사용하여:

    package com.adobe.aem.guides.wkndspa.react.core.models;
    
    import com.adobe.cq.wcm.core.components.models.Image;
    import org.osgi.annotation.versioning.ProviderType;
    
    @ProviderType
    public interface BannerModel extends Image {
    
        public String getBannerText();
    
    }
    

    핵심 구성 요소에서 모든 메서드를 상속합니다. Image 인터페이스 및 새 메서드 추가 getBannerText().

Sling 모델 구현

그런 다음 Sling 모델 을 BannerModel 인터페이스.

  1. 다음에서 core 모듈 이름이 인 새 파일 만들기 BannerModelImpl.java 위치: core/src/main/java/com/adobe/aem/guides/wkndspa/react/core/models/impl.

  2. 채우기 BannerModelImpl.java 다음을 사용하여:

    package com.adobe.aem.guides.wkndspa.react.core.models.impl;
    
    import com.adobe.aem.guides.wkndspa.react.core.models.BannerModel;
    import com.adobe.cq.export.json.ComponentExporter;
    import com.adobe.cq.export.json.ExporterConstants;
    import com.adobe.cq.wcm.core.components.models.Image;
    import org.apache.sling.models.annotations.*;
    import org.apache.sling.api.SlingHttpServletRequest;
    import org.apache.sling.models.annotations.Model;
    import org.apache.sling.models.annotations.injectorspecific.Self;
    import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
    import org.apache.sling.models.annotations.via.ResourceSuperType;
    
    @Model(
        adaptables = SlingHttpServletRequest.class,
        adapters = { BannerModel.class,ComponentExporter.class},
        resourceType = BannerModelImpl.RESOURCE_TYPE,
        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
    )
    @Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)
    public class BannerModelImpl implements BannerModel {
    
        // points to the the component resource path in ui.apps
        static final String RESOURCE_TYPE = "wknd-spa-react/components/banner";
    
        @Self
        private SlingHttpServletRequest request;
    
        // With sling inheritance (sling:resourceSuperType) we can adapt the current resource to the Image class
        // this allows us to re-use all of the functionality of the Image class, without having to implement it ourself
        // see https://github.com/adobe/aem-core-wcm-components/wiki/Delegation-Pattern-for-Sling-Models
        @Self
        @Via(type = ResourceSuperType.class)
        private Image image;
    
        // map the property saved by the dialog to a variable named `bannerText`
        @ValueMapValue
        private String bannerText;
    
        // public getter to expose the value of `bannerText` via the Sling Model and JSON output
        @Override
        public String getBannerText() {
            return bannerText;
        }
    
        // Re-use the Image class for all other methods:
    
        @Override
        public String getSrc() {
            return null != image ? image.getSrc() : null;
        }
    
        @Override
        public String getAlt() {
            return null != image ? image.getAlt() : null;
        }
    
        @Override
        public String getTitle() {
            return null != image ? image.getTitle() : null;
        }
    
        // method required by `ComponentExporter` interface
        // exposes a JSON property named `:type` with a value of `wknd-spa-react/components/banner`
        // required to map the JSON export to the SPA component props via the `MapTo`
        @Override
        public String getExportedType() {
            return BannerModelImpl.RESOURCE_TYPE;
        }
    }
    

    의 사용에 주목하십시오. @Model@Exporter 슬링 모델 익스포터를 통해 슬링 모델을 JSON으로 일련화할 수 있도록 하는 주석입니다.

    BannerModelImpl.java 를 사용합니다. Sling 모델의 전달 패턴 이미지 핵심 구성 요소에서 모든 논리를 다시 작성하지 않도록 합니다.

  3. 다음 행을 검토하십시오.

    @Self
    @Via(type = ResourceSuperType.class)
    private Image image;
    

    위의 주석은 이라는 이미지 개체를 인스턴스화합니다. image 를 기반으로 함 sling:resourceSuperType 상속 Banner 구성 요소.

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

    그런 다음 를 간단하게 사용할 수 있습니다. image 로 정의된 메서드를 구현할 개체 Image 직접 논리를 작성하지 않고도 인터페이스를 사용할 수 있습니다. 이 기법은 다음 용도로 사용됩니다. getSrc(), getAlt()getTitle().

  4. 터미널 창을 열고 업데이트에 대한 core Maven을 사용한 모듈 autoInstallBundle 프로필의 core 디렉토리.

    $ cd core/
    $ mvn clean install -PautoInstallBundle
    

결합하기

  1. AEMSPA 으로 돌아가서 Banner 구성 요소.

  2. 업데이트 Banner 포함할 구성 요소 배너 텍스트:

    배너 텍스트

  3. 구성 요소를 이미지로 채웁니다.

    배너 대화 상자에 이미지 추가

    대화 상자 업데이트를 저장합니다.

  4. 이제 의 렌더링된 값이 표시됩니다. 배너 텍스트:

배너 텍스트 표시

  1. 다음 위치에서 JSON 모델 응답을 봅니다. http://localhost:4502/content/wknd-spa-react/us/en.model.json 및 검색 wknd-spa-react/components/card:

    "banner": {
        "bannerText": "My Banner Text",
        "src": "/content/wknd-spa-react/us/en/home/_jcr_content/root/responsivegrid/banner.coreimg.jpeg/1622167884688/sport-climbing.jpeg",
        "alt": "alt banner rock climber",
        ":type": "wknd-spa-react/components/banner"
     },
    

    JSON 모델은에서 Sling 모델을 구현한 후 추가 키/값 쌍으로 업데이트됩니다 BannerModelImpl.java.

축하합니다!

축하합니다. 를 사용하여 AEM 구성 요소를 확장하는 방법과 Sling 모델 및 대화 상자가 JSON 모델과 함께 작동하는 방법을 배웠습니다.

이 페이지에서는