 {"id":581,"date":"2021-06-29T11:00:51","date_gmt":"2021-06-29T18:00:51","guid":{"rendered":"https:\/\/www.mavice.com\/blog\/?p=581"},"modified":"2021-08-10T11:47:03","modified_gmt":"2021-08-10T18:47:03","slug":"extend-a-component","status":"publish","type":"post","link":"https:\/\/www.mavice.com\/blog\/extend-a-component\/","title":{"rendered":"Extend a Component"},"content":{"rendered":"\n<p><em>*This article was modeled from the current Adobe React SPA&nbsp;<a href=\"https:\/\/experienceleague.adobe.com\/docs\/experience-manager-learn\/getting-started-with-aem-headless\/spa-editor\/react\/extend-component.html?lang=en\">documentation<\/a>.<\/em><\/p>\n\n\n\n<p>Learn how to extend an existing Core Component to be used with the AEM SPA Editor. Understanding how to extend an existing component is a powerful technique to customize and expand the capabilities of an AEM SPA Editor implementation.<\/p>\n\n\n\n<h2 id=\"objective\">Objective<\/h2>\n\n\n\n<ol><li>Extend an existing Core Component with additional properties and content.<\/li><li>Understand the basic of Component Inheritance with the use of&nbsp;<code>sling:resourceSuperType<\/code>.<\/li><li>Learn how to leverage the&nbsp;<a href=\"https:\/\/github.com\/adobe\/aem-core-wcm-components\/wiki\/Delegation-Pattern-for-Sling-Models\">Delegation Pattern<\/a>&nbsp;for Sling Models to re-use existing logic and functionality.<\/li><\/ol>\n\n\n\n<h2 id=\"what-you-will-build\">What you will build<\/h2>\n\n\n\n<p>This chapter illustrates the additional code needed to add an extra property to a standard&nbsp;<code>Image<\/code>&nbsp;component to fulfill the requirements for a new&nbsp;<code>Banner<\/code>&nbsp;component. The&nbsp;<code>Banner<\/code>&nbsp;component contains all of the same properties as the standard&nbsp;<code>Image<\/code>&nbsp;component but includes an additional property for users to populate the&nbsp;<strong>Banner Text<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img src=\"https:\/\/experienceleague.adobe.com\/docs\/experience-manager-learn\/assets\/final-author-banner-component.png?lang=en\" alt=\"Final authored banner component\"\/><\/figure>\n\n\n\n<h2 id=\"prerequisites\">Prerequisites<\/h2>\n\n\n\n<p>Review the required tooling and instructions for setting up a&nbsp;<a href=\"https:\/\/experienceleague.adobe.com\/docs\/experience-manager-learn\/getting-started-with-aem-headless\/spa-editor\/react\/overview.html?lang=en#local-dev-environment\">local development environment<\/a>. It is assumed at this point in the tutorial users have a solid understanding of the AEM SPA Editor feature.<\/p>\n\n\n\n<h2 id=\"sling-resource-super-type\">Inheritance with Sling Resource Super Type<\/h2>\n\n\n\n<p>To extend an existing component set a property named&nbsp;<code>sling:resourceSuperType<\/code>&nbsp;on your component\u2019s definition.&nbsp;<code>sling:resourceSuperType<\/code>is a&nbsp;<a href=\"https:\/\/sling.apache.org\/documentation\/the-sling-engine\/resources.html#resource-properties\">property<\/a>&nbsp;that can be set on an AEM component\u2019s definition that points to another component. This explicitly sets the component to inherit all functionality of the component identified as the&nbsp;<code>sling:resourceSuperType<\/code>.<\/p>\n\n\n\n<p>If we want to extend the&nbsp;<code>Image<\/code>&nbsp;component at&nbsp;<code>wknd-spa-vue\/components\/image<\/code>&nbsp;we need to update the code in the&nbsp;<code>ui.apps<\/code>&nbsp;module.<\/p>\n\n\n\n<ol><li>Create a new folder beneath the&nbsp;<code>ui.apps<\/code>&nbsp;module for&nbsp;<code>banner<\/code>&nbsp;at&nbsp;<code>ui.apps\/src\/main\/content\/jcr_root\/apps\/wknd-spa-vue\/components\/banner<\/code>.<\/li><li>Beneath&nbsp;<code>banner<\/code>&nbsp;create a Component definition (<code>.content.xml<\/code>) like the following:<br><\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;?<em>xml version<\/em>=\"1.0\" <em>encoding<\/em>=\"UTF-8\"?&gt;\n&lt;jcr:root <em>xmlns:sling<\/em>=\"http:\/\/sling.apache.org\/jcr\/sling\/1.0\" <em>xmlns:cq<\/em>=\"http:\/\/www.day.com\/jcr\/cq\/1.0\" <em>xmlns:jcr<\/em>=\"http:\/\/www.jcp.org\/jcr\/1.0\"\n          <em>jcr:primaryType<\/em>=\"cq:Component\"\n          <em>jcr:title<\/em>=\"Banner\"\n          <em>sling:resourceSuperType<\/em>=\"wknd-spa-vue\/components\/image\"\n          <em>componentGroup<\/em>=\"WKND SPA Vue - Content\"\/&gt;<\/pre>\n\n\n\n<p>This sets&nbsp;<code>wknd-spa-vue\/components\/banner<\/code>&nbsp;to inherit all functionality of&nbsp;<code>wknd-spa-vue\/components\/image<\/code>.<\/p>\n\n\n\n<h2 id=\"cq-edit-config\">cq:editConfig<\/h2>\n\n\n\n<p>The&nbsp;<code>_cq_editConfig.xml<\/code>&nbsp;file dictates the drag and drop behavior in the AEM authoring UI. When extending the Image component it is important that the resource type matches the component itself.<\/p>\n\n\n\n<ol><li>In the&nbsp;<code>ui.apps<\/code>&nbsp;module create another file beneath&nbsp;<code>banner<\/code>&nbsp;named&nbsp;<code>_cq_editConfig.xml<\/code>.<\/li><li>Populate&nbsp;<code>_cq_editConfig.xml<\/code>&nbsp;with the following XML:<br><\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;?<em>xml version<\/em>=\"1.0\" <em>encoding<\/em>=\"UTF-8\"?&gt;\n&lt;jcr:root <em>xmlns:sling<\/em>=\"http:\/\/sling.apache.org\/jcr\/sling\/1.0\" <em>xmlns:cq<\/em>=\"http:\/\/www.day.com\/jcr\/cq\/1.0\" <em>xmlns:jcr<\/em>=\"http:\/\/www.jcp.org\/jcr\/1.0\" <em>xmlns:nt<\/em>=\"http:\/\/www.jcp.org\/jcr\/nt\/1.0\"\n          <em>jcr:primaryType<\/em>=\"cq:EditConfig\"&gt;\n    &lt;cq:dropTargets <em>jcr:primaryType<\/em>=\"nt:unstructured\"&gt;\n        &lt;image\n                <em>jcr:primaryType<\/em>=\"cq:DropTargetConfig\"\n                <em>accept<\/em>=\"[image\/gif,image\/jpeg,image\/png,image\/webp,image\/tiff,image\/svg\\\\+xml]\"\n                <em>groups<\/em>=\"[media]\"\n                <em>propertyName<\/em>=\".\/fileReference\"&gt;\n            &lt;parameters\n                    <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                    <em>sling:resourceType<\/em>=\"wknd-spa-vue\/components\/banner\"\n                    <em>imageCrop<\/em>=\"\"\n                    <em>imageMap<\/em>=\"\"\n                    <em>imageRotate<\/em>=\"\"\/&gt;\n        &lt;\/image&gt;\n    &lt;\/cq:dropTargets&gt;\n    &lt;cq:inplaceEditing\n            <em>jcr:primaryType<\/em>=\"cq:InplaceEditingConfig\"\n            <em>active<\/em>=\"{Boolean}true\"\n            <em>editorType<\/em>=\"image\"&gt;\n        &lt;inplaceEditingConfig <em>jcr:primaryType<\/em>=\"nt:unstructured\"&gt;\n            &lt;plugins <em>jcr:primaryType<\/em>=\"nt:unstructured\"&gt;\n                &lt;crop\n                        <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                        <em>supportedMimeTypes<\/em>=\"[image\/jpeg,image\/png,image\/webp,image\/tiff]\"\n                        <em>features<\/em>=\"*\"&gt;\n                    &lt;aspectRatios <em>jcr:primaryType<\/em>=\"nt:unstructured\"&gt;\n                        &lt;wideLandscape\n                                <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                                <em>name<\/em>=\"Wide Landscape\"\n                                <em>ratio<\/em>=\"0.6180\"\/&gt;\n                        &lt;landscape\n                                <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                                <em>name<\/em>=\"Landscape\"\n                                <em>ratio<\/em>=\"0.8284\"\/&gt;\n                        &lt;square\n                                <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                                <em>name<\/em>=\"Square\"\n                                <em>ratio<\/em>=\"1\"\/&gt;\n                        &lt;portrait\n                                <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                                <em>name<\/em>=\"Portrait\"\n                                <em>ratio<\/em>=\"1.6180\"\/&gt;\n                    &lt;\/aspectRatios&gt;\n                &lt;\/crop&gt;\n                &lt;flip\n                        <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                        <em>supportedMimeTypes<\/em>=\"[image\/jpeg,image\/png,image\/webp,image\/tiff]\"\n                        <em>features<\/em>=\"-\"\/&gt;\n                &lt;map\n                        <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                        <em>supportedMimeTypes<\/em>=\"[image\/jpeg,image\/png,image\/webp,image\/tiff,image\/svg+xml]\"\n                        <em>features<\/em>=\"*\"\/&gt;\n                &lt;rotate\n                        <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                        <em>supportedMimeTypes<\/em>=\"[image\/jpeg,image\/png,image\/webp,image\/tiff]\"\n                        <em>features<\/em>=\"*\"\/&gt;\n                &lt;zoom\n                        <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                        <em>supportedMimeTypes<\/em>=\"[image\/jpeg,image\/png,image\/webp,image\/tiff]\"\n                        <em>features<\/em>=\"*\"\/&gt;\n            &lt;\/plugins&gt;\n            &lt;ui <em>jcr:primaryType<\/em>=\"nt:unstructured\"&gt;\n                &lt;inline\n                        <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                        <em>toolbar<\/em>=\"[crop#launch,rotate#right,history#undo,history#redo,fullscreen#fullscreen,control#close,control#finish]\"&gt;\n                    &lt;replacementToolbars\n                            <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                            <em>crop<\/em>=\"[crop#identifier,crop#unlaunch,crop#confirm]\"\/&gt;\n                &lt;\/inline&gt;\n                &lt;fullscreen <em>jcr:primaryType<\/em>=\"nt:unstructured\"&gt;\n                    &lt;toolbar\n                            <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                            <em>left<\/em>=\"[crop#launchwithratio,rotate#right,flip#horizontal,flip#vertical,zoom#reset100,zoom#popupslider]\"\n                            <em>right<\/em>=\"[history#undo,history#redo,fullscreen#fullscreenexit]\"\/&gt;\n                    &lt;replacementToolbars <em>jcr:primaryType<\/em>=\"nt:unstructured\"&gt;\n                        &lt;crop\n                                <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                                <em>left<\/em>=\"[crop#identifier]\"\n                                <em>right<\/em>=\"[crop#unlaunch,crop#confirm]\"\/&gt;\n                        &lt;map\n                                <em>jcr:primaryType<\/em>=\"nt:unstructured\"\n                                <em>left<\/em>=\"[map#rectangle,map#circle,map#polygon]\"\n                                <em>right<\/em>=\"[map#unlaunch,map#confirm]\"\/&gt;\n                    &lt;\/replacementToolbars&gt;\n                &lt;\/fullscreen&gt;\n            &lt;\/ui&gt;\n        &lt;\/inplaceEditingConfig&gt;\n    &lt;\/cq:inplaceEditing&gt;\n&lt;\/jcr:root&gt;<\/pre>\n\n\n\n<p>3. The unique aspect of the file is the&nbsp;<code>&lt;parameters&gt;<\/code>&nbsp;node that sets the resourceType to&nbsp;<code>wknd-spa-vue\/components\/banner<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">&lt;parameters\n    jcr:primaryType=\"nt:unstructured\"\n    sling:resourceType=\"wknd-spa-vue\/components\/banner\"\n    imageCrop=\"\"\n    imageMap=\"\"\n    imageRotate=\"\"\/&gt;<\/code><\/pre>\n\n\n\n<p><br>Most component\u2019s do not require a&nbsp;<code>_cq_editConfig<\/code>. Image components and descendants are the exception.<\/p>\n\n\n\n<h2 id=\"extend-dialog\">Extend the Dialog<\/h2>\n\n\n\n<p>Our&nbsp;<code>Banner<\/code>&nbsp;component requires an extra text field in the dialog to capture the&nbsp;<code>bannerText<\/code>. Since we are using Sling inheritance, we can use features of the&nbsp;<a href=\"https:\/\/experienceleague.adobe.com\/docs\/experience-manager-65\/developing\/platform\/sling-resource-merger.html?lang=en\">Sling Resource Merger<\/a>&nbsp;to override or extend portions of the dialog. In this sample a new tab has been added to the dialog to capture additional data from an author to populate the Card Component.<\/p>\n\n\n\n<ol><li>In the&nbsp;<code>ui.apps<\/code>&nbsp;module, beneath the&nbsp;<code>banner<\/code>&nbsp;folder, create a folder named&nbsp;<code>_cq_dialog<\/code>.<\/li><li>Beneath&nbsp;<code>_cq_dialog<\/code>&nbsp;create a Dialog definition file&nbsp;<code>.content.xml<\/code>. Populate it with the following:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">&lt;?xml version=\"1.0\" encoding=\"UTF-8\"?&gt;\n&lt;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\"\n    jcr:primaryType=\"nt:unstructured\"\n    jcr:title=\"Banner\"\n    sling:resourceType=\"cq\/gui\/components\/authoring\/dialog\"&gt;\n    &lt;content jcr:primaryType=\"nt:unstructured\"&gt;\n        &lt;items jcr:primaryType=\"nt:unstructured\"&gt;\n            &lt;tabs jcr:primaryType=\"nt:unstructured\"&gt;\n                &lt;items jcr:primaryType=\"nt:unstructured\"&gt;\n                    &lt;text\n                        jcr:primaryType=\"nt:unstructured\"\n                        jcr:title=\"Text\"\n                        sling:orderBefore=\"asset\"\n                        sling:resourceType=\"granite\/ui\/components\/coral\/foundation\/container\"\n                        margin=\"{Boolean}true\"&gt;\n                        &lt;items jcr:primaryType=\"nt:unstructured\"&gt;\n                            &lt;columns\n                                jcr:primaryType=\"nt:unstructured\"\n                                sling:resourceType=\"granite\/ui\/components\/coral\/foundation\/fixedcolumns\"\n                                margin=\"{Boolean}true\"&gt;\n                                &lt;items jcr:primaryType=\"nt:unstructured\"&gt;\n                                    &lt;column\n                                        jcr:primaryType=\"nt:unstructured\"\n                                        sling:resourceType=\"granite\/ui\/components\/coral\/foundation\/container\"&gt;\n                                        &lt;items jcr:primaryType=\"nt:unstructured\"&gt;\n                                            &lt;textGroup\n                                                granite:hide=\"${cqDesign.titleHidden}\"\n                                                jcr:primaryType=\"nt:unstructured\"\n                                                sling:resourceType=\"granite\/ui\/components\/coral\/foundation\/well\"&gt;\n                                                &lt;items jcr:primaryType=\"nt:unstructured\"&gt;\n                                                    &lt;bannerText\n                                                        jcr:primaryType=\"nt:unstructured\"\n                                                        sling:resourceType=\"granite\/ui\/components\/coral\/foundation\/form\/textfield\"\n                                                        fieldDescription=\"Text to display on top of the banner.\"\n                                                        fieldLabel=\"Banner Text\"\n                                                        name=\".\/bannerText\"\/&gt;\n                                                &lt;\/items&gt;\n                                            &lt;\/textGroup&gt;\n                                        &lt;\/items&gt;\n                                    &lt;\/column&gt;\n                                &lt;\/items&gt;\n                            &lt;\/columns&gt;\n                        &lt;\/items&gt;\n                    &lt;\/text&gt;\n                &lt;\/items&gt;\n            &lt;\/tabs&gt;\n        &lt;\/items&gt;\n    &lt;\/content&gt;\n&lt;\/jcr:root&gt;<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>The above XML definition will create a new tab named&nbsp;<strong>Text<\/strong>&nbsp;and order it&nbsp;<em>before<\/em>&nbsp;the existing&nbsp;<strong>Asset<\/strong>&nbsp;tab. It will contain a single field&nbsp;<strong>Banner Text<\/strong>.<\/p>\n\n\n\n<p>The dialog will look like the following:<\/p>\n\n\n\n<p><\/p>\n\n\n\n<div class=\"wp-block-image\"><figure class=\"aligncenter size-large is-resized\"><img loading=\"lazy\" src=\"https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.33.29-PM.png\" alt=\"\" class=\"wp-image-582\" width=\"512\" height=\"403\" srcset=\"https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.33.29-PM.png 924w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.33.29-PM-300x236.png 300w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.33.29-PM-768x605.png 768w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.33.29-PM-360x284.png 360w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.33.29-PM-640x504.png 640w\" sizes=\"(max-width: 512px) 100vw, 512px\" \/><\/figure><\/div>\n\n\n\n<p>Observe that we did not have to define the tabs for&nbsp;<strong>Asset<\/strong>&nbsp;or&nbsp;<strong>Metadata<\/strong>. These are inherited via the&nbsp;<code>sling:resourceSuperType<\/code>&nbsp;property.<\/p>\n\n\n\n<p>Before we can preview the dialog, we need to implement the SPA Component and the&nbsp;<code>MapTo<\/code>&nbsp;function.<\/p>\n\n\n\n<h2 id=\"implement-spa-component\">Implement SPA Component<\/h2>\n\n\n\n<p>In order to use the Banner component with the SPA Editor, a new SPA component must be created that will map to&nbsp;<code>wknd-spa-vue\/components\/banner<\/code>. This will be done in the&nbsp;<code>ui.frontend<\/code>&nbsp;module.<\/p>\n\n\n\n<ol><li>In the&nbsp;<code>ui.frontend<\/code>&nbsp;module create a new folder for&nbsp;<code>Banner<\/code>&nbsp;at&nbsp;<code>ui.frontend\/src\/components\/Banner<\/code>.<\/li><li>Create a new file named&nbsp;<code>Banner.js<\/code>&nbsp;beneath the&nbsp;<code>Banner<\/code>&nbsp;folder. Populate it with the following:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;template&gt;\n  &lt;div <em>class<\/em>=\"Banner\"&gt;\n    &lt;h4&gt;{{bannerText}}&lt;\/h4&gt;\n    &lt;div <em>class<\/em>=\"BannerImage\"&gt;\n      &lt;img\n        <em>class<\/em>=\"Image-src\"\n        <em>:src<\/em>=\"src\"\n        <em>:alt<\/em>=\"alt\"\n        <em>:title<\/em>=\"title ? title : alt\" \/&gt;\n    &lt;\/div&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n\n&lt;script&gt;\n<em>export default <\/em>{\n  name: 'Banner',\n  props: {\n    src: {\n      type: String\n    },\n    alt: {\n      type: String\n    },\n    title: {\n      type: String\n    },\n    bannerText: {\n      type: String\n    }\n  }\n}\n&lt;\/script&gt;\n\n&lt;style <em>scoped<\/em>&gt;\n\n&lt;\/style&gt;\n<\/pre>\n\n\n\n<p>Update map-components.js to include the Banner component:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">MapTo('wknd-spa-vue\/components\/banner')(Banner, {\n  emptyLabel: 'Banner',\n  isEmpty: <em>function <\/em>(props) {\n    <em>return <\/em>!props || !props.src || props.src.trim().length &lt; 1\n  }\n})<\/pre>\n\n\n\n<ol start=\"3\"><li>At this point the project can be deployed to AEM and the dialog can be tested:<br><code>$ cd aem-guides-wknd-spa-vue<\/code><br><code>$ mvn clean install -PautoInstallSinglePackage<\/code><br><\/li><li>Update the SPA Template\u2019s policy to add the&nbsp;<code>Banner<\/code>&nbsp;component as an&nbsp;<strong>allowed component<\/strong>.<br><\/li><li>Navigate to a SPA page and add the&nbsp;<code>Banner<\/code>&nbsp;component to one of the SPA pages<\/li><\/ol>\n\n\n\n<h2 id=\"java-interface\">Add Java Interface<\/h2>\n\n\n\n<p>To ultimately expose the values from the component dialog to the Vue component we need to update the Sling Model that populates the JSON for the&nbsp;<code>Banner<\/code>&nbsp;component. This will be done in the&nbsp;<code>core<\/code>&nbsp;module that contains all of the Java code for our SPA project.<\/p>\n\n\n\n<p>First we will create a new Java interface for&nbsp;<code>Banner<\/code>&nbsp;that extends the&nbsp;<code>Image<\/code>&nbsp;Java interface.<\/p>\n\n\n\n<ol><li>In the&nbsp;<code>core<\/code>&nbsp;module create a new file named&nbsp;<code>BannerModel.java<\/code>&nbsp;at&nbsp;<code>core\/src\/main\/java\/com\/adobe\/aem\/guides\/wkndspa\/vue\/core\/models<\/code>.<\/li><li>Populate&nbsp;<code>BannerModel.java<\/code>&nbsp;with the following:<br><\/li><\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">package com.adobe.aem.guides.wknd.spa.vue.core.models;\n\nimport com.adobe.cq.wcm.core.components.models.Image;\nimport org.osgi.annotation.versioning.ProviderType;\n\n@ProviderType\npublic interface BannerModel extends Image {\n\n    public String getBannerText();\n\n}<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>This will inherit all of the methods from the Core Component&nbsp;<code>Image<\/code>&nbsp;interface and add one new method&nbsp;<code>getBannerText()<\/code>.<br><br><\/p>\n\n\n\n<h2 id=\"sling-model\">Implement Sling Model<\/h2>\n\n\n\n<p>Next, implement the Sling Model for the&nbsp;<code>BannerModel<\/code>&nbsp;interface.<\/p>\n\n\n\n<ol><li>In the&nbsp;<code>core<\/code>&nbsp;module create a new file named&nbsp;<code>BannerModelImpl.java<\/code>&nbsp;at&nbsp;<code>core\/src\/main\/java\/com\/adobe\/aem\/guides\/wknd\/spa\/vue\/core\/models\/impl<\/code>.<\/li><li>Populate&nbsp;<code>BannerModelImpl.java<\/code>&nbsp;with the following:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">package com.adobe.aem.guides.wknd.spa.vue.core.models.impl;\n\nimport com.adobe.aem.guides.wknd.spa.vue.core.models.BannerModel;\nimport com.adobe.cq.export.json.ComponentExporter;\nimport com.adobe.cq.export.json.ExporterConstants;\nimport com.adobe.cq.wcm.core.components.models.Image;\nimport org.apache.sling.models.annotations.*;\nimport org.apache.sling.api.SlingHttpServletRequest;\nimport org.apache.sling.models.annotations.Model;\nimport org.apache.sling.models.annotations.injectorspecific.Self;\nimport org.apache.sling.models.annotations.injectorspecific.ValueMapValue;\nimport org.apache.sling.models.annotations.via.ResourceSuperType;\n\n@Model(\n    adaptables = SlingHttpServletRequest.class, \n    adapters = { BannerModel.class,ComponentExporter.class}, \n    resourceType = BannerModelImpl.RESOURCE_TYPE, \n    defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL\n    )\n@Exporter(name = ExporterConstants.SLING_MODEL_EXPORTER_NAME, extensions = ExporterConstants.SLING_MODEL_EXTENSION)\npublic class BannerModelImpl implements BannerModel {\n\n    \/\/ points to the the component resource path in ui.apps\n    static final String RESOURCE_TYPE = \"wknd-spa-vue\/components\/banner\";\n\n    @Self\n    private SlingHttpServletRequest request;\n\n    \/\/ With sling inheritance (sling:resourceSuperType) we can adapt the current resource to the Image class\n    \/\/ this allows us to re-use all of the functionality of the Image class, without having to implement it ourself\n    \/\/ see https:\/\/github.com\/adobe\/aem-core-wcm-components\/wiki\/Delegation-Pattern-for-Sling-Models\n    @Self\n    @Via(type = ResourceSuperType.class)\n    private Image image;\n\n    \/\/ map the property saved by the dialog to a variable named `bannerText`\n    @ValueMapValue\n    private String bannerText;\n\n    \/\/ public getter to expose the value of `bannerText` via the Sling Model and JSON output\n    @Override\n    public String getBannerText() {\n        return bannerText;\n    }\n\n    \/\/ Re-use the Image class for all other methods:\n\n    @Override\n    public String getSrc() {\n        return null != image ? image.getSrc() : null;\n    }\n\n    @Override\n    public String getAlt() {\n        return null != image ? image.getAlt() : null;\n    }\n\n    @Override\n    public String getTitle() {\n        return null != image ? image.getTitle() : null;\n    }\n\n\n    \/\/ method required by `ComponentExporter` interface\n    \/\/ exposes a JSON property named `:type` with a value of `wknd-spa-vue\/components\/banner`\n    \/\/ required to map the JSON export to the SPA component props via the `MapTo`\n    @Override\n    public String getExportedType() {\n        return BannerModelImpl.RESOURCE_TYPE;\n    }\n\n}<\/code><\/pre>\n\n\n\n<p><br>Notice the use of the&nbsp;<code>@Model<\/code>&nbsp;and&nbsp;<code>@Exporter<\/code>&nbsp;annotations to ensure the Sling Model is able to be serialized as JSON via the Sling Model Exporter.<\/p>\n\n\n\n<p><code>BannerModelImpl.java<\/code>&nbsp;uses the&nbsp;<a href=\"https:\/\/github.com\/adobe\/aem-core-wcm-components\/wiki\/Delegation-Pattern-for-Sling-Models\">Delegation pattern for Sling Models<\/a>&nbsp;to avoid rewriting all of the logic from the Image core component.<\/p>\n\n\n\n<p>Observe the following lines:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">@Self\n@Via(type = ResourceSuperType.class)\nprivate Image image;<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>The above annotation will instantiate an Image object named&nbsp;<code>image<\/code>&nbsp;based on the&nbsp;<code>sling:resourceSuperType<\/code>&nbsp;inheritance of the&nbsp;<code>Banner<\/code>&nbsp;component.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">@Override\npublic String getSrc() {\n    return null != image ? image.getSrc() : null;\n}<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>It is then possible to simply use the&nbsp;<code>image<\/code>&nbsp;object to implement methods defined by the&nbsp;<code>Image<\/code>&nbsp;interface, without having to write the logic ourselves. This technique is used for&nbsp;<code>getSrc()<\/code>,&nbsp;<code>getAlt()<\/code>&nbsp;and&nbsp;<code>getTitle()<\/code>.<\/p>\n\n\n\n<p>Open a terminal window and deploy just the updates to the&nbsp;<code>core<\/code>&nbsp;module using the Maven&nbsp;<code>autoInstallBundle<\/code>&nbsp;profile from the&nbsp;<code>core<\/code>&nbsp;directory.<\/p>\n\n\n\n<h2 id=\"put-together\">Putting it all together<\/h2>\n\n\n\n<ol><li>Return to AEM and open the SPA page that has the&nbsp;<code>Banner<\/code>&nbsp;component.<\/li><li>Update the&nbsp;<code>Banner<\/code>&nbsp;component to include&nbsp;<strong>Banner Text<\/strong>:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"794\" height=\"502\" src=\"https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.56.42-PM.png\" alt=\"\" class=\"wp-image-583\" srcset=\"https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.56.42-PM.png 794w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.56.42-PM-300x190.png 300w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.56.42-PM-768x486.png 768w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.56.42-PM-360x228.png 360w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.56.42-PM-640x405.png 640w\" sizes=\"(max-width: 794px) 100vw, 794px\" \/><\/figure>\n\n\n\n<p>Populate the component with an image:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"618\" height=\"512\" src=\"https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.57.50-PM.png\" alt=\"\" class=\"wp-image-584\" srcset=\"https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.57.50-PM.png 618w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.57.50-PM-300x249.png 300w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.57.50-PM-360x298.png 360w\" sizes=\"(max-width: 618px) 100vw, 618px\" \/><\/figure>\n\n\n\n<p>Save the dialog updates.<\/p>\n\n\n\n<p>You should now see the rendered value of&nbsp;<strong>Banner Text<\/strong><\/p>\n\n\n\n<h2 id=\"congratulations\">Congratulations!<\/h2>\n\n\n\n<p>Congratulations, you learned how to extend an AEM component using the and how Sling Models and dialogs work with the JSON model.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>*This article was modeled from the current Adobe React SPA&nbsp;documentation. Learn how to extend an existing Core Component to be used with the AEM SPA Editor. Understanding how to extend an existing component is a powerful technique to customize and expand the capabilities of an AEM SPA Editor implementation. Objective&#8230;<\/p>\n","protected":false},"author":2,"featured_media":620,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[1,36],"tags":[21,41],"_links":{"self":[{"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/posts\/581"}],"collection":[{"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/comments?post=581"}],"version-history":[{"count":2,"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/posts\/581\/revisions"}],"predecessor-version":[{"id":621,"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/posts\/581\/revisions\/621"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/media\/620"}],"wp:attachment":[{"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/media?parent=581"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/categories?post=581"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/tags?post=581"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}