新增導覽和路由 navigation-routing
瞭解如何使用SPA頁面和AEM編輯器SDK支援SPA中的多個檢視。 動態導覽是使用Angular路由實作,並新增到現有的Header元件。
目標
- 瞭解使用SPA編輯器時可用的SPA模型路由選項。
- 瞭解如何使用 angular路由 以瀏覽SPA的不同檢視。
- 實作由AEM頁面階層驅動的動態導覽。
您將建置的內容
本章將導覽功能表新增至現有的 Header
元件。 導覽功能表是由AEM頁面階層所驅動,並使用 導覽核心元件.
先決條件
檢閱設定所需的工具和指示 本機開發環境.
取得程式碼
-
透過Git下載本教學課程的起點:
code language-shell $ git clone git@github.com:adobe/aem-guides-wknd-spa.git $ cd aem-guides-wknd-spa $ git checkout Angular/navigation-routing-start
-
使用Maven將程式碼庫部署到本機AEM執行個體:
code language-shell $ mvn clean install -PautoInstallSinglePackage
若使用 AEM 6.x 新增
classic
設定檔:code language-shell $ mvn clean install -PautoInstallSinglePackage -Pclassic
-
安裝完成的傳統套件 WKND參考網站. 提供的影像 WKND參考網站 在WKND SPA上重複使用。 套件可使用以下方式安裝: AEM封裝管理員.
您一律可以於檢視完成的程式碼 GitHub 或切換至分支以在本機取出程式碼 Angular/navigation-routing-solution
.
Inspect HeaderComponent更新 inspect-header
在前幾章中, HeaderComponent
元件是透過新增為純Angular元件 app.component.html
. 在本章中, HeaderComponent
元件會從應用程式移除,並透過以下方式新增: 範本編輯器. 這可讓使用者設定 HeaderComponent
從AEM中。
-
在您選擇的IDE中,開啟本章的SPA入門專案。
-
在
ui.frontend
模組檢查檔案header.component.ts
於:ui.frontend/src/app/components/header/header.component.ts
.已進行數個更新,包括新增
HeaderEditConfig
和MapTo
啟用元件對應至AEM元件的方式wknd-spa-angular/components/header
.code language-js /* header.component.ts */ ... const HeaderEditConfig = { ... }; @Component({ selector: 'app-header', templateUrl: './header.component.html', styleUrls: ['./header.component.scss'] }) export class HeaderComponent implements OnInit { @Input() items: object[]; ... } ... MapTo('wknd-spa-angular/components/header')(withRouter(Header), HeaderEditConfig);
請注意
@Input()
註解items
.items
將包含從AEM傳入的導覽物件陣列。 -
在
ui.apps
模組檢查AEM的元件定義Header
元件:ui.apps/src/main/content/jcr_root/apps/wknd-spa-angular/components/header/.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="Header" sling:resourceSuperType="wknd-spa-angular/components/navigation" componentGroup="WKND SPA Angular - Structure"/>
AEM
Header
元件將繼承的所有功能 導覽核心元件 透過sling:resourceSuperType
屬性。
將HeaderComponent新增至SPA範本 add-header-template
-
開啟瀏覽器並登入AEM, http://localhost:4502/. 起始程式碼基底應該已經部署。
-
導覽至 SPA頁面範本: http://localhost:4502/editor.html/conf/wknd-spa-angular/settings/wcm/templates/spa-page-template/structure.html.
-
選取最外層 根配置容器 並按一下 原則 圖示。 請小心 非 以選取 配置容器 已解除製作鎖定。
-
複製目前的原則並建立名為的新原則 SPA結構:
在 允許的元件 > 一般 >選取 配置容器 元件。
在 允許的元件 > WKND SPAANGULAR — 結構 >選取 頁首 元件:
在 允許的元件 > WKND SPAANGULAR — 內容 >選取 影像 和 文字 元件。 您總共應選取4個元件。
按一下「完成」以儲存變更。
-
重新整理 頁面。 新增 頁首 元件在解鎖前面 配置容器:
-
選取 頁首 元件並按一下其 原則 圖示可編輯原則。
-
使用建立新原則 原則標題 之 "WKND SPA標題".
在 屬性:
- 設定 導覽根目錄 至
/content/wknd-spa-angular/us/en
. - 設定 排除根層級 至 1.
- 取消核取 收集所有子頁面.
- 設定 導覽結構深度 至 3.
這會收集下方2個層級的導覽
/content/wknd-spa-angular/us/en
. - 設定 導覽根目錄 至
-
儲存變更後,您應會看到已填入的
Header
做為範本的一部分:
建立子頁面
接著,在AEM中建立其他頁面,做為SPA中的不同檢視。 我們也會檢查AEM所提供JSON模型的階層結構。
-
導覽至 網站 主控台: http://localhost:4502/sites.html/content/wknd-spa-angular/us/en/home. 選取 WKND SPAAngular首頁 並按一下 建立 > 頁面:
-
在 範本 選取 SPA頁面. 在 屬性 進入 "第1頁" 針對 標題 和 "page-1" 作為名稱。
按一下 建立 而在對話方塊快顯視窗中,按一下 開啟 以在AEM SPA編輯器中開啟頁面。
-
新增 文字 元件至主要 配置容器. 編輯元件並輸入文字: "第1頁" 使用RTE和 H1 元素(您必須進入全熒幕模式才能變更段落元素)
您可以隨意新增其他內容,例如影像。
-
返回AEM Sites主控台並重複上述步驟,建立第二個頁面,名為 「第2頁」 做為同層級 第1頁. 新增內容至 第2頁 以便輕鬆識別。
-
最後,建立第三頁, 「第3頁」 但 子項 之 第2頁. 完成後,網站階層應如下所示:
-
在新標籤中,開啟AEM提供的JSON模型API: http://localhost:4502/content/wknd-spa-angular/us/en.model.json. SPA首次載入時會請求此JSON內容。 外部結構如下所示:
code language-json { "language": "en", "title": "en", "templateName": "spa-app-template", "designPath": "/libs/settings/wcm/designs/default", "cssClassNames": "spa page basicpage", ":type": "wknd-spa-angular/components/spa", ":items": {}, ":itemsOrder": [], ":hierarchyType": "page", ":path": "/content/wknd-spa-angular/us/en", ":children": { "/content/wknd-spa-angular/us/en/home": {}, "/content/wknd-spa-angular/us/en/home/page-1": {}, "/content/wknd-spa-angular/us/en/home/page-2": {}, "/content/wknd-spa-angular/us/en/home/page-2/page-3": {} } }
在
:children
您應該會看見每個已建立頁面的專案。 所有頁面的內容都在此初始JSON請求中。 一旦導覽路由實作後,SPA的後續檢視就會快速載入,因為內容在使用者端即可使用。載入是不明智的 全部 SPA內容於初始JSON請求中的變數,因為這會減慢初始頁面載入的速度。 接下來,讓我們看看如何收集頁面的階層式深度。
-
導覽至 SPA根目錄 範本位於: http://localhost:4502/editor.html/conf/wknd-spa-angular/settings/wcm/templates/spa-app-template/structure.html.
按一下 頁面屬性功能表 > 頁面原則:
-
此 SPA根目錄 範本有一個額外的 階層結構 索引標籤來控制收集的JSON內容。 此 結構深度 決定網站階層中要多深才能收集下方的子頁面 根. 您也可以使用 結構模式 根據規則運算式篩選掉其他頁面的欄位。
更新 結構深度 至 "2":
按一下 完成 儲存原則的變更。
-
重新開啟JSON模型 http://localhost:4502/content/wknd-spa-angular/us/en.model.json.
code language-json { "language": "en", "title": "en", "templateName": "spa-app-template", "designPath": "/libs/settings/wcm/designs/default", "cssClassNames": "spa page basicpage", ":type": "wknd-spa-angular/components/spa", ":items": {}, ":itemsOrder": [], ":hierarchyType": "page", ":path": "/content/wknd-spa-angular/us/en", ":children": { "/content/wknd-spa-angular/us/en/home": {}, "/content/wknd-spa-angular/us/en/home/page-1": {}, "/content/wknd-spa-angular/us/en/home/page-2": {} } }
請注意 第3頁 路徑已移除:
/content/wknd-spa-angular/us/en/home/page-2/page-3
從初始JSON模型。稍後,我們將觀察AEM SPA Editor SDK如何以動態方式載入其他內容。
實作導覽
接下來,使用新的來實施導覽功能表 NavigationComponent
. 我們可以直接在中新增程式碼 header.component.html
但避免大型元件是較好的作法。 請改為實作 NavigationComponent
日後可能會重複使用。
-
檢閱AEM公開的JSON
Header
元件於 http://localhost:4502/content/wknd-spa-angular/us/en.model.json:code language-json ... "header": { "items": [ { "level": 0, "active": true, "path": "/content/wknd-spa-angular/us/en/home", "description": null, "url": "/content/wknd-spa-angular/us/en/home.html", "lastModified": 1589062597083, "title": "WKND SPA Angular Home Page", "children": [ { "children": [], "level": 1, "active": false, "path": "/content/wknd-spa-angular/us/en/home/page-1", "description": null, "url": "/content/wknd-spa-angular/us/en/home/page-1.html", "lastModified": 1589429385100, "title": "Page 1" }, { "level": 1, "active": true, "path": "/content/wknd-spa-angular/us/en/home/page-2", "description": null, "url": "/content/wknd-spa-angular/us/en/home/page-2.html", "lastModified": 1589429603507, "title": "Page 2", "children": [ { "children": [], "level": 2, "active": false, "path": "/content/wknd-spa-angular/us/en/home/page-2/page-3", "description": null, "url": "/content/wknd-spa-angular/us/en/home/page-2/page-3.html", "lastModified": 1589430413831, "title": "Page 3" } ], } ] } ], ":type": "wknd-spa-angular/components/header"
AEM頁面的階層特性使用JSON進行建模,可用來填入導覽功能表。 記住
Header
元件繼承了 導覽核心元件 透過JSON公開的內容會自動對應至Angular@Input
註解。 -
開啟新的終端機視窗並導覽至
ui.frontend
SPA專案的資料夾。 建立新的NavigationComponent
使用AngularCLI工具:code language-shell $ cd ui.frontend $ ng generate component components/navigation CREATE src/app/components/navigation/navigation.component.scss (0 bytes) CREATE src/app/components/navigation/navigation.component.html (25 bytes) CREATE src/app/components/navigation/navigation.component.spec.ts (656 bytes) CREATE src/app/components/navigation/navigation.component.ts (286 bytes) UPDATE src/app/app.module.ts (2032 bytes)
-
接下來,建立名為的類別
NavigationLink
在新建立的中使用AngularCLIcomponents/navigation
目錄:code language-shell $ cd src/app/components/navigation/ $ ng generate class NavigationLink CREATE src/app/components/navigation/navigation-link.spec.ts (187 bytes) CREATE src/app/components/navigation/navigation-link.ts (32 bytes)
-
返回您選擇的IDE並在以下位置開啟檔案:
navigation-link.ts
在/src/app/components/navigation/navigation-link.ts
. -
填入
navigation-link.ts
,其功能如下:code language-js export class NavigationLink { title: string; path: string; url: string; level: number; children: NavigationLink[]; active: boolean; constructor(data) { this.path = data.path; this.title = data.title; this.url = data.url; this.level = data.level; this.active = data.active; this.children = data.children.map( item => { return new NavigationLink(item); }); } }
這是代表個別導覽連結的簡單類別。 在預期的類別建構函式中
data
作為從AEM傳入的JSON物件。 此類別同時用於NavigationComponent
和HeaderComponent
以輕鬆填入導覽結構。不會執行資料轉換,此類別主要是建立以強式輸入JSON模型。 請注意
this.children
是型別為NavigationLink[]
而且建構函式會遞回建立NavigationLink
中每個專案的物件children
陣列。 召回的JSON模型Header
為階層式。 -
開啟檔案
navigation-link.spec.ts
. 這是的測試檔案NavigationLink
類別。 以下列專案更新它:code language-js import { NavigationLink } from './navigation-link'; describe('NavigationLink', () => { it('should create an instance', () => { const data = { children: [], level: 1, active: false, path: '/content/wknd-spa-angular/us/en/home/page-1', description: null, url: '/content/wknd-spa-angular/us/en/home/page-1.html', lastModified: 1589429385100, title: 'Page 1' }; expect(new NavigationLink(data)).toBeTruthy(); }); });
請注意
const data
遵循先前針對單一連結檢查的相同JSON模型。 這遠非強大的單位測試,但應該足以測試下列的建構函式:NavigationLink
. -
開啟檔案
navigation.component.ts
. 以下列專案更新它:code language-js import { Component, OnInit, Input } from '@angular/core'; import { NavigationLink } from './navigation-link'; @Component({ selector: 'app-navigation', templateUrl: './navigation.component.html', styleUrls: ['./navigation.component.scss'] }) export class NavigationComponent implements OnInit { @Input() items: object[]; constructor() { } get navigationLinks(): NavigationLink[] { if (this.items && this.items.length > 0) { return this.items.map(item => { return new NavigationLink(item); }); } return null; } ngOnInit() {} }
NavigationComponent
預期一個object[]
已命名items
這是來自AEM的JSON模型。 此類別會公開單一方法get navigationLinks()
會傳回陣列NavigationLink
物件。 -
開啟檔案
navigation.component.html
並以下列專案更新:code language-html <ul *ngIf="navigationLinks && navigationLinks.length > 0" class="navigation__group"> <ng-container *ngTemplateOutlet="recursiveListTmpl; context:{ links: navigationLinks }"></ng-container> </ul>
這會產生一個初始
<ul>
並呼叫get navigationLinks()
方法來源navigation.component.ts
. 一個<ng-container>
用於呼叫範本,命名為recursiveListTmpl
並傳遞給navigationLinks
作為變數,名為links
.新增
recursiveListTmpl
下一步:code language-html <ng-template #recursiveListTmpl let-links="links"> <li *ngFor="let link of links" class="{{'navigation__item navigation__item--' + link.level}}"> <a [routerLink]="link.url" class="navigation__item-link" [title]="link.title" [attr.aria-current]="link.active"> {{link.title}} </a> <ul *ngIf="link.children && link.children.length > 0"> <ng-container *ngTemplateOutlet="recursiveListTmpl; context:{ links: link.children }"></ng-container> </ul> </li> </ng-template>
接著會實作導覽連結的其餘轉譯。 請注意,變數
link
屬於型別NavigationLink
以及該類別建立的所有方法/屬性皆可使用。[routerLink]
已使用,而非正常href
屬性。 這可讓我們連結至應用程式中的特定路由,不需要重新整理整頁。導覽的遞回部分也透過建立另一個來實施
<ul>
若目前link
具有非空白children
陣列。 -
更新
navigation.component.spec.ts
新增支援RouterTestingModule
:code language-diff ... + import { RouterTestingModule } from '@angular/router/testing'; ... beforeEach(async(() => { TestBed.configureTestingModule({ + imports: [ RouterTestingModule ], declarations: [ NavigationComponent ] }) .compileComponents(); })); ...
新增
RouterTestingModule
為必要,因為元件使用[routerLink]
. -
更新
navigation.component.scss
將一些基本樣式新增至NavigationComponent
:
@import "~src/styles/variables";
$link-color: $black;
$link-hover-color: $white;
$link-background: $black;
:host-context {
display: block;
width: 100%;
}
.navigation__item {
list-style: none;
}
.navigation__item-link {
color: $link-color;
font-size: $font-size-large;
text-transform: uppercase;
padding: $gutter-padding;
display: flex;
border-bottom: 1px solid $gray;
&:hover {
background: $link-background;
color: $link-hover-color;
}
}
更新標題元件
現在 NavigationComponent
已實施, HeaderComponent
必須更新以參考它。
-
開啟終端機並導覽至
ui.frontend
SPA檔案夾。 開始 webpack開發伺服器:code language-shell $ npm start
-
開啟瀏覽器標籤並導覽至 http://localhost:4200/.
此 webpack開發伺服器 應設定為從AEM的本機執行個體代理JSON模型(
ui.frontend/proxy.conf.json
)。 這可讓我們直接針對教學課程中先前在AEM中建立的內容進行編碼。此
HeaderComponent
目前已經實作功能表切換功能。 接著,新增導覽元件。 -
返回您選擇的IDE並開啟檔案
header.component.ts
在ui.frontend/src/app/components/header/header.component.ts
. -
更新
setHomePage()
移除硬式編碼字串並使用由AEM元件傳入的動態屬性的方法:code language-js /* header.component.ts */ import { NavigationLink } from '../navigation/navigation-link'; ... setHomePage() { if (this.hasNavigation) { const rootNavigationLink: NavigationLink = new NavigationLink(this.items[0]); this.isHome = rootNavigationLink.path === this.route.snapshot.data.path; this.homePageUrl = rootNavigationLink.url; } } ...
的新執行個體
NavigationLink
建立依據items[0]
,是從AEM傳入的導覽JSON模型的根目錄。this.route.snapshot.data.path
傳回目前Angular路由的路徑。 此值用於決定目前的路由是否為 首頁.this.homePageUrl
用於填入 標誌. -
開啟
header.component.html
和將導覽的靜態預留位置取代為新建立的參照NavigationComponent
:code language-diff <div class="header-navigation"> <div class="navigation"> - Navigation Placeholder + <app-navigation [items]="items"></app-navigation> </div> </div>
[items]=items
屬性傳遞@Input() items
從HeaderComponent
至NavigationComponent
建置導覽的位置。 -
開啟
header.component.spec.ts
並為新增宣告NavigationComponent
:code language-diff /* header.component.spect.ts */ + import { NavigationComponent } from '../navigation/navigation.component'; describe('HeaderComponent', () => { let component: HeaderComponent; let fixture: ComponentFixture<HeaderComponent>; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ RouterTestingModule ], + declarations: [ HeaderComponent, NavigationComponent ] }) .compileComponents(); }));
由於
NavigationComponent
現已用作HeaderComponent
它需要宣告為測試平台的一部分。 -
儲存任何開啟檔案的變更並返回 webpack開發伺服器: http://localhost:4200/
按一下功能表切換即可開啟導覽,且您應會看到已填入的導覽連結。 您應該能夠導覽至SPA的不同檢視。
瞭解SPA路由
現在導覽已實施,請在AEM中檢查路由。
-
在IDE中開啟檔案
app-routing.module.ts
在ui.frontend/src/app
.code language-js /* app-routing.module.ts */ import { AemPageDataResolver, AemPageRouteReuseStrategy } from '@adobe/cq-angular-editable-components'; import { NgModule } from '@angular/core'; import { RouteReuseStrategy, RouterModule, Routes, UrlMatchResult, UrlSegment } from '@angular/router'; import { PageComponent } from './components/page/page.component'; export function AemPageMatcher(url: UrlSegment[]): UrlMatchResult { if (url.length) { return { consumed: url, posParams: { path: url[url.length - 1] } }; } } const routes: Routes = [ { matcher: AemPageMatcher, component: PageComponent, resolve: { path: AemPageDataResolver } } ]; @NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule], providers: [ AemPageDataResolver, { provide: RouteReuseStrategy, useClass: AemPageRouteReuseStrategy } ] }) export class AppRoutingModule {}
此
routes: Routes = [];
array會定義Angular元件對應的路徑或導覽路徑。AemPageMatcher
是自訂Angular路由器 UrlMatcher,會比對AEM中「看起來」屬於此Angular應用程式一部分的頁面的任何內容。PageComponent
是Angular元件,代表AEM中的頁面,用來呈現相符的路由。 此PageComponent
將在教學課程稍後複習。AemPageDataResolver
由AEM SPA編輯器JS SDK提供,為自訂 angular路由器解析程式 用來將路由URL (AEM中的路徑,包含.html副檔名)轉換為AEM中的資源路徑(頁面路徑減去副檔名)。例如,
AemPageDataResolver
轉換路由的URLcontent/wknd-spa-angular/us/en/home.html
到以下路徑:/content/wknd-spa-angular/us/en/home
. 用於根據JSON模型API中的路徑解析頁面內容。AemPageRouteReuseStrategy
由AEM SPA編輯器JS SDK提供,為自訂 RouteReuseStrategy 防止重複使用PageComponent
跨越路由。 否則,頁面「A」中的內容可能會在導覽至頁面「B」時顯示。 -
開啟檔案
page.component.ts
在ui.frontend/src/app/components/page/
.code language-js ... export class PageComponent { items; itemsOrder; path; constructor( private route: ActivatedRoute, private modelManagerService: ModelManagerService ) { this.modelManagerService .getData({ path: this.route.snapshot.data.path }) .then(data => { this.path = data[Constants.PATH_PROP]; this.items = data[Constants.ITEMS_PROP]; this.itemsOrder = data[Constants.ITEMS_ORDER_PROP]; }); } }
此
PageComponent
處理從AEM擷取的JSON時需要,並作為呈現路由的Angular元件使用。ActivatedRoute
由Angular路由器模組提供,包含指出哪個AEM頁面的JSON內容應載入此Angular頁面元件例項中的狀態。ModelManagerService
,會根據路由取得JSON資料,並將資料對應至類別變數path
,items
,itemsOrder
. 這些資料隨後將傳遞至 AEMPageComponent -
開啟檔案
page.component.html
在ui.frontend/src/app/components/page/
code language-html <aem-page class="structure-page" [attr.data-cq-page-path]="path" [cqPath]="path" [cqItems]="items" [cqItemsOrder]="itemsOrder"> </aem-page>
aem-page
包含 AEMPageComponent. 變數path
,items
、和itemsOrder
傳遞至AEMPageComponent
. 此AemPageComponent
,透過SPA編輯器JavaScript SDK提供,接著會反複處理此資料,並根據JSON資料動態例項化Angular元件,如以下所示: 對應元件教學課程.此
PageComponent
其實只是AEMPageComponent
而且它是AEMPageComponent
如此一來,大部分繁重的工作都會正確地將JSON模型對應至Angular元件。
Inspect AEM中的SPA路由
-
開啟終端機並停止 webpack開發伺服器 若已啟動。 導覽至專案的根目錄,並使用您的Maven技能將專案部署到AEM:
code language-shell $ cd aem-guides-wknd-spa $ mvn clean install -PautoInstallSinglePackage
note caution CAUTION angular專案已啟用一些非常嚴格的Linting規則。 如果Maven組建失敗,請檢查錯誤並尋找 在列出的檔案中找到Lint錯誤。。修正篩選器發現的任何問題,並重新執行Maven命令。 -
導覽至AEM中的SPA首頁: http://localhost:4502/content/wknd-spa-angular/us/en/home.html 並開啟瀏覽器的開發人員工具。 熒幕擷取畫面如下,擷取自Google Chrome瀏覽器。
重新整理頁面,您應該會看到XHR要求
/content/wknd-spa-angular/us/en.model.json
,即SPA根目錄。 請注意,根據教學課程中先前進行的階層深度設定,SPA根範本僅包含三個子頁面。 這不包括 第3頁. -
在開發人員工具開啟的狀態下,導覽至 第3頁:
請注意,已提出新的XHR要求給:
/content/wknd-spa-angular/us/en/home/page-2/page-3.model.json
AEM模型管理員瞭解 第3頁 JSON內容無法使用,並自動觸發其他XHR請求。
-
繼續使用各種導覽連結來導覽SPA。 請注意,不會提出其他XHR要求,也不會發生完整頁面重新整理。 這可讓一般使用者快速使用SPA,並減少傳回AEM的不必要請求。
-
透過直接導覽至以下位置,嘗試使用深層連結: http://localhost:4502/content/wknd-spa-angular/us/en/home/page-2.html. 請注意,瀏覽器的返回按鈕仍會繼續運作。
恭喜! congratulations
恭喜,您已瞭解如何使用SPA編輯器SDK將對應至AEM頁面,以支援SPA中的多個檢視。 動態導覽已使用Angular路由實作,並新增至 Header
元件。
您一律可以於檢視完成的程式碼 GitHub 或切換至分支以在本機取出程式碼 Angular/navigation-routing-solution
.
後續步驟 next-steps
建立自訂元件 — 瞭解如何建立要與AEM SPA編輯器搭配使用的自訂元件。 瞭解如何開發作者對話方塊和Sling模型,以擴充JSON模型來填入自訂元件。