iT邦幫忙

2025 iThome 鐵人賽

DAY 25
1
Vue.js

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

第 24 天 - Alert 元件 第 3 部分 - 新增用於更換樣式的 Alert Bar

  • 分享至 

  • xImage
  •  

第 24 天 - Alert 元件 第 3 部分 - 新增用於更換樣式的 Alert Bar

在第 24 天,我建立了一個 Alert Bar 元件,用來顯示或隱藏關閉按鈕,套用新的樣式到警示訊息並改變它們的方向。

建立一個關閉的 SVG Icon

Vue 3 application

建立一個 icons/CloseIcon.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 fill-rule="evenodd" clip-rule="evenodd" d="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" fill="#0F1729"></path>
    </svg>
</template>

Alert 元件中顯示關閉圖示。

<script setup lang="ts">
import CloseIcon from '@/icons/CloseIcon.vue'
</script>
<template>
... omitted to save space ...
<div>
    <button class="btn btn-sm btn-primary" title="Close button" @click="closeAlert">
        <CloseIcon />
    </button>
</div>
</template>

匯入 CloseIcon 並在 <template> 標籤內加入 <CloseIcon>,以在按鈕上呈現關閉圖示。


SvelteKit application

建立一個 lib/icons/close-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 fill-rule="evenodd" clip-rule="evenodd" d="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" fill="#0F1729"></path>
</svg>

Alert 元件中顯示關閉圖示。

<script lang="ts">
    import CloseIcon from '$lib/icons/close-icon.svelte'
</script>
... omitted to save space ... 
<div>
    <button class="btn btn-sm btn-primary" title="Close button" onclick={closeAlert}>
        <CloseIcon />
    </button>
</div>

匯入 CloseIcon,並在 HTML 模板中加入 <CloseIcon />,以在按鈕上呈現關閉圖示。


Angular 20 application

建立一個 CloseIconComponent

@Component({
  selector: 'app-close-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 fill-rule="evenodd" clip-rule="evenodd" d="M5.29289 5.29289C5.68342 4.90237 6.31658 4.90237 6.70711 5.29289L12 10.5858L17.2929 5.29289C17.6834 4.90237 18.3166 4.90237 18.7071 5.29289C19.0976 5.68342 19.0976 6.31658 18.7071 6.70711L13.4142 12L18.7071 17.2929C19.0976 17.6834 19.0976 18.3166 18.7071 18.7071C18.3166 19.0976 17.6834 19.0976 17.2929 18.7071L12 13.4142L6.70711 18.7071C6.31658 19.0976 5.68342 19.0976 5.29289 18.7071C4.90237 18.3166 4.90237 17.6834 5.29289 17.2929L10.5858 12L5.29289 6.70711C4.90237 6.31658 4.90237 5.68342 5.29289 5.29289Z" fill="#0F1729"></path>
</svg>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class CloseIconComponent {}

Alert 元件中顯示關閉圖示。

import { NgComponentOutlet } from '@angular/common';
import { ChangeDetectionStrategy, Component, computed, input, output, signal } from '@angular/core';
import { AlertType } from '../alert.type';
import { CloseIconComponent, ErrorIconComponent, InfoIconComponent, SuccessIconComponent, WarningIconComponent } from '../icons/icon.component';

@Component({
  selector: 'app-alert',
  imports: [NgComponentOutlet, CloseIconComponent],
  template: `
     ... omitted to save space ...
    <div>
      <button class="btn btn-sm btn-primary" title="Close button" (click)="closeAlert()">
        <app-close-icon />
      </button>
    </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertComponent {}

匯入 CloseIconComponent 並註冊到 @Component 裝飾器的 imports 陣列中。

在內嵌模板中,使用 app-close-icon 選擇器在按鈕上呈現關閉圖示。


AlertBar 元件中新增顯示/隱藏勾選框

Vue 3 application

自 Vue 3.4 起,推薦的雙向資料綁定方式是使用 defineModel 巨集 (macro)。

<script setup lang="ts">
const hasCloseButton = defineModel<boolean>('hasCloseButton', { default: true })
</script>

宣告一個 defineModel<boolean>,並將值指派給 hasCloseButton 這個 ref。

{ default: true } 表示該 ref 的預設值為 true,勾選框會被選取,警示元件應顯示關閉按鈕。

<template>
  <div>
    <p class="mb-[0.75rem]">
      <span>Has close button? </span>
      <input type="checkbox" class="mr-[0.5rem]" v-model="hasCloseButton" />
    </p>
  </div>
</template>

在模板中,v-model 指令加入至勾選框輸入元素,並綁定到 hasCloseButton

hasCloseButton 啟用雙向資料綁定,並將值傳遞到父元件,即 AlertList 元件。


SvelteKit application

在 Svelte 5 中,雙向綁定透過 $bindable 函數實現。此外,必須將 $bindable$props 一起使用。我們會更新 Prop 類型,新增 hasCloseButton,並從 $prop() 呼叫中解構該屬性。


 type Props = {
    hasCloseButton: boolean;
}

let { 
    hasCloseButton = $bindable(), 
}: Props = $props();
<div>
    <p class="mb-[0.75rem]">
        <span>Has close button?</span>
        <input type="checkbox" class="mr-[0.5rem]" bind:checked={hasCloseButton} />
    </p>
</div>

在模板中,勾選框的 bind:checked 屬性綁定到 hasCloseButtonhasCloseButton 啟用雙向資料綁定,並將值傳遞到父元件,即 AlertList 元件。


Angular 20 application

在 Angular 中,雙向綁定是透過 model Writable 信號達成。

import { ChangeDetectionStrategy, Component, input, model } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-alert-bar',
  imports: [],
  template: `
  <div>
    <p class="mb-[0.75rem]">
      <span>Has close button? </span>
      <input type="checkbox" class="mr-[0.5rem]" [(ngModel)]="hasCloseButton" />
      </select>
    </p>
  </div>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertBarComponent {
  hasCloseButton = model<boolean>(true);
}

宣告 model<boolean>(true) 並將值指派給 hasCloseButton 信號。model 的預設值為 true,因此勾選框被選取,而 Alert 元件應顯示關閉按鈕。


hasCloseButton 傳遞到 AlertList 元件

Vue 3 application

<script setup lang="ts">
import { computed, ref } from 'vue';

const hasCloseButton = ref(true)
</script>

AlertList 元件中宣告 hasCloseButton ref 以接收來自子元件的值。

Vue 元件允許多重 v-model 綁定。

v-model:hasCloseButton="hasCloseButton" 綁定子元件的 hasCloseButton 屬性到 hasCloseButton ref。

<template>
  <h2>Alert Components (Vue ver.)</h2>

  <AlertBar
    v-model:hasCloseButton="hasCloseButton"
  />
</template>

Vue 元件允許多重 v-model 綁定。

v-model:hasCloseButton="hasCloseButton" 綁定子元件的 hasCloseButton 屬性到 hasCloseButton ref。


SvelteKit application

<script lang="ts">
	import AlertBar from './alert-bar.svelte';

    let hasCloseButton = $state(true);
</script>

AlertList 元件中宣告一個 hasCloseButton 變數以接收來自子元件的值。

<AlertBar {configs} 
    bind:hasCloseButton={hasCloseButton} 
/>

bind:hasCloseButton={hasCloseButton} 表示 AlertList 元件會監聽 AlertBar 元件的 hasCloseButton 屬性變化。


Angular 20 application

import { ChangeDetectionStrategy, Component, computed, input, signal } from '@angular/core';
import { AlertBarComponent } from '../alert-bar/alert-bar.component';

@Component({
  selector: 'app-alert-list',
  imports: [AlertBarComponent],
  template: `
    <app-alert-bar 
      [(hasCloseButton)]="hasCloseButton" 
    />
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertListComponent {
  hasCloseButton = signal<boolean>(true);
}

AlertList 元件中宣告一個 hasCloseButton 信號以接收來自子元件的值。

使用香蕉盒語法 [(expression)] 來對 AlertListAlertBar 元件進行雙向綁定。

在警示元件中顯示/隱藏關閉按鈕


在 Alert 元件中顯示/隱藏關閉按鈕

Vue 3 application

const hasCloseButton = ref(true)

const alertConfig = computed(() => ({
  hasCloseButton: hasCloseButton.value,
}))

定義一個 alertConfig 計算型 ref,來建立一個物件以儲存 hasCloseButton.value 的值。

<template>
    <Alert v-for="{ type, message } in alerts"
        class="mb-[0.75rem]"
        :key="type"
        :type="type"
        :alertConfig="alertConfig">
        {{  message }}
    </Alert>
</template>

alertConfig 傳遞給 Alert 元件的 alertConfig 屬性。

<script setup lang="ts">
    import { computed, ref } from 'vue'
    import CloseIcon from '@/icons/CloseIcon.vue'
    
    type Props = {
        alertConfig: {
            hasCloseButton: boolean
        },
        type: string
    }

    const { type, alertConfig } = defineProps<Props>()

    ... omit the template codes ...
</script>

Alert 元件中,將新的 alertConfig 屬性加入 Prop 類型。 從 defineProps 巨集中解構 alertConfig

<template>
  <div role="alert" :class="alertClasses" v-if="!closed">
    <component :is="icon" />
    <span><slot></slot></span>
    <div v-if="alertConfig.hasCloseButton">
        <button class="btn btn-sm btn-primary" alt="Close button" @click="closeAlert">
            <CloseIcon />
        </button>
    </div>
  </div>
</template>

alertConfig.hasCloseButton 為 true,則會顯示關閉按鈕,否則隱藏。


SvelteKit application

<script lang="ts">
	import AlertBar from './alert-bar.svelte';
    import Alert from './alert.svelte';
	import type { AlertMessage } from './alert.type';

    let hasCloseButton = $state(true);
</script>

AlertList 元件中宣告一個 hasCloseButton 變數以接收來自子元件的值。

{#each alerts as alert (alert.type) } 
    <Alert {alert} {hasCloseButton} />
{/each}

hasCloseButton 變數傳遞給 Alert 元件的 hasCloseButton 屬性。

<AlertBar {configs} 
    bind:hasCloseButton={hasCloseButton} 
/>

bind:hasCloseButton={hasCloseButton} 表示 AlertList 元件會監聽 AlertBar 元件的 hasCloseButton 屬性變化。

<script lang="ts">
    type Props = {
        ... other properties ...
        hasCloseButton: boolean;
    }

    const { 
        hasCloseButton, 
    }: Props = $props();
</script>

Alert 元件中,將 hasCloseButton 屬性加入 Prop 類型,並從物件中解構此屬性。

{#if !closed}
    <div role="alert" class={alertClasses}>
        ... omit the template code...
        {#if hasCloseButton} 
            <div>
                <button class="btn btn-sm btn-primary" title="Close button" onclick={closeAlert}>
                    <CloseIcon />
                </button>
            </div>
        {/if}
    </div>
{/if}

hasCloseButton 為 true,則渲染按鈕;否則按鈕隱藏。


Angular 20 application

import { ChangeDetectionStrategy, Component, computed, input, signal } from '@angular/core';
import { AlertBarComponent } from '../alert-bar/alert-bar.component';

@Component({
  selector: 'app-alert-list',
  imports: [AlertBarComponent],
  template: `
    <app-alert-bar 
      [(hasCloseButton)]="hasCloseButton" 
    />
    
    @for (alert of alerts(); track alert.type) {
      <app-alert [type]="alert.type" 
        [alertConfig]="alertConfig()"
      >
        {{ alert.message }}
      </app-alert>
    }
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertListComponent {
  hasCloseButton = signal<boolean>(true);
    
  alertConfig = computed(() => ({
    hasCloseButton: this.hasCloseButton(),
  }));
}

AlertList 元件中宣告一個 hasCloseButton 信號以接收來自子元件的值。

使用香蕉盒語法 [(expression)],對 AlertListAlertBar 元件進行雙向綁定。

定義一個 alertConfig 計算型信號,用來建立一個物件以儲存 hasCloseButton 信號的值。

alertConfig 計算型信號的值指派給 AlertComponent 的輸入信號 (input signal)。

import { NgComponentOutlet } from '@angular/common';
import { ChangeDetectionStrategy, Component, computed, input, output, signal } from '@angular/core';
import { AlertType } from '../alert.type';
import { CloseIconComponent, ErrorIconComponent, InfoIconComponent, SuccessIconComponent, WarningIconComponent } from '../icons/icon.component';

@Component({
  selector: 'app-alert',
  imports: [NgComponentOutlet, CloseIconComponent],
  template: `
    @if (!closed()) {
        ... omit the template codes ...
        @if (alertConfig().hasCloseButton) {
        <div>
          <button class="btn btn-sm btn-primary" title="Close button" (click)="closeAlert()">
            <app-close-icon />
          </button>
        </div>
        }
    }
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertComponent {
  type = input.required<AlertType>();

  alertConfig = input.required<{
    hasCloseButton: boolean
  }>();
}

AlertComponent 中,宣告一個必填輸入信號 alertConfig

alertConfig().hasCloseButton 為 true,則渲染按鈕,否則按鈕隱藏。

接著,我們將重複相同程序,新增樣式及方向下拉選單,以變更警示元件的樣式。


新增警示樣式和方向下拉選單

Vue 3 application

<script setup lang="ts">
    type Props = {
        config: { 
          styleLabel: string
          styles: { text: string, value: string }[]
          directionLabel: string
          directions: { text: string, value: string }[]
        },
    }

    const props = defineProps<Props>()
    const { config } = props
</script>

AlertBar 元件中宣告 Prop,以接收樣式標籤、樣式下拉選單、方向標籤和方向下拉選單。

const style = defineModel<string>('style', { default: 'color' })
const direction = defineModel<string>('direction', { default: 'horizontal' })

宣告 styledirection 這兩個 ref 用於雙向資料綁定。
style 的預設值為 'color'direction 的預設值為 'horizontal'

<template>
  <div>
    <p class="mb-[0.75rem]">
      <span>{{ config.styleLabel }}&nbsp;&nbsp;</span>
      <select class="select select-info mr-[0.5rem]" v-model="style">
        <option v-for="{value, text} in config.styles" :key="value" :value="value">
          {{ text }}
        </option>
      </select>
      <span>{{ config.directionLabel }}&nbsp;&nbsp;</span>
      <select class="select select-info mr-[0.5rem]" v-model="direction">
        <option v-for="{ value, text } in config.directions" :key="value" :value="value">
          {{ text }}
        </option>
      </select>
    </p>
  </div>
</template>

使用 v-for 迭代 config.stylesconfig.directions,以填充下拉選單的選項項目。


SvelteKit application

<script lang="ts">
	import { capitalize } from './capitalize';
    import OpenIcon from './icons/open-icon.svelte';

    type Props = {
        configs: { 
            styleLabel: string
            styles: { text: string, value: string }[]
            directionLabel: string
            directions: { text: string, value: string }[]
        };
        hasCloseButton: string;
        style: string;
        direction: string;
    }

    let { 
        hasCloseButton = $bindable(), 
        style = $bindable(), 
        direction = $bindable(), 
        closedNotifications = $bindable(),
        configs 
    }: Props = $props();
</script>

configsstyledirection 新增到 Props 類型中。
$props() 解構 styledirection,並使用 $bindable 將它們綁定到 AlertList 元件。

 <p class="mb-[0.75rem]">
    <span>{ configs.styleLabel }&nbsp;&nbsp;</span> { style }
    <select class="select select-info mr-[0.5rem]" bind:value={style}>
        {#each configs.styles as s (s.value) }
            <option value={s.value}>
                { s.text }
            </option>
        {/each}
    </select>
    <span>{ configs.directionLabel }&nbsp;&nbsp;</span>
    <select class="select select-info mr-[0.5rem]" bind:value={direction}> 
        {#each configs.directions as d (d.value)}
            <option value={d.value}>
                { d.text }
            </option>
        {/each}
    </select>
</p>

bind:value={style}style 變數綁定到樣式下拉選單。
bind:value={direction}direction 變數綁定到方向下拉選單。

使用 #each 迭代 stylesdirections 清單,填充下拉選單項目。

Angula 20 application

@Component({
  selector: 'app-alert-bar',
  imports: [FormsModule],
  template: `... inline template explained below ...`,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertBarComponent {
  config = input.required<{ 
    styleLabel: string
    styles: { text: string, value: string }[]
    directionLabel: string
    directions: { text: string, value: string }[]
  }>();

  hasCloseButton = model<boolean>(true);
  style = model<string>('color');
  direction = model<string>('horizontal');
}

AlertBarComponent 宣告了標籤和下拉選單的必填輸入信號。

同樣地,styledirection 被宣告為 model 信號,以實現雙向綁定。

該元件也匯入了 FormsModule,在內嵌模板中將 model 綁定到 [(ngModel)]

  <div>
    @let c = config();
    <p class="mb-[0.75rem]">
      <span>{{ c.styleLabel }}&nbsp;&nbsp;</span>
      <select class="select select-info mr-[0.5rem]" [(ngModel)]="style">
        @for (style of c.styles; track style.value) {
          <option [ngValue]="style.value">
            {{ style.text }}
          </option>
        }
      </select>
      <span>{{ c.directionLabel }}&nbsp;&nbsp;</span>
      <select class="select select-info mr-[0.5rem]" [(ngModel)]="direction">
        @for (direction of c.directions; track direction.value) {
          <option [ngValue]="direction.value">
            {{ direction.text }}
          </option>
        }
      </select>
    </p>
  </div>

[(ngModel)]="style"style 綁定到樣式下拉選單。

同理,[(ngModel)]="direction"direction 綁定到方向下拉選單。

@for 迴圈會遍歷 styles 和 directions 清單,顯示值和文字項目。


執行雙向綁定

Vue 3 Application

config ref 定義了樣式和方向的標籤及項目。與 hasCloseButton

<script setup lang="ts">
import type { AlertType } from '@/types/alert-type';
import { computed, ref } from 'vue';
import Alert from './Alert.vue';
import AlertBar from './AlertBar.vue';

const hasCloseButton = ref(true)
const style = ref('color');
const direction = ref('horizontal')

const alertConfig = computed(() => ({
  style: style.value,
  direction: direction.value,
  hasCloseButton: hasCloseButton.value,
}))

const config = ref({
  styleLabel: "Alert styles:",
  styles: [
    { text: 'Default', value: 'color' },
    { text: 'Soft', value: 'soft' },
    { text: 'Outline', value: 'outline' },
    { text: 'Dash', value: 'dash' }
  ],
  directionLabel: "Alert direction:",
  directions: [
    { text: 'Horizontal', value: 'horizontal' },
    { text: 'Vertical', value: 'vertical' },
  ]
})
</script>

類似,styledirection 將值從 AlertBar 元件傳遞到 AlertList 元件。接著,alertConfig 計算型 ref 回傳 styledirection 的值。alertConfig 將新的值傳遞給 Alert 元件以套用樣式。

<AlertBar
    :config="config"
    v-model:hasCloseButton="hasCloseButton"
    v-model:style="style"
    v-model:direction="direction"
 />

AlertBar 元件接收 config prop。v-model:style="style"。 將 AlertBar 元件的 style ref 綁定到 AlertList 元件的 style ref 。同樣地,v-model:direction="direction"AlertBar 元件的 direction ref 綁定到 AlertList 元件的 direction ref。

SvelteKit Application

樣式和方向的標籤及項目定義於 config 變數中。

<script lang="ts">
	import AlertBar from './alert-bar.svelte';
    import Alert from './alert.svelte';

    const configs = $state(... same object...);

    let hasCloseButton = $state(true);
    let style = $state('color');
    let direction = $state('horizontal');
</script>

hasCloseButton 類似,將 styledirection 變數新增到 AlertList 元件。

<AlertBar {configs} 
    bind:hasCloseButton={hasCloseButton} 
    bind:style={style}
    bind:direction={direction}
/>

AlertBar 元件接收 config prop 以填充下拉選單。AlertBar 使用 bind 語法將 styledirection 傳遞給 AlertList 元件。

{#each filteredNotification as alert (alert.type) } 
    <Alert {alert} {alertMessage} {notifyClosed} {hasCloseButton} {style} {direction} />
{/each}

將新的 prop 值 styledirection 傳遞給 Alert 元件。

Angular 20 Application

樣式和方向的標籤及項目定義於 config 信號中。

import { ChangeDetectionStrategy, Component, computed, input, signal } from '@angular/core';
import { AlertComponent } from '../alert/alert.component';
import { AlertBarComponent } from '../alert-bar/alert-bar.component';

@Component({
  selector: 'app-alert-list',
  imports: [AlertComponent, AlertBarComponent],
  template: `...inline template shown below ...`,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertListComponent {
  style = signal<string>('color');
  direction = signal<string>('horizontal');
  hasCloseButton = signal<boolean>(true);

  config = signal({... same object ... });

  alertConfig = computed(() => ({
    hasCloseButton: this.hasCloseButton(),
    style: this.style(),
    direction: this.direction()
  }));
}

hasCloseButton 類似,將 styledirection 信號新增到 AlertList 元件。alertConfig 計算型信號回傳 styledirection 信號的值。

 <app-alert-bar 
      [config]="config()" 
      [(style)]="style" 
      [(direction)]="direction"
      [(hasCloseButton)]="hasCloseButton"
/>

AlertBar 元件接收 config 輸入以填充下拉選單。AlertBar 使用香蕉盒語法 [(expression)],將 AlertBar 元件的 styledirection models 綁定到 AlertList 元件的 styledirection 信號 (signals)。

[(style)]="style" — 第一個 styleAlertBarComponentstyle model,第二個 styleAlertListComponentstyle 信號 (signal)。

[(direction)]="direction" — 第一個 directionAlertBarComponentdirection model,第二個 directionAlertListComponentdirection 信號 (signal)。


套用樣式與方向到 Alert 元件

Vue 3 Application

 type Props = {
    alertConfig: {
        hasCloseButton: boolean
        style: string
        direction: string
    },
    type: string
}

const { type, alertConfig } = defineProps<Props>()

Alert 元件的 Props 類型中,將 styledirection 新增到 alertConfig 屬性。

const alertColor = computed(() => ({
    info: 'alert-info',
    warning: 'alert-warning',
    error: 'alert-error',
    success: 'alert-success'
}[type]))

 const alertStyle = computed(() => ({
    color: '',
    dash: 'alert-dash',
    soft: 'alert-soft',
    outline: 'alert-outline'
}[alertConfig.style]))

const alertDirection = computed(() => ({
    horizontal: 'alert-horizontal',
    vertical: 'alert-vertical',
}[alertConfig.direction]))

const alertClasses = computed(() => `alert ${alertColor.value} ${alertStyle.value} ${alertDirection.value}`)

建立 alertStyle 計算型 ref 用以推導樣式類別。建立 alertDirection 計算型 ref 用以推導方向類別。

alertClasses 計算型 ref 合併 Alert 元件的樣式類別 (style classes)。

 <template>
  <div role="alert" :class="alertClasses" v-if="!closed">
    <!-- HTML button -->
  </div>
</template>

alertClasses 將新樣式綁定到 class 屬性,以變更方向、邊框樣式及顏色。

SvelteKit Application

type Props = {
    hasCloseButton: boolean;
    style: string;
    direction: string;
}

const { 
    hasCloseButton, 
    direction, 
    style 
}: Props = $props();

Alert 元件的 Props 類型中,新增 styledirection 屬性。

const alertColor = $derived.by(() => ({
    info: 'alert-info',
    success: 'alert-success',
    warning: 'alert-warning',
    error: 'alert-error',
}[alert.type]));

const alertDirection = $derived.by(() => ({
    horizontal: 'alert-horizontal',
    vertical: 'alert-vertical',
}[direction]));

const alertStyle = $derived.by(() => ({
    color: '',
    soft: 'alert-soft',
    outline: 'alert-outline',
    dash: 'alert-dash',
}[style]));

const alertClasses = $derived(`alert ${alertColor} ${alertDirection} ${alertStyle} mb-[0.75rem]`);

建立 alertStyle 衍生 rune 以衍生樣式類別 (style class)。建立 alertDirection 衍生 rune 以衍生方向類別 (direction class)。

{#if !closed}
    <div role="alert" class={alertClasses}>
       <!-- HTML button -->
    </div>
{/if}

alertClasses 衍生 rune串接 Alert 元件的樣式類別,並將新的樣式綁定到 class 屬性,以變更方向、邊框樣式和顏色。

Angular 20 Application

@Component({
  selector: 'app-alert',
  imports: [NgComponentOutlet, CloseIconComponent],
  template: `... inline template ...`,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertComponent {
  type = input.required<AlertType>();

  alertConfig = input.required<{
    hasCloseButton: boolean
    style: string
    direction: string
  }>();

  alertColor = computed(() => {
    return {
        info: 'alert-info',
        warning: 'alert-warning',
        error: 'alert-error',
        success: 'alert-success'
    }[this.type()]
  });

  alertStyle = computed(() => {
    return {
        color: '',
        dash: 'alert-dash',
        soft: 'alert-soft',
        outline: 'alert-outline'
    }[this.alertConfig().style]
  });

  alertDirection = computed(() => {
    return {
        horizontal: 'alert-horizontal',
        vertical: 'alert-vertical',
    }[this.alertConfig().direction]
  });

  alertClasses = computed(() => `alert ${this.alertColor()} ${this.alertStyle()} ${this.alertDirection()}`);
}

styledirection 新增到 alertConfig 必填輸入信號中 (required input signal)。

建立 alertStyle 計算型信號以衍生樣式類別 (style class)。
建立 alertDirection 計算型信號以衍生方向類別 (direction class)。

alertClasses 計算型信號串接 Alert 元件的樣式類別。

@if (!closed()) {
<div role="alert" class="mb-[0.75rem]" [class]="alertClasses()">
       <!-- HTML button -->
</div>
}

alertClasses 將新樣式綁定到 class 屬性,以變更方向、邊框樣式及顏色。

現在,使用者可以在 AlertBar 元件中選擇值,以顯示/隱藏關閉按鈕,並更改警示的邊框樣式、方向和顏色。


Github Repositories

Github Pages

資源


上一篇
第23天 - Alert 組件第二部分 - 動態渲染 (Dynamic Rendering) SVG 圖示
系列文
作為 Angular 專家探索 Vue 3 和 Svelte 525
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言