了解如何建立要與AEM SPA編輯器搭配使用的自訂天氣元件。 了解如何開發製作對話方塊和Sling模型,以擴充JSON模型以填入自訂元件。 使用開放天氣API和反應開放天氣元件。
將建立簡單的天氣元件。 內容作者可將此元件新增至SPA。 使用AEM對話方塊,作者可設定天氣的顯示位置。 此元件的實作說明建立與AEM SPA Editor架構相容的全新AEM元件所需執行的步驟。
查看設定本地開發環境所需的工具和說明。 本章是導航和路由章節的繼續,但是,您只需要部署到本地AEM實例的啟用SPA的AEM項目即可。
若要遵循本教學課程,需要Open Weather的API金鑰。 註冊是免 費的,只需數量有限的API呼叫。
AEM元件定義為節點和屬性。 在項目中,這些節點和屬性在ui.apps
模組中以XML檔案的形式表示。 接下來,在ui.apps
模組中建立AEM元件。
在所選IDE中,開啟ui.apps
資料夾。
導覽至ui.apps/src/main/content/jcr_root/apps/wknd-spa-react/components
並建立名為open-weather
的新資料夾。
在open-weather
資料夾下建立名為.content.xml
的新檔案。 將下列項目填入open-weather/.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="Open Weather"
componentGroup="WKND SPA React - Content"/>
jcr:primaryType="cq:Component"
— 標識此節點將是AEM元件。
jcr:title
是要顯示給內容作者的值,並決 componentGroup
定製作UI中的元件分組。
在custom-component
資料夾下,建立另一個名為_cq_dialog
的資料夾。
在_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="Open Weather"
sling:resourceType="cq/gui/components/authoring/dialog">
<content
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/container">
<items jcr:primaryType="nt:unstructured">
<tabs
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/tabs"
maximized="{Boolean}true">
<items jcr:primaryType="nt:unstructured">
<properties
jcr:primaryType="nt:unstructured"
jcr:title="Properties"
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">
<label
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldDescription="The label to display for the component"
fieldLabel="Label"
name="./label"/>
<lat
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/numberfield"
fieldDescription="The latitude of the location."
fieldLabel="Latitude"
step="any"
name="./lat" />
<lon
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/numberfield"
fieldDescription="The longitude of the location."
fieldLabel="Longitude"
step="any"
name="./lon"/>
</items>
</column>
</items>
</columns>
</items>
</properties>
</items>
</tabs>
</items>
</content>
</jcr:root>
上面的XML檔案為Weather Component
生成一個非常簡單的對話框。 檔案的關鍵部分是內部<label>
、<lat>
和<lon>
節點。 此對話方塊將包含兩個numberfield
s和一個textfield
,可讓使用者設定要顯示的天氣。
系統會在旁邊建立Sling模型,以透過JSON模型公開label
、lat
和long
屬性的值。
您可以檢視核心元件定義🔗以檢視更多對話方塊範例。 您也可以檢視其他表單欄位,例如select
、textarea
、pathfield
,可在CRXDE-Lite的/libs/granite/ui/components/coral/foundation/form
下方取得。
若使用傳統AEM元件,通常需要 HTL指令碼。 由於SPA會轉譯元件,因此不需要HTL指令碼。
Sling模型是註解導向的Java "POJO's"(純舊Java物件),可方便將資料從JCR對應至Java變數。 Sling Model 一般能封裝AEM元件的複雜伺服器端業務邏輯。
在SPA編輯器的內容中,Sling模型會透過使用Sling Model Exporter的功能,透過JSON模型公開元件的內容。
在您選擇的IDE中,在aem-guides-wknd-spa.react/core
開啟core
模組。
在core/src/main/java/com/adobe/aem/guides/wkndspa/react/core/models
建立名為OpenWeatherModel.java
的檔案。
將以下內容填入OpenWeatherModel.java
:
package com.adobe.aem.guides.wkndspa.react.core.models;
import com.adobe.cq.export.json.ComponentExporter;
// Sling Models intended to be used with SPA Editor must extend ComponentExporter interface
public interface OpenWeatherModel extends ComponentExporter {
public String getLabel();
public double getLat();
public double getLon();
}
這是元件的Java介面。 為了讓我們的Sling模型與SPA Editor架構相容,必須擴充ComponentExporter
類別。
在core/src/main/java/com/adobe/aem/guides/wkndspa/react/core/models
下方建立名為impl
的資料夾。
在impl
下方建立名為OpenWeatherModelImpl.java
的檔案,並填入下列內容:
package com.adobe.aem.guides.wkndspa.react.core.models.impl;
import org.apache.sling.models.annotations.*;
import org.apache.sling.models.annotations.injectorspecific.ValueMapValue;
import com.adobe.cq.export.json.ComponentExporter;
import com.adobe.cq.export.json.ExporterConstants;
import org.apache.commons.lang3.StringUtils;
import org.apache.sling.api.SlingHttpServletRequest;
import com.adobe.aem.guides.wkndspa.react.core.models.OpenWeatherModel;
// Sling Model annotation
@Model(
adaptables = SlingHttpServletRequest.class,
adapters = { OpenWeatherModel.class, ComponentExporter.class },
resourceType = OpenWeatherModelImpl.RESOURCE_TYPE,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
@Exporter( //Exporter annotation that serializes the modoel as JSON
name = ExporterConstants.SLING_MODEL_EXPORTER_NAME,
extensions = ExporterConstants.SLING_MODEL_EXTENSION
)
public class OpenWeatherModelImpl implements OpenWeatherModel {
@ValueMapValue
private String label; //maps variable to jcr property named "label" persisted by Dialog
@ValueMapValue
private double lat; //maps variable to jcr property named "lat"
@ValueMapValue
private double lon; //maps variable to jcr property named "lon"
// points to AEM component definition in ui.apps
static final String RESOURCE_TYPE = "wknd-spa-react/components/open-weather";
// public getter method to expose value of private variable `label`
// adds additional logic to default the label to "(Default)" if not set.
@Override
public String getLabel() {
return StringUtils.isNotBlank(label) ? label : "(Default)";
}
// public getter method to expose value of private variable `lat`
@Override
public double getLat() {
return lat;
}
// public getter method to expose value of private variable `lon`
@Override
public double getLon() {
return lon;
}
// method required by `ComponentExporter` interface
// exposes a JSON property named `:type` with a value of `wknd-spa-react/components/open-weather`
// required to map the JSON export to the SPA component props via the `MapTo`
@Override
public String getExportedType() {
return OpenWeatherModelImpl.RESOURCE_TYPE;
}
}
靜態變數RESOURCE_TYPE
必須指向元件ui.apps
中的路徑。 getExportedType()
可用來透過MapTo
將JSON屬性對應至SPA元件。 @ValueMapValue
是一個注釋,用於讀取對話框保存的jcr屬性。
接下來,更新React程式碼以包含 React開放天氣元件,並將其對應至先前步驟中建立的AEM元件。
將React Open Weather元件安裝為npm相依性:
$ cd aem-guides-wknd-spa.react/ui.frontend
$ npm i react-open-weather
在ui.frontend/src/components/OpenWeather
建立名為OpenWeather
的新資料夾。
添加名為OpenWeather.js
的檔案,並填入以下內容:
import React from 'react';
import {MapTo} from '@adobe/aem-react-editable-components';
import ReactWeather, { useOpenWeather } from 'react-open-weather';
// Open weather API Key
// For simplicity it is hard coded in the file, ideally this is extracted in to an environment variable
const API_KEY = 'YOUR_API_KEY';
// Logic to render placeholder or component
const OpenWeatherEditConfig = {
emptyLabel: 'Weather',
isEmpty: function(props) {
return !props || !props.lat || !props.lon || !props.label;
}
};
// Wrapper function that includes react-open-weather component
function ReactWeatherWrapper(props) {
const { data, isLoading, errorMessage } = useOpenWeather({
key: API_KEY,
lat: props.lat, // passed in from AEM JSON
lon: props.lon, // passed in from AEM JSON
lang: 'en',
unit: 'imperial', // values are (metric, standard, imperial)
});
return (
<div className="cmp-open-weather">
<ReactWeather
isLoading={isLoading}
errorMessage={errorMessage}
data={data}
lang="en"
locationLabel={props.label} // passed in from AEM JSON
unitsLabels={{ temperature: 'F', windSpeed: 'mph' }}
showForecast={false}
/>
</div>
);
}
export default function OpenWeather(props) {
// render nothing if component not configured
if(OpenWeatherEditConfig.isEmpty(props)) {
return null;
}
// render ReactWeather component if component configured
// pass props to ReactWeatherWrapper. These props include the mapped properties from AEM JSON
return ReactWeatherWrapper(props);
}
// Map OpenWeather to AEM component
MapTo('wknd-spa-react/components/open-weather')(OpenWeather, OpenWeatherEditConfig);
在ui.frontend/src/components/import-components.js
更新import-components.js
以包含OpenWeather
元件:
// import-component.js
import './Container/Container';
import './ExperienceFragment/ExperienceFragment';
+ import './OpenWeather/OpenWeather';
使用您的Maven技能,從專案目錄的根目錄,將所有更新部署至本機AEM環境:
$ cd aem-guides-wknd-spa.react
$ mvn clean install -PautoInstallSinglePackage
接下來,導覽至AEM以驗證更新並允許將OpenWeather
元件新增至SPA。
導覽至http://localhost:4502/system/console/status-slingmodels以驗證新Sling模型的註冊。
com.adobe.aem.guides.wkndspa.react.core.models.impl.OpenWeatherModelImpl - wknd-spa-react/components/open-weather
com.adobe.aem.guides.wkndspa.react.core.models.impl.OpenWeatherModelImpl exports 'wknd-spa-react/components/open-weather' with selector 'model' and extension '[Ljava.lang.String;@2fd80fc5' with exporter 'jackson'
您應會看到上述兩行,指出OpenWeatherModelImpl
與wknd-spa-react/components/open-weather
元件相關聯,且已透過Sling模型匯出工具註冊。
導覽至SPA頁面範本,網址為http://localhost:4502/editor.html/conf/wknd-spa-react/settings/wcm/templates/spa-page-template/structure.html。
更新「配置容器」的策略,以將新Open Weather
添加為允許的元件:
保存對策略的更改,並將Open Weather
作為允許的元件進行觀察:
接下來,使用AEM SPA編輯器編寫Open Weather
元件。
導覽至http://localhost:4502/editor.html/content/wknd-spa-react/us/en/home.html。
在Edit
模式中,將Open Weather
新增至Layout Container
:
開啟元件的對話框,然後輸入Label、Latitude和Longitude。 例如San Diego、32.7157和-117.1611。 西半球和南半球的數字以開放天氣API表示為負數
這是在章節前面根據XML檔案建立的對話框。
儲存變更。請注意,現在已顯示聖地亞哥的天氣:
導覽至http://localhost:4502/content/wknd-spa-react/us/en.model.json以檢視JSON模型。 搜尋 wknd-spa-react/components/open-weather
:
"open_weather": {
"label": "San Diego",
"lat": 32.7157,
"lon": -117.1611,
":type": "wknd-spa-react/components/open-weather"
}
JSON值會由Sling模型輸出。 這些JSON值會以prop的形式傳入React元件。
恭喜您,您已學會如何建立要與SPA編輯器搭配使用的自訂AEM元件。 您也了解對話方塊、JCR屬性和Sling模型如何互動以輸出JSON模型。
擴充核心元件 — 了解如何擴充現有AEM核心元件以與AEM SPA編輯器搭配使用。了解如何將屬性和內容新增至現有元件,是擴充AEM SPA Editor實作功能的強大技術。