iT邦幫忙

2025 iThome 鐵人賽

DAY 24
0
Vue.js

作為 Angular 專家探索 Vue 3 和 Svelte 5系列 第 24

第23天 - Alert 組件第二部分 - 動態渲染 (Dynamic Rendering) SVG 圖示

  • 分享至 

  • xImage
  •  

在第23天,我動態渲染警示(alert)的 SVG 圖示,因為動態渲染更易於維護(maintainable)、擴展(scalable)及高效(efficient)。

我為 SVG 圖示建立組件(components),並根據警示類型(alert type)渲染相應的圖示組件。

Framework Method
Vue 3 :is attribute with the
Svelte 5 Dynamic component is capitalized
Angular ngComponentOutlet structural directive enables dynamic rendering of the components

建立圖示組件(Icon Components)

Vue 3 application

在 src 目錄下建立一個名為 icons 的資料夾(directory)。

  • InfoIcon.vue
<template>
    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="h-6 w-6 shrink-0 stroke-current">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
    </svg>
</template>  
  • SuccessIcon.vue
<template>
  <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
  </svg>
</template>
  • WarningIcon.vue
<template>
    <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
      <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
    </svg>
</template> 
  • ErrorIcon.vue
<template>
    <svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
        <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
    </svg>
</template>  

.vue 檔案中,SVG 圖示定義在 <template> 元素中。


SvelteKit application

Create an icons directory under src/lib

  • info-icon.svelte
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="h-6 w-6 shrink-0 stroke-current">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg> 
  • success-icon.svelte
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
  • warning-icon.svelte
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
  • error-icon.svelte
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
    <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>

在 .svelte 檔案中,SVG 圖示是在檔案內定義的。


Angular 20 application

在 src/app 目錄下建立一個名為 icons 的資料夾(directory)。

  • icon.component.ts
@Component({
  selector: 'app-info-icon',
  template: `
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" class="h-6 w-6 shrink-0 stroke-current">
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 16h-1v-4h-1m1-4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"></path>
</svg>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class InfoIconComponent {}
@Component({
  selector: 'app-success-icon',
  template: `
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class SuccessIconComponent {}
@Component({
  selector: 'app-warning-icon',
  template: `
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z" />
</svg>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class WarningIconComponent {}
@Component({
  selector: 'app-error-icon',
  template: `
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 shrink-0 stroke-current" fill="none" viewBox="0 0 24 24">
  <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
</svg>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ErrorIconComponent {}

在組件(component)類別中,由於代碼簡短,SVG 圖示定義為內嵌模板(inline template)。可以在 @Component 裝飾器(decorator)的 template 屬性中看到。


在 Alert 組件(component)中動態渲染圖示。

目標是從 Alert 組件(component)中刪除條件分支。

Vue 3 application

<script setup lang="ts"> 
    import InfoIcon from '@/icons/InfoIcon.vue'
    import SuccessIcon from '@/icons/SuccessIcon.vue'
    import WarningIcon from '@/icons/WarningIcon.vue'
    import ErrorIcon from '@/icons/ErrorIcon.vue'

    const icon = computed(() => ({
        info: InfoIcon,
        warning: WarningIcon,
        error: ErrorIcon,
        success: SuccessIcon,
    }[type]))
</script>

將 Icon 組件匯入到 Alert 組件(component)。然後,定義一個 icon 計算引用(computed ref),用以推導要顯示的圖示。字典根據 type 屬性(prop)來索引,以決定適當的圖示。

<component :is="icon" />    

v-ifv-if-else 被一行代碼所取代。


SvelteKit application

<script lang="ts">
    import InfoIcon from '$lib/icons/info-icon.svelte'
    import ErrorIcon from '$lib/icons/error-icon.svelte'
    import SuccessIcon from '$lib/icons/success-icon.svelte'
    import WarningIcon from '$lib/icons/warning-icon.svelte'
    
    const Icon = $derived.by(() => ({
        info: InfoIcon,
        success: SuccessIcon,
        warning: WarningIcon,
        error: ErrorIcon,
    }[alert.type]));
</script>

將 Icon 組件匯入到 Alert 組件(component)。然後,定義一個顯示圖示的 Icon 衍生 rune(derived rune)。字典根據 alert.type 屬性(prop)來索引,以決定適當的圖示。

   <Icon ></Icon> 

if-else-if 控制流程被 <Icon></Icon> 所取代。動態組件的名稱必須大寫,因此,衍生出的 rune 是 Icon


Angular 20 application

import { NgComponentOutlet } from '@angular/common';
import { ErrorIconComponent, InfoIconComponent, SuccessIconComponent, WarningIconComponent } from '../icons/icon.component';

@Component({
  selector: 'app-alert',
  imports: [NgComponentOutlet],
  template: `
    <ng-container [ngComponentOutlet]="icon()" />
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertComponent {
  icon = computed(() => {
    return {
        info: InfoIconComponent,
        warning: WarningIconComponent,
        error: ErrorIconComponent,
        success: SuccessIconComponent,
    }[this.type()];
  });
}

The if-else-if control flow is replaced with <ng-container [ngComponentOutlet]="icon()" />.

將 Icon 組件匯入到 AlertComponenet。然後,定義一個 icon 計算信號(computed signal),用以推導要顯示的圖示。字典根據 type 輸入信號 (input signal) 來索引,以決定適當的圖示。

if-else-if 控制流程被 <ng-container [ngComponentOutlet]="icon()" /> 所取代。


模板已整理為一行代碼來渲染動態組件。
我們已成功在 Vue、Svelte 和 Angular 框架中渲染警示中的圖標組件。

Github Repositories

Github Pages

資源


上一篇
第22天 - Alert Component 第一部份 - Alert List 和 Alert Components
下一篇
第 24 天 - Alert 元件 第 3 部分 - 新增用於更換樣式的 Alert Bar
系列文
作為 Angular 專家探索 Vue 3 和 Svelte 525
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言