[Angular] Meta 與 Route

Angular 4 版內建了 Meta 的服務,雖然目前還是標示 EXPERIMENTAL (表示未來有可能會有 break change),但還是先來玩看看,希望能和路由設定檔綁在一起。

路由設定

我們知道 Route 的設定檔裡面可以設定 data 或是透過 resolve 來預先處理非同步的資料取得行為 ( 例如 call API),可參閱這篇文章

假設,我們的路由設定檔,長這樣

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
path: '',
component: HomeComponent,
data: {
meta: [
{name: 'twitter:title', content: 'Good Morning Harwood'},
{property: 'og:title', content: 'Good Morning Harwood'}
]
}
},
{
path: 'about',
component: AboutComponent,
data: {
meta: [
{name: 'twitter:title', content: 'About Us'},
{property: 'og:title', content: 'About Us'}
]
}
}

以上的設定,就可以透過 RouterActivatedRoute的方式取得 data 下 meta 的資訊。而這些資訊就是要設定到 <head></head> 間的訊息

Meta

1
2
3
4
5
6
7
8
9
10
11

class Meta {
constructor(_doc: any)
addTag(tag: MetaDefinition, forceCreation?: boolean) : HTMLMetaElement
addTags(tags: MetaDefinition[], forceCreation?: boolean) : HTMLMetaElement[]
getTag(attrSelector: string) : HTMLMetaElement
getTags(attrSelector: string) : HTMLMetaElement[]
updateTag(tag: MetaDefinition, selector?: string) : HTMLMetaElement
removeTag(attrSelector: string) : void
removeTagElement(meta: HTMLMetaElement) : void
}

雖然有這麼多功能,比較需要提的是 updateTag 這一個包含新增的功能,他會判斷如果你想要更新的MetaDefiniton 不存在時,就會幫你建立一個。

實作範例

路由設定檔關於 meta 資料這邊就先略過,可以參照上面的路由設定範例,這段範例的主要目的,要讓 Angular 可以自動更新 Meta 資訊,我先把程式貼出來,在一行行的解釋

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
constructor(
private metaService: Meta,
private router: Router,
private activatedRoute: ActivatedRoute) {
this.router.events.filter(event => (event instanceof NavigationEnd))
.switchMap(() => {
const snapshot = this.activatedRoute.snapshot;
let child = snapshot.firstChild;
while (child.firstChild !== null) {
child = child.firstChild;
}
return Observable.from(child.data.meta);
})
.do((meta: any) => this.metaService.updateTag(meta))
.subscribe();
};

為了避免重複執行,這段程式碼我是放在 app.component.ts的地方,因為正常情況下,app.component 只會被執行一次。

1
this.router.events.filter(event => (event instanceof NavigationEnd))

這我之前有寫過,就參讀這篇文章

1
2
3
4
5
6
7
8
.switchMap(() => {
const snapshot = this.activatedRoute.snapshot;
let child = snapshot.firstChild;
while (child.firstChild !== null) {
child = child.firstChild;
}
return Observable.from(child.data.meta);
})

為什麼要取snapShot,因為 snapShot 是當時的值,透過這種方式取得的資料就不是 Observable 型別了。透過 while 的方式取得 data 區段的資料。然後將 meta 的陣列轉換成 stream 的模式

1
.do((meta: any) => this.metaService.updateTag(meta))

依前一個 Operator 傳回的結果,一個一個的更新 meta 資訊

1
.subscribe();

執行上面的工作。

執行結果