在第 25 天,我擴充了 Alert Bar
元件來顯示已關閉 Alert 的按鈕。接著,使用者可以選擇重新開啟所有已關閉的 Alert,或是特定類型的 Alert。
<script setup lang="ts">
import { ref } from 'vue'
const closedNotifications = defineModel<string[]>('closedNotifications', { default: [] })
</script>
在 AlertBar
元件中,使用 defineModel
建立一個類型為 string[]
的 closedNotifications
ref。
closedNotifications
的預設值為空清單。
針對此 ref 中的每種類型,會顯示一個按鈕,可用來開啟該警示。
當此 ref 不為空時,亦會顯示 Open all alerts
按鈕。
<script lang="ts">
type Props = {
... other prop ...
closedNotifications: string[];
}
let {
... other bindables ...
closedNotifications = $bindable(),
}: Props = $props();
</script>
將 closedNotifications
回呼屬性 (callback prop) 新增到 Props
類型中。
接著,從 $props()
巨集中解構 closedNotifications
,並使用 $bindable
巨集建立雙向綁定。
import { ChangeDetectionStrategy, Component, input, model } from '@angular/core';
import { FormsModule } from '@angular/forms';
@Component({
selector: 'app-alert-bar',
imports: [FormsModule],
template: `...inline template...`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertBarComponent {
... other models ...
closedNotifications = model<string[]>([]);
}
在 AlertBarComponent
中,建立一個類型為 string[]
的 closedNotifications
模型信號。closedNotifications
的預設值為空字串陣列。
在 AlertBar 元件中,有兩種用戶事件會操作 closedNotifications ref。當使用者點擊 "Open all alerts" 按鈕時,closedNotifications
ref 應該設為空陣列。
當使用者點擊按鈕重新開啟特定類型的警示時,該類型必須從 closedNotifications
ref 中移除。
<script setup lang="ts">
const closedNotifications = defineModel<string[]>('closedNotifications', { default: [] })
function removeNotification(type: string) {
closedNotifications.value = closedNotifications.value.filter((t) => t !== type)
}
function clearAllNotifications() {
closedNotifications.value = []
}
function hasClosedNotifications() {
return closedNotifications.value.length > 0
}
</script>
removeNotification
函數會從 closedNotifications
ref 中移除特定的 type
。clearAllNotifications
函數會從 closedNotifications
ref 中移除所有類型。
hasClosedNotifications
函數用來判斷 closedNotifications
ref 是否為空。
<template>
<div>
<p class="mb-[0.75rem]">
<button v-for="type in closedNotifications"
:key="type" @click="removeNotification(type)"
>
<OpenIcon />{{ capitalize(type) }}
</button>
<button
v-if="hasClosedNotifications()"
@click="clearAllNotifications">
Open all alerts
</button>
</p>
</div>
</template>
v-for
指令會遍歷 closedNotifications
ref,為每個已關閉的通知顯示按鈕。當發生 click
事件時,removeNotification
函數會從該 ref 移除該 type
。
v-if
指令會在 closedNotifications
ref 非空陣列時顯示 Open all alerts
按鈕。當發生 click
事件時,clearAllNotifications
函數會從 ref 中移除所有類型。
In the AlertBar
component.
<script lang="ts">
type Props = {
... omitted due to brevity ...
closedNotifications: string[];
}
let {
... omitted due to brevity ...
closedNotifications = $bindable(),
}: Props = $props();
function removeNotification(type: string) {
closedNotifications = closedNotifications.filter((t) => t !== type)
}
function clearAllNotifications() {
closedNotifications = []
}
function hasClosedNotifications() {
return closedNotifications.length > 0
}
</script>
removeNotification
函數會從 closedNotifications
ref 中移除特定的 type
。clearAllNotifications
函數會從 closedNotifications
ref 中移除所有類型。
hasClosedNotifications
函數用來判斷 closedNotifications
ref 是否為空。
<div>
<p class="mb-[0.75rem]">
{#each closedNotifications as type (type)}
<button
class={getBtnClass(type) + ' mr-[0.5rem] btn'}
onclick={() => removeNotification(type)}
>
<OpenIcon />{ capitalize(type) }
</button>
{/each}
{#if hasClosedNotifications()}
<button
class="btn btn-primary"
onclick={clearAllNotifications}>
Open all alerts
</button>
{/if}
</p>
</div>
在 Svelte 中,事件是以 'on' 開頭的屬性,因此點擊事件的屬性名稱為 onclick
。
onclick={() => removeNotification(type)}
會執行 removeNotification
函數。
onclick={clearAllNotifications}
會執行 clearAllNotifications
函數,這是一個簡寫,因為該函數不需要任何參數。
#each
會遍歷 closedNotifications
回呼屬性,為每個已關閉的通知顯示按鈕。當發生 click
事件時,removeNotification
函數會被觸發。
#if
在 closedNotifications
回呼屬性非空陣列時顯示 Open all alerts
按鈕。當發生 click
事件時,clearAllNotifications
函數會被觸發。
In the AlertBarComponent
import { ChangeDetectionStrategy, Component, input, model } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { capitalize } from '../capitalize';
import { OpenIconComponent } from '../icons/icon.component';
@Component({
selector: 'app-alert-bar',
imports: [FormsModule, OpenIconComponent],
template: `...inline template...`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertBarComponent {
closedNotifications = model<string[]>([]);
removeNotification(type: string) {
this.closedNotifications.update((prev) => prev.filter((t) => t !== type));
}
clearAllNotifications() {
this.closedNotifications.set([])
}
hasCloseButtonChanged() {
return this.closedNotifications().length > 0;
}
}
removeNotification
方法呼叫 closedNotifications
模型信號的 update
方法,以移除特定的 type
。
clearAllNotifications
方法呼叫 closedNotifications
模型信號的 set
方法,以移除所有類型。
hasClosedNotifications
方法用來判斷 closedNotifications
模型信號是否為空。
<div>
<p class="mb-[0.75rem]">
@for (type of closedNotifications(); track type) {
<button (click)="removeNotification(type)">
<app-open-icon />{{ capitalize(type) }}
</button>
}
@if (hasClosedNotifications()) {
<button (click)="closedNotifications.set([])">
Open all alerts
</button>
}
</p>
</div>
在 Angular 中,事件透過發射(emit)產生,並使用香蕉語法 (event)
將事件發射給父元件。
(click)="removeNotification(type)"
會執行 removeNotification
方法。
(click)="clearAllNotifications"
會執行 clearAllNotifications
方法。
@each
會遍歷 closedNotifications
模型信號,為每個已關閉的通知顯示按鈕。 當發生 click
事件時,會觸發 removeNotification
方法。
@if
在 closedNotifications
模型信號非空陣列時顯示 Open all alerts
按鈕。 當發生 click
事件時,會觸發 clearAllNotifications
方法。
AlertList
元件的雙向綁定<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 closedNotifications = ref<string[]>([])
const alerts = computed(() => props.alerts.filter((alert) =>
!closedNotifications.value.includes(alert.type))
)
function handleClosed(type: string) {
closedNotifications.value.push(type)
}
</script>
closedNotifications
ref 綁定到 AlertBar
元件的 closedNotifications
ref。
alerts
計算型 ref 是一個已開啟警示的陣列。當 AlertBar
元件從 closedNotification
中移除任何項目時,alerts
ref 會重新計算要顯示的警示。
handleClosed
函數會將已關閉警示的類型新增到 closedNotifications
ref 中。此函數執行後,alerts
ref 會重新計算以取得新值。
<template>
<AlertBar
v-model:closedNotifications="closedNotifications"
/>
</template>
v-model
指令將 closedNotifications
ref 綁定到 AlertBar
元件的 closedNotifications
ref。 當 AlertBar
元件中的 closedNotifications
更新時,AlertList
元件中的 closedNotifications
也會更新。v-for
指令會遍歷 alerts
計算型 ref,顯示已開啟的警示。
<template>
<Alert v-for="{ type, message } in alerts"
:key="type"
:type="type"
@closed="handleClosed">
{{ message }}
</Alert>
</template>
v-for
指令會遍歷 alerts
計算型 ref,顯示已開啟的警示。
<script lang="ts">
import AlertBar from './alert-bar.svelte';
import Alert from './alert.svelte';
let closedNotifications = $state<string[]>([]);
let filteredNotifications = $derived.by(() =>
alerts.filter(alert => !closedNotifications.includes(alert.type))
);
function notifyClosed(type: string) {
console.log(`Alert of type ${type} closed`);
closedNotifications.push(type);
}
</script>
closedNotifications
變數綁定到 AlertBar
元件的 closedNotifications
變數。filteredNotifications
衍生變數是一個已開啟警示的陣列。當 AlertBar
元件從 closedNotifications
中移除任何項目時,filteredNotifications
衍生變數會重新計算要顯示的警示。
handleClosed
函數會將已關閉警示的類型新增到 closedNotifications
變數中。該函數執行後,filteredNotifications
衍生變數會重新計算以取得新值。
<AlertBar
bind:closedNotifications={closedNotifications}
/>
{#each filteredNotifications as alert (alert.type) }
<Alert {notifyClosed} />
{/each}
bind:closedNotifications={closedNotifications}
表示 AlertList
元件會監聽 AlertBar
元件的 closedNotifications
prop。
#each
會遍歷 filteredNotifications
衍生變數,顯示已開啟的警示。
import { ChangeDetectionStrategy, Component, computed, input, signal } from '@angular/core';
import { AlertType } from '../alert.type';
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...`,
changeDetection: ChangeDetectionStrategy.OnPush,
})
export class AlertListComponent {
closedNotifications = signal<string[]>([]);
filteredAlerts = computed(() =>
this.alerts().filter(alert => !this.closedNotifications().includes(alert.type))
);
handleCloseNotification(type: string) {
this.closedNotifications.update((prev) => ([...prev, type ]));
}
}
closedNotifications
信號綁定到 AlertBar
元件的 closedNotifications
模型信號。
filteredAlerts
計算型信號是一個已開啟警示的陣列。當 AlertBar
元件從 closedNotifications
中移除任何項目時,filteredAlerts
計算型信號會重新計算要顯示的警示。
handleCloseNotification
方法會將已關閉警示的類型新增到 closedNotifications
信號中。該方法執行後,filteredAlerts
會重新計算以取得新值。
<app-alert-bar
[(closedNotifications)]="closedNotifications"
/>
@for (alert of filteredAlerts(); track alert.type) {
<app-alert [type]="alert.type"
[alertConfig]="alertConfig()"(closeNotification)="handleCloseNotification($event)">
{{ alert.message }}
</app-alert>
}
[(closedNotifications)]="closedNotifications"
/> 表示 AlertList
元件會監聽 AlertBar
元件的 closedNotifications
模型信號。
@for
會遍歷 filteredAlerts
計算型信號,顯示已開啟的警示。
現在,使用者可以點擊 AlertBar
元件中的按鈕來開啟已關閉的警示。