﻿# Angular 2 開發心得分享 (01)：使用 HTML 自訂元素的注意事項

使用 Angular 2 開發網站跟以前 Angular 1 最不一樣的地方在於「網頁元件化」的觀念，網頁中所有看到的東西都將變成一個個的 directive 元件，當你將現有網頁 HTML 版型變成 Angular 2 的 Directive 元件後，Angular 2 執行時會把這些 HTML 片段編譯過，並且找出這些 HTML 片段中是否含有其他的 Directive 元件，如果有找到，就會初始化這個 Directive 元件。不過當你的 HTML 版型中有用到「自訂的 HTML 元素」或「寫錯的 HTML 元素」，執行時期就會立刻發生錯誤，這問題發生的原因與解決方法，將是本文討論的重點。

Angular 2 在編譯與剖析 HTML 片段時，必須認真看待每一個 HTML 元素 (HTML Element)，只要是 [HTML5 規格](https://www.w3.org/TR/html5/)中定義的 HTML 元素，就會被視為 HTML 範本的一部分，而那些不是 HTML5 規格定義的元素，預設就會被視為一個 Directive 元件，並對此進一步解析成元件。

這裡我們直接透過 [Angular CLI](https://cli.angular.io/) 快速建立一個 Angular 2 專案，並用 [Visual Studio Code](https://code.visualstudio.com/) 開啟該專案：

> ng new demo0920
> 
> cd demo0920
> 
> code .
> 
> ng serve

我們直接修改 AppComponent 的 Template 頁面 ( src/app/app.component.html ) 並加上一個 **[hgroup](http://html5doctor.com/the-hgroup-element/)** 元素，如下圖示：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/2a831a06f463d96cbff6-image_2933.png)

接著我們來看看網頁執行的情況，你會發現我們加上 **<hgroup>** 元素後，網頁就跑不出來了！

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/6881490b639b2efd3a81-image_2934.png)

我們按下 F12 開發者工具，切換到 Console 頁籤，此時你會看到完整的錯誤訊息：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/eb625e22230eef80455d-image_2935.png)

我來摘要一下重要的錯誤訊息片段如下：

> zone.js:344Unhandled Promise rejection: Template parse errors:  
> 'hgroup' is not a known element:  
> 1\. If 'hgroup' is an Angular component, then verify that it is part of this module.  
> 2\. If 'hgroup' is a Web Component then add "CUSTOM\_ELEMENTS\_SCHEMA" to the '@NgModule.schema' of this component to suppress this message.

上次錯誤訊息寫得非常清楚，他說：「**'hgroup' 並不是一個已知的元素**」！

> **※ 補充說明 ※**
> 
> 早期在 HTML5 規格尚未發佈正式版的時候，當時是有 hgroup 元素的，但 HTML5 工作小組在 2013/4/2 的一次[會議結論](http://lists.w3.org/Archives/Public/public-html-admin/2013Apr/0003.html)中決定從規格中移除 hgroup 元素，因此請大家不要再用這個 hgroup 元素。

由此可知，Angular 2 在解析網頁版型時，任何自訂的 HTML 標籤，預設都必須為合法的 Directive 元件才行，以我們這個例子來說，最簡單的作法當然就是不要用 hgroup 元素，你可以改寫成 <div> 或乾脆直接移除 (前提是版型不會跑掉才能直接移除)，這也是建議的做法。

但如果你真的有特殊理由，必須保留這些自訂元素在 Template 裡面的話，你還是可以參考以下設定，讓自訂的 HTML 標籤可以繼續存在。

我們從 Angular 2 官方的 [NgModule API 文件](https://angular.io/docs/ts/latest/api/core/index/NgModule-interface.html)中可以發現，在 AppModule 設定 @NgModule 時，可以加上一個 **schemas** 屬性，而這個屬性可用的屬性值只有兩種：

-   **NO\_ERRORS\_SCHEMA**  
    任何自訂的元素與屬性都不會提示任何錯誤訊息。
-   **CUSTOM\_ELEMENTS\_SCHEMA**  
    任何自訂的元素名稱中有出現減號 ( \- ) 的都是允許的元素 (例如: my-hgroup )

如果我們以本文稍早提到的這個 hgroup 元素為例，這個元素名稱並沒有出現減號，因此你必須設定 **NO\_ERRORS\_SCHEMA** 才能正常執行，設定好的畫面如下圖示：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/9b9767d2d591ac03df6b-image_2936.png)

設定完成之後，網頁就可以正常執行了：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/411ba95bb0679417f2dd-image_2937.png)

如果想使用 [Web Components](http://webcomponents.org/) 技術在 Angular 2 應用程式中，依據 W3C 的 [Custom Elements](https://www.w3.org/TR/custom-elements/) 規格，你在命名自訂元素時，都應該至少加上一個 dash 符號 (就是減號) ( - )。

參考文章: [How should I name my element? — WebComponents.org](http://webcomponents.org/articles/how-should-i-name-my-element/)

本篇文章當然不會特別提及 Web Components 的開發，我們就稍微修改一下原本的 **hgroup** 標籤，改用 **my-hgroup** 當成自訂的元素名稱，如下圖示：

![image](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/7dc4c0c8cbb7e25f9930-image_2938.png "image")

此時你就可以在 @NgModule.schema 設定 **CUSTOM\_ELEMENTS\_SCHEMA** 即可允許所有自訂元素的標籤 (但名稱必須至少有一個 dash 符號)，設定好的畫面如下圖示：

![](https://stwillblogassets.blob.core.windows.net/files/images/external/stwillblogassets.blob.core.windows.net/70505d07fd2929f44f92-image_2939.png)

設定好之後，Angular 2 在執行的過程中就不會再出現任何錯誤訊息了！

**相關連結**

-   [NgModule - ts](https://angular.io/docs/ts/latest/api/core/index/NgModule-interface.html)
-   [bug(DomSchema): \`<time>\`, \`<summary>\` els aren't allowed · Issue #11219 · angular/angular](https://github.com/angular/angular/issues/11219)
-   [angular2 - Angular 2 RC6 - "sidebar" is not a known element - Stack Overflow](http://stackoverflow.com/questions/39288714/angular-2-rc6-sidebar-is-not-a-known-element)
-   [angular2 - How to use a custom directive in Angular 2 RC6? - Stack Overflow](http://stackoverflow.com/questions/39550672/how-to-use-a-custom-directive-in-angular-2-rc6/39550703#39550703)
-   [How should I name my element? — WebComponents.org](http://webcomponents.org/articles/how-should-i-name-my-element/)
