 {"id":571,"date":"2021-06-29T10:12:57","date_gmt":"2021-06-29T17:12:57","guid":{"rendered":"https:\/\/www.mavice.com\/blog\/?p=571"},"modified":"2021-08-10T11:45:52","modified_gmt":"2021-08-10T18:45:52","slug":"create-a-custom-aem-vue-component","status":"publish","type":"post","link":"https:\/\/www.mavice.com\/blog\/create-a-custom-aem-vue-component\/","title":{"rendered":"Create a Custom AEM Vue 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\/custom-component.html?lang=en\">documentation<\/a>.<\/em><\/p>\n\n\n\n<h1 id=\"custom-component\">Create a Custom WeatherComponent<\/h1>\n\n\n\n<p>Learn how to create a custom weather component to be used with the AEM SPA Editor. Learn how to develop author dialogs and Sling Models to extend the JSON model to populate a custom component. The&nbsp;<a href=\"https:\/\/openweathermap.org\/\">Open Weather API<\/a>&nbsp;and&nbsp;<a href=\"https:\/\/www.npmjs.com\/package\/vue-open-weather-widget\">Vue Open Weather Widget<\/a> are used.<\/p>\n\n\n\n<h2 id=\"objective\">Objective<\/h2>\n\n\n\n<ol><li>Understand the role of Sling Models in manipulating the JSON model API provided by AEM.<\/li><li>Understand how to create new AEM component dialogs.<\/li><li>Learn to create a&nbsp;<strong>custom<\/strong>&nbsp;AEM Component that will be compatible with the SPA editor framework.<\/li><\/ol>\n\n\n\n<h2 id=\"what-you-will-build\">What you will build<\/h2>\n\n\n\n<p>A simple weather component will be built. This component will be able to be added to the SPA by content authors. Using an AEM dialog, authors can set the location for the weather to be displayed. The implementation of this component illustrates the steps needed to create a net-new AEM component that is compatible with the AEM SPA Editor framework.<\/p>\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>. This chapter is a continuation of the&nbsp;<a href=\"https:\/\/experienceleague.adobe.com\/docs\/experience-manager-learn\/getting-started-with-aem-headless\/spa-editor\/react\/navigation-routing.html?lang=en\">Navigation and Routing<\/a>&nbsp;chapter, however to follow along all you need is a SPA-enabled AEM project deployed to a local AEM instance.<\/p>\n\n\n\n<h3 id=\"open-weather-api-key\">Open Weather API Key<\/h3>\n\n\n\n<p>An API key from&nbsp;<a href=\"https:\/\/openweathermap.org\/\">Open Weather<\/a>&nbsp;is needed to follow along with the tutorial.&nbsp;<a href=\"https:\/\/home.openweathermap.org\/users\/sign_up\">Sign up is free<\/a>&nbsp;for a limited amount of API calls.<\/p>\n\n\n\n<h2 id=\"define-the-aem-component\">Define the AEM Component<\/h2>\n\n\n\n<p>An AEM component is defined as a node and properties. In the project these nodes and properties are represented as XML files in the&nbsp;<code>ui.apps<\/code>&nbsp;module. Next, create the AEM component in the&nbsp;<code>ui.apps<\/code>&nbsp;module.NOTE<\/p>\n\n\n\n<p>A quick refresher on the&nbsp;<a href=\"https:\/\/experienceleague.adobe.com\/docs\/experience-manager-learn\/getting-started-wknd-tutorial-develop\/project-archetype\/component-basics.html?lang=en\">basics of AEM components may be helpful<\/a>.<\/p>\n\n\n\n<ol><li>In the IDE of your choice open the&nbsp;<code>ui.apps<\/code>&nbsp;folder.<br><\/li><li>Navigate to&nbsp;<code>ui.apps\/src\/main\/content\/jcr_root\/apps\/wknd-spa-vue\/components<\/code>&nbsp;and create a new folder named&nbsp;<code>open-weather<\/code>.<br><\/li><li>Create a new file named&nbsp;<code>.content.xml<\/code>&nbsp;beneath the&nbsp;<code>open-weather<\/code>&nbsp;folder. Populate the&nbsp;<code>open-weather\/.content.xml<\/code>&nbsp;with the following:<br><\/li><\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">&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>=\"Open Weather\"\n          <em>componentGroup<\/em>=\"WKND SPA Vue - Content\"\/&gt;<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p><code>jcr:primaryType=\"cq:Component\"<\/code>&nbsp;&#8211; identifies that this node will be an AEM component.<\/p>\n\n\n\n<p><code>jcr:title<\/code>&nbsp;is the value that will be displayed to Content Authors and the&nbsp;<code>componentGroup<\/code>&nbsp;determines the grouping of components in the authoring UI.<\/p>\n\n\n\n<ol start=\"4\"><li>Beneath the&nbsp;<code>custom-component<\/code>&nbsp;folder, create another folder named&nbsp;<code>_cq_dialog<\/code>.<br><\/li><li>Beneath the&nbsp;<code>_cq_dialog<\/code>&nbsp;folder create a new file named&nbsp;<code>.content.xml<\/code>&nbsp;and populate it with 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;<br>&lt;jcr:root <em>xmlns:<\/em><em>sling<\/em>=\"http:\/\/sling.apache.org\/jcr\/sling\/1.0\" <em>xmlns:<\/em><em>granite<\/em>=\"http:\/\/www.adobe.com\/jcr\/granite\/1.0\" <em>xmlns:<\/em><em>cq<\/em>=\"http:\/\/www.day.com\/jcr\/cq\/1.0\" <em>xmlns:<\/em><em>jcr<\/em>=\"http:\/\/www.jcp.org\/jcr\/1.0\" <em>xmlns:<\/em><em>nt<\/em>=\"http:\/\/www.jcp.org\/jcr\/nt\/1.0\"<br>          <em>jcr<\/em><em>:primaryType<\/em>=\"nt:unstructured\"<br>          <em>jcr<\/em><em>:title<\/em>=\"Open Weather\"<br>          <em>sling<\/em><em>:resourceType<\/em>=\"cq\/gui\/components\/authoring\/dialog\"&gt;<br>    &lt;content<br>            <em>jcr<\/em><em>:primaryType<\/em>=\"nt:unstructured\"<br>            <em>sling<\/em><em>:resourceType<\/em>=\"granite\/ui\/components\/coral\/foundation\/container\"&gt;<br>        &lt;items <em>jcr<\/em><em>:primaryType<\/em>=\"nt:unstructured\"&gt;<br>            &lt;tabs<br>                    <em>jcr<\/em><em>:primaryType<\/em>=\"nt:unstructured\"<br>                    <em>sling<\/em><em>:resourceType<\/em>=\"granite\/ui\/components\/coral\/foundation\/tabs\"<br>                    <em>maximized<\/em>=\"{Boolean}true\"&gt;<br>                &lt;items <em>jcr<\/em><em>:primaryType<\/em>=\"nt:unstructured\"&gt;<br>                    &lt;properties<br>                            <em>jcr<\/em><em>:primaryType<\/em>=\"nt:unstructured\"<br>                            <em>jcr<\/em><em>:title<\/em>=\"Properties\"<br>                            <em>sling<\/em><em>:resourceType<\/em>=\"granite\/ui\/components\/coral\/foundation\/container\"<br>                            <em>margin<\/em>=\"{Boolean}true\"&gt;<br>                        &lt;items <em>jcr<\/em><em>:primaryType<\/em>=\"nt:unstructured\"&gt;<br>                            &lt;columns<br>                                    <em>jcr<\/em><em>:primaryType<\/em>=\"nt:unstructured\"<br>                                    <em>sling<\/em><em>:resourceType<\/em>=\"granite\/ui\/components\/coral\/foundation\/fixedcolumns\"<br>                                    <em>margin<\/em>=\"{Boolean}true\"&gt;<br>                                &lt;items <em>jcr<\/em><em>:primaryType<\/em>=\"nt:unstructured\"&gt;<br>                                    &lt;column<br>                                            <em>jcr<\/em><em>:primaryType<\/em>=\"nt:unstructured\"<br>                                            <em>sling<\/em><em>:resourceType<\/em>=\"granite\/ui\/components\/coral\/foundation\/container\"&gt;<br>                                        &lt;items <em>jcr<\/em><em>:primaryType<\/em>=\"nt:unstructured\"&gt;<br>                                            &lt;label<br>                                                    <em>jcr<\/em><em>:primaryType<\/em>=\"nt:unstructured\"<br>                                                    <em>sling<\/em><em>:resourceType<\/em>=\"granite\/ui\/components\/coral\/foundation\/form\/textfield\"<br>                                                    <em>fieldDescription<\/em>=\"The label to display for the component\"<br>                                                    <em>fieldLabel<\/em>=\"Label\"<br>                                                    <em>name<\/em>=\".\/label\"\/&gt;<br>                                        &lt;\/items&gt;<br>                                    &lt;\/column&gt;<br>                                &lt;\/items&gt;<br>                            &lt;\/columns&gt;<br>                        &lt;\/items&gt;<br>                    &lt;\/properties&gt;<br>                &lt;\/items&gt;<br>            &lt;\/tabs&gt;<br>        &lt;\/items&gt;<br>    &lt;\/content&gt;<br>&lt;\/jcr:root&gt;<\/pre>\n\n\n\n<p>The above XML file generates a very simple dialog for the&nbsp;<code>Weather Component<\/code>. The critical part of the file is the inner&nbsp;<code>&lt;label&gt;<\/code>. This dialog will contain a&nbsp;<code>textfield<\/code>&nbsp;that will allow a user to configure the weather to be displayed.<\/p>\n\n\n\n<p>A Sling Model will be created next to expose the value of the&nbsp;<code>label<\/code>&nbsp;properties via the JSON model.<\/p>\n\n\n\n<p>You can view a lot more&nbsp;<a href=\"https:\/\/github.com\/adobe\/aem-core-wcm-components\/tree\/master\/content\/src\/content\/jcr_root\/apps\/core\/wcm\/components\">examples of dialogs by viewing the Core Component definitions<\/a>. You can also view additional form fields, like&nbsp;<code>select<\/code>,&nbsp;<code>textarea<\/code>,&nbsp;<code>pathfield<\/code>, available beneath&nbsp;<code>\/libs\/granite\/ui\/components\/coral\/foundation\/form<\/code>&nbsp;in&nbsp;<a href=\"http:\/\/localhost:4502\/crx\/de\/index.jsp#\/libs\/granite\/ui\/components\/coral\/foundation\/form\">CRXDE-Lite<\/a>.<\/p>\n\n\n\n<p>With a traditional AEM component, an&nbsp;<a href=\"https:\/\/docs.adobe.com\/content\/help\/en\/experience-manager-htl\/using\/overview.html\">HTL<\/a>&nbsp;script is typically required. Since the SPA will render the component, no HTL script is needed.<br><br><\/p>\n\n\n\n<h2 id=\"create-the-sling-model\">Create the Sling Model<\/h2>\n\n\n\n<p>Sling Models are annotation driven Java \u201cPOJO\u2019s\u201d (Plain Old Java Objects) that facilitate the mapping of data from the JCR to Java variables.&nbsp;<a href=\"https:\/\/experienceleague.adobe.com\/docs\/experience-manager-learn\/getting-started-wknd-tutorial-develop\/project-archetype\/component-basics.html?lang=en#sling-models\">Sling Models<\/a>&nbsp;typically function to encapsulate complex server-side business logic for AEM Components.<\/p>\n\n\n\n<p>In the context of the SPA Editor, Sling Models expose a component\u2019s content through the JSON model through a feature using the&nbsp;<a href=\"https:\/\/experienceleague.adobe.com\/docs\/experience-manager-learn\/foundation\/development\/develop-sling-model-exporter.html?lang=en\">Sling Model Exporter<\/a>.<\/p>\n\n\n\n<ol><li>In the IDE of your choice open the&nbsp;<code>core<\/code>&nbsp;module at&nbsp;<code>aem-guides-wknd-spa-vue\/core<\/code>.<\/li><li>Create a file named at&nbsp;<code>OpenWeatherModel.java<\/code>&nbsp;at&nbsp;<code>core\/src\/main\/java\/com\/adobe\/aem\/guides\/wknd\/spa\/vue\/core\/models<\/code>.<\/li><li>Populate&nbsp;<code>OpenWeatherModel.java<\/code>&nbsp;with the following:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\"><em>package <\/em>com.adobe.aem.guides.wknd.spa.vue.core.models;\n\n<em>import <\/em>com.adobe.cq.export.json.ComponentExporter;\n\n<em>\/\/ Sling Models intended to be used with SPA Editor must extend ComponentExporter interface\npublic interface OpenWeatherModel extends <\/em>ComponentExporter {\n\n    <em>public <\/em>String getLabel();\n\n}<\/pre>\n\n\n\n<ol start=\"4\"><li>This is the Java interface for our component. In order for our Sling Model to be compatible with the SPA Editor framework it must extend the&nbsp;<code>ComponentExporter<\/code>&nbsp;class.<\/li><li>Create a folder named&nbsp;<code>impl<\/code>&nbsp;beneath&nbsp;<code>core\/src\/main\/java\/com\/adobe\/aem\/guides\/wknd\/spa\/vue\/core\/models<\/code>.<\/li><li>Create a file named&nbsp;<code>OpenWeatherModelImpl.java<\/code>&nbsp;beneath&nbsp;<code>impl<\/code>&nbsp;and populate with the following:<br><\/li><\/ol>\n\n\n\n<pre class=\"wp-block-preformatted\"><em>package <\/em>com.adobe.aem.guides.wknd.spa.vue.core.models.impl;\n\n<em>import <\/em>org.apache.sling.models.annotations.*;\n<em>import <\/em>org.apache.sling.models.annotations.injectorspecific.ValueMapValue;\n<em>import <\/em>com.adobe.cq.export.json.ComponentExporter;\n<em>import <\/em>com.adobe.cq.export.json.ExporterConstants;\n<em>import <\/em>org.apache.commons.lang3.StringUtils;\n<em>import <\/em>org.apache.sling.api.SlingHttpServletRequest;\n<em>import <\/em>com.adobe.aem.guides.wknd.spa.vue.core.models.<em>OpenWeatherModel<\/em>;\n\n<em>\/\/ Sling Model annotation\n<\/em>@Model(\n        adaptables = SlingHttpServletRequest.<em>class<\/em>,\n        adapters = { <em>OpenWeatherModel<\/em>.<em>class<\/em>, ComponentExporter.<em>class <\/em>},\n        resourceType = OpenWeatherModelImpl.<em>RESOURCE_TYPE<\/em>,\n        defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL\n)\n@Exporter( <em>\/\/Exporter annotation that serializes the modoel as JSON\n        <\/em>name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,\n        extensions = ExporterConstants.SLING_MODEL_EXTENSION\n)\n<em>public class <\/em>OpenWeatherModelImpl <em>implements OpenWeatherModel <\/em>{\n\n    @ValueMapValue\n    <em>private <\/em>String label; <em>\/\/maps variable to jcr property named \"label\" persisted by Dialog\n\n    \/\/ points to AEM component definition in ui.apps\n    static final <\/em>String <em>RESOURCE_TYPE <\/em>= \"wknd-spa-vue\/components\/open-weather\";\n\n    <em>\/\/ public getter method to expose value of private variable `label`\n    \/\/ adds additional logic to default the label to \"(Default)\" if not set.\n    <\/em>@Override\n    <em>public <\/em>String getLabel() {\n        <em>return <\/em>StringUtils.isNotBlank(label) ? label : \"(Default)\";\n    }\n\n    <em>\/\/ method required by `ComponentExporter` interface\n    \/\/ exposes a JSON property named `:type` with a value of `wknd-spa-vue\/components\/open-weather`\n    \/\/ required to map the JSON export to the SPA component props via the `MapTo`\n    <\/em>@Override\n    <em>public <\/em>String getExportedType() {\n        <em>return <\/em>OpenWeatherModelImpl.<em>RESOURCE_TYPE<\/em>;\n    }\n}<\/pre>\n\n\n\n<p>The the static variable&nbsp;<code>RESOURCE_TYPE<\/code>&nbsp;must point to the path in&nbsp;<code>ui.apps<\/code>&nbsp;of the component. The&nbsp;<code>getExportedType()<\/code>&nbsp;is used to map the JSON properties to the SPA component via&nbsp;<code>MapTo<\/code>.&nbsp;<code>@ValueMapValue<\/code>&nbsp;is an annotation that reads the jcr property saved by the dialog.<\/p>\n\n\n\n<h2 id=\"update-the-spa\">Update the SPA<\/h2>\n\n\n\n<p>Next, update the Vue code to include the\u00a0<a href=\"https:\/\/www.npmjs.com\/package\/react-open-weather\"><a href=\"https:\/\/www.npmjs.com\/package\/vue-open-weather-widget\">Vue Open Weather Widget<\/a><\/a>\u00a0and have it map to the AEM component created in the previous steps.<\/p>\n\n\n\n<ol><li>Install the <a href=\"https:\/\/www.npmjs.com\/package\/vue-open-weather-widget\">Vue Open Weather Widget<\/a> as an&nbsp;<strong>npm<\/strong>&nbsp;dependency:<\/li><\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">$ cd aem-guides-wknd-spa-vue\/ui.frontend<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">npm install vue-open-weather-widget<\/code><\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p>Create a new folder named&nbsp;<code>OpenWeather<\/code>&nbsp;at&nbsp;<code>ui.frontend\/src\/components\/OpenWeather<\/code>.<\/p>\n\n\n\n<p>Add a file named&nbsp;<code>OpenWeather.<\/code>vue&nbsp;and populate it with the following:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;template&gt;\n  &lt;div <em>class<\/em>=\"weather-widget-container\"&gt;\n    &lt;open-weather-widget <em>:api-key<\/em>=\"API_KEY\"\/&gt;\n  &lt;\/div&gt;\n&lt;\/template&gt;\n&lt;script&gt;\n<em>\/\/ For simplicity it is hard coded in the file, ideally this is extracted in to an environment variable<\/em>\n\n<em>import <\/em>OpenWeatherWidget <em>from <\/em>'vueOpenWeatherWidget'\n<em>import <\/em>'vue-open-weather-widget\/dist\/vue-open-weather-widget.css'\n\n<em>export default <\/em>{\n  name: 'OpenWeather',\n  components: {\n    OpenWeatherWidget\n  }\n}\n&lt;\/script&gt;\n\n&lt;style <em>scoped<\/em>&gt;\n\n&lt;\/style&gt;<\/pre>\n\n\n\n<p><br>Update&nbsp;<code>map-components.js<\/code>&nbsp;at&nbsp;<code>ui.frontend\/src\/components\/map-components.js<\/code>&nbsp;to include the&nbsp;<code>OpenWeather<\/code>&nbsp;component:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><em>\/\/ Open Weather Component Mapping<br><\/em>MapTo('wknd-spa-vue\/components\/open-weather')(<br>  OpenWeather,<br>  {<br>    emptyLabel: 'Open Weather',<br>    isEmpty: <em>function <\/em>(props) {<br>      <em>return <\/em>!props || !props.label || props.labek.trim().length &lt; 1<br>    },<br>    resourceType: 'wknd-spa-vue\/components\/open-weather'<br>  }<br>)<\/pre>\n\n\n\n<ol start=\"2\"><li>Deploy all of the updates to a local AEM environment from the root of the project directory<br><code>$ cd aem-guides-wknd-spa-vue <\/code><br><code>$ mvn clean install -PautoInstallSinglePackage<\/code><\/li><\/ol>\n\n\n\n<h2 id=\"update-the-template-policy\">Update the Template Policy<\/h2>\n\n\n\n<p>Next, navigate to AEM to verify the updates and allow the&nbsp;<code>OpenWeather<\/code>&nbsp;component to be added to the SPA.<\/p>\n\n\n\n<ol><li>Verify the registration of the new Sling Model by navigating to&nbsp;<a href=\"http:\/\/localhost:4502\/system\/console\/status-slingmodels\">http:\/\/localhost:4502\/system\/console\/status-slingmodels<\/a>.<br><br>You should see the above two lines that indicate the&nbsp;<code>OpenWeatherModelImpl<\/code>&nbsp;is associated with the&nbsp;<code>wknd-spa-vue\/components\/open-weather<\/code>&nbsp;component and that it is registered via the Sling Model Exporter.<br><\/li><li>Navigate to the SPA Page Template at&nbsp;<a href=\"http:\/\/localhost:4502\/editor.html\/conf\/wknd-spa-vue\/settings\/wcm\/templates\/spa-page-template\/structure.html\">http:\/\/localhost:4502\/editor.html\/conf\/wknd-spa-vue\/settings\/wcm\/templates\/spa-page-template\/structure.html<\/a>.<br><\/li><li>Update the Layout Container\u2019s policy to add the new&nbsp;<code>Open Weather<\/code>&nbsp;as an allowed component:<\/li><\/ol>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"948\" height=\"414\" src=\"https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-12.45.59-PM.png\" alt=\"\" class=\"wp-image-573\" srcset=\"https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-12.45.59-PM.png 948w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-12.45.59-PM-300x131.png 300w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-12.45.59-PM-768x335.png 768w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-12.45.59-PM-360x157.png 360w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-12.45.59-PM-640x279.png 640w\" sizes=\"(max-width: 948px) 100vw, 948px\" \/><\/figure>\n\n\n\n<p>Save the changes to the policy.<\/p>\n\n\n\n<h2 id=\"author-the-open-weather-component\">Author the Open Weather Component<\/h2>\n\n\n\n<p>Next, author the&nbsp;<code>Open Weather<\/code>&nbsp;component using the AEM SPA Editor.<\/p>\n\n\n\n<ol><li>Navigate to&nbsp;<a href=\"http:\/\/localhost:4502\/editor.html\/content\/wknd-spa-vue\/us\/en\/home.html\">http:\/\/localhost:4502\/editor.html\/content\/wknd-spa-vue\/us\/en\/home.html<\/a>.<\/li><li>In&nbsp;<code>Edit<\/code>&nbsp;mode, add the&nbsp;<code>Open Weather<\/code>&nbsp;to the&nbsp;<code>Layout Container<\/code>:<br><\/li><\/ol>\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.02.34-PM.png\" alt=\"\" class=\"wp-image-575\" width=\"268\" height=\"262\" srcset=\"https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.02.34-PM.png 642w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.02.34-PM-300x293.png 300w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.02.34-PM-360x352.png 360w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.02.34-PM-640x626.png 640w\" sizes=\"(max-width: 268px) 100vw, 268px\" \/><\/figure><\/div>\n\n\n\n<p>Open the component\u2019s dialog and enter a&nbsp;<strong>Label<\/strong>.<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"301\" src=\"https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.00.31-PM-1024x301.png\" alt=\"\" class=\"wp-image-574\" srcset=\"https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.00.31-PM-1024x301.png 1024w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.00.31-PM-300x88.png 300w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.00.31-PM-768x226.png 768w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.00.31-PM-360x106.png 360w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.00.31-PM-1040x306.png 1040w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.00.31-PM-640x188.png 640w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.00.31-PM.png 1340w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>This is the dialog that was created based on the XML file earlier in the chapter.<\/p>\n\n\n\n<p>Save the changes and allow the browser to have access to your location. Observe that the weather for&nbsp;<strong>your location<\/strong>&nbsp;is now displayed:<\/p>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" width=\"1024\" height=\"671\" src=\"https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.10.21-PM-1024x671.png\" alt=\"\" class=\"wp-image-577\" srcset=\"https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.10.21-PM-1024x671.png 1024w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.10.21-PM-300x197.png 300w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.10.21-PM-768x504.png 768w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.10.21-PM-360x236.png 360w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.10.21-PM-1040x682.png 1040w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.10.21-PM-640x420.png 640w, https:\/\/www.mavice.com\/blog\/wp-content\/uploads\/2021\/06\/Screen-Shot-2021-06-29-at-1.10.21-PM.png 1272w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p><\/p>\n\n\n\n<h2 id=\"congratulations\">Congratulations!<\/h2>\n\n\n\n<p>Congratulations, you learned how to create a custom AEM component to be used with the SPA Editor. You also learned how dialogs, JCR properties, and Sling Models interact to output the JSON model.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>*This article was modeled from the current Adobe React SPA&nbsp;documentation. Create a Custom WeatherComponent Learn how to create a custom weather component to be used with the AEM SPA Editor. Learn how to develop author dialogs and Sling Models to extend the JSON model to populate a custom component. The&nbsp;Open&#8230;<\/p>\n","protected":false},"author":2,"featured_media":616,"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\/571"}],"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=571"}],"version-history":[{"count":3,"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/posts\/571\/revisions"}],"predecessor-version":[{"id":617,"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/posts\/571\/revisions\/617"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/media\/616"}],"wp:attachment":[{"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/media?parent=571"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/categories?post=571"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mavice.com\/blog\/wp-json\/wp\/v2\/tags?post=571"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}