Angular Material 17+ 高級教程 – Material Icon

前言

不熟悉 Icon 的可以先看這篇 CSS – Icon

Material Design 有自己的一套 Icon 設計。Angular Material 默認就使用這一套。

不過呢,目前 v17.2.0 用的是舊版本的的設計 -- Material Icons

Material Design 3 推出了新版本的設計 -- Material Symbols

本篇會教如果使用舊的 Material Icons,新的 Material Symbols,還有 SVG 版 Icon。

 

Angular Material for Material Icons (舊設計)

要在 Angular 項目中使用 Material Icons 並不一定要搭配 Angular Material。

Without Angular Material 

首先在 index.html 添加 link 

<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">

App Template

<span class="material-icons">home</span>

App Styles

.material-icons {
  font-size: 56px;
  color: red;
}

效果

Material Icons 是用 font-family 實現的,所以它只需要 CSS Styles 就可以 working 了。

不僅僅是 Material Icons,其它 Icon (e.g. fontawesomeFlaticonionicons) 用法也大同小異。

Do we really need Angualr Material Icon?

Material Icons, fontawesome, Flaticon, ionicons 它們的使用方式已經很簡單了,我們還有必要引入 Angualr Material Icon 嗎?

不一定,有 3 種情況下使用 Angualr Material Icon 會比較好:

  1. multiple icon library

    一個 icon library 未必可以滿足整個項目的需求,比如說我們選了 Material Icons 作爲 primary,但有些 icon 或許它沒用。

    這時就需要額外加一些 SVG icon 彌補上。

    一旦出現 2 個 library,代碼風格管理可能就不統一了,比如 Material Icons 用 <span>,fontawesome 用 <i>,有些又是 <svg>。

    這時用 Angualr Material 組件 <mat-icon> 就可以把它們統一起來。

  2. multiple projects

    不同項目 icon 風格可能會不一樣。哪怕它們都使用 Material Symbols 也可能分 Outlined, Rounded, Sharp 不同變種。

    這種情況使用 Angualr Material 組件 <mat-icon> 也可以達到統一管理的目的。

  3. 順風水

    人家 wrap 一層給你一定是有原因的嘛。

With Angular Material Icon

App 組件 import MatIconModule

import { ChangeDetectionStrategy, Component } from '@angular/core';
import { MatIconModule } from '@angular/material/icon';

@Component({
  selector: 'app-root',
  standalone: true,
  // 1. import MatIconModule
  imports: [MatIconModule],
  templateUrl: './app.component.html',
  styleUrl: './app.component.scss',
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AppComponent {}

App Template

<mat-icon aria-hidden="false" aria-label="home icon" fontIcon="home"></mat-icon>

如果項目不需要支持 screen reader 可以去掉所有 “aria-” attribute。

下面 2 種寫法幾乎是等價的,唯一的區別好像是 search engine 會以爲第一種是 text。

<mat-icon>home</mat-icon>
<mat-icon fontIcon="home" />

Angular Material 推薦我們使用第二種,但是 Google Fonts Icon 給的例子卻是第一種,我也不懂要聽誰的😕。

CSS Styles

mat-icon {
  color: red;
  font-size: 56px;
  width: 56px;
  height: 56px;
}

修改 size 比較麻煩,需要改 3 個屬性 font-size, width, height。

這是因爲 <mat-icon> 是 inline-block 

inline-block 的作用是在 font-family 加載完成前先卡着位置,這樣佈局就不會跳一下。

rendered html

<mat-icon _ngcontent-ng-c2283532919="" role="img" class="mat-icon notranslate material-icons mat-ligature-font mat-icon-no-color" aria-hidden="true" data-mat-icon-type="font">home</mat-icon>

<mat-icon> 組件其實也沒用替我們做多少事兒,主要就是添加了 class "material-icons"。其它的像 role, aria-hidden 這些是 for accessibility。

 

Angular Material for Material Symbols (新設計)

Material Symbols 是 Material Design 3 對 Icon 的新設計。

官網依舊是 https://fonts.google.com/icons

它有一個 select options 可以切換回去舊設計。

舊設計有 5 個變種,新設計只有 3 個變種

我選 Rounded 作爲例子。

Setup Angular Material

index.html 添加 link (去官網拿)

<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@24,400,0,0" />

Material Symbols 的使用代碼長這樣

<span class="material-symbols-rounded">home</span>

它和 Material Icons 的區別是 class 不一樣。

這時我們需要設置 Angular Material Icon 讓它知道我們要換掉原本的 class。

App 組件

export class AppComponent {
  constructor() {
    const iconRegistry = inject(MatIconRegistry);
    iconRegistry.setDefaultFontSetClass('material-symbols-rounded');
  }
}

MatIconRegistry 是一個 Root Level Provider。通過它我們可以對全局的 <mat-icon> 進行設置。

App Template

<mat-icon>home</mat-icon>

這裏和 Material Icons 是一模一樣的,我們只需要設置 MatIconRegistry 就可以了,它會負責修改所有 <mat-icon> 的邏輯。

效果

注意:如果使用 fontIcon attribute 的話

<mat-icon fontIcon="home" />

MatIconRegistry 還需要添加多一個 class "mat-ligature-font" 纔行。

const iconRegistry = inject(MatIconRegistry);
iconRegistry.setDefaultFontSetClass('material-symbols-rounded', 'mat-ligature-font');

相關文檔 Docs – MatIcon

class "mat-ligature-font" 是輸出僞元素的 selector,所以使用 fontIcon 的寫法就必須搭配它。

Use both Material Symbols and Material Icons

如果我們想同時使用 2 種不同的 Icon Library 也是可以的。

export class AppComponent {
  constructor() {
    const iconRegistry = inject(MatIconRegistry);
    iconRegistry.registerFontClassAlias('symbols', 'material-symbols-rounded mat-ligature-font');
  }
}

上一 part 我們用 MatIconRegistry.setDefaultFontSetClass 替換了默認的設置,

這裏我們改用 registerFontClassAlias 添加一個設置。

使用方式

<!-- 用默認的 Material Icons -->
<mat-icon fontIcon="home" />

<!-- 用新的 Material Symbols -->
<mat-icon fontIcon="home" fontSet="symbols" />

通過 fontset="name" 來表明用哪一個,這個 name 就是上面 registerFontClassAlias 的註冊的名字。

效果

 

題外話:Self Hosting for Material Symbols

這個和 Angular Material Icon 無關,我只是隨便想替一下而已。

上面 index.html 引入的 link,它的具體內容是

我們下載它的 .woff2 放到自己的 server,然後把 style 代碼寫進 style.scss 就可以了。

 

Angular Material SVG Icon

假設有 2 個 SVG icons 

我們想使用 <mat-icon> 來統一輸出它們。

addSvgIcon

App Template

<mat-icon svgIcon="home" />
<mat-icon svgIcon="toggle_on" />

把原本的 fontIcon attribute 改成 svgIcon attribute。

然後用 MatIconRegistry 做設置

export class AppComponent {
  constructor() {
    const iconRegistry = inject(MatIconRegistry);
    const domSanitizer = inject(DomSanitizer);
    // 1. 需要 bypass 消毒
    const homeSvgUrl = domSanitizer.bypassSecurityTrustResourceUrl('/assets/icons/home.svg');
    const toggleOnSvgUrl = domSanitizer.bypassSecurityTrustResourceUrl('/assets/icons/toggle_on.svg');

    // 2. 一個一個添加
    iconRegistry.addSvgIcon('home', homeSvgUrl);
    iconRegistry.addSvgIcon('toggle_on', toggleOnSvgUrl);
  }
}

不熟悉消毒的朋友可以看這篇:DomSanitizer

效果

它會用 HttpClient 發請求去下載 SVG file。

提醒:我們需要 provide HttpClient 哦。

addSvgIconInNamespace

SVG file 通常會來自四面八方不同的設計,多了就很亂,Angular Material Icon 提供了一個 namespace 概念幫助管理。

iconRegistry.addSvgIconInNamespace('my-design', 'home', homeSvgUrl);
iconRegistry.addSvgIconInNamespace('my-design', 'toggle_on', toggleOnSvgUrl);

把 addSvgIcon 換成 addSvgIconInNamespace,並且提供一個 namespace 名字

使用方式

<mat-icon svgIcon="my-design:home" />
<mat-icon svgIcon="my-design:toggle_on" />

多了一個 "my-design:" prefix。

addSvgIconLiteral or addSvgIconLiteralInNamespace

除了可以提供 SVG URL,我們也可以直接提供 SVG element。(提醒:一樣要消毒)

const homeSvgHtml = domSanitizer.bypassSecurityTrustHtml(`
    <svg fill="currentColor" xmlns="http://www.w3.org/2000/svg" viewBox="0 -960 960 960"><path d="M240-200h120v-240h240v240h120v-360L480-740 240-560v360Zm-80 80v-480l320-240 320 240v480H520v-240h-80v240H160Zm320-350Z"/></svg>
`);
iconRegistry.addSvgIconLiteralInNamespace('my-design', 'home', homeSvgHtml);

這樣它就不需要 HttpClient 發請求去下載了。

addSvgIconSet or addSvgIconSetInNamespace

SVG 有一個概念叫 sprite,就是把多個 SVG combine 在一個 SVG file 裏,這樣就一次性下載多個 SVG。

我們把 home.svg 和 toggle_on.svg combine 成 1 個 sprite.symbol.svg。

使用 addSvgIconSet or addSvgIconSetInNamespace 做設置

const svgSpriteUrl = domSanitizer.bypassSecurityTrustResourceUrl(
  '/assets/icons/svg-icon-sprite/symbol/svg/sprite.symbol.svg',
);
iconRegistry.addSvgIconSetInNamespace('my-design', svgSpriteUrl);

效果

2 個 SVG icons 只需要下載一個 sprite svg file。

提醒:Angular Material Icon 並不知道 sprite svg file 的具體內容,它會一次性下載所有 sprite svg file。

 

總結

本篇介紹瞭如何使用 Angular Material Icon 做 Icon 統一管理。

不管是不同的 Icon Library (Material 新 vs Material 舊)

或者不同輸出方式 (font-family vs SVG)

Angular Material Icon 都統一了 <mat-icon> 的接口。大部分情況我們只需要對全局的 MatIconRegistry 做設置管理就可以了。 

  

目錄

上一篇 Angular Material 17+ 高級教程 – CDK Scrolling

下一篇 TODO

想查看目錄,請移步 Angular 17+ 高級教程 – 目錄

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章