iT邦幫忙

2025 iThome 鐵人賽

DAY 7
0
Vue.js

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

第 6 天 - 元件中的用戶事件處理

  • 分享至 

  • xImage
  •  

在第 6 天,我將說明 Vue 3、SvelteKit 與 Angular 如何在購物車元件中回應 HTML 事件。

在購物車元件中,我們會在表單送出事件時,將新項目加入項目清單;同時,當刪除按鈕被點擊時,該項目會從清單中移除。

範例 1:在表單送出時插入一筆項目

Vue 3 範例

<script> 區塊中新增 saveItem 方法,用以將新項目加入 itemsref 陣列。由於 itemsnewItemnewItemHighPriority 都是 Vue 的 ref,因此存取它們的值時需要使用 .value。新的項目包含 newItemnewItemHighPriority 的值,然後被加入陣列中。

<script setup lang="ts">
import { Icon } from "@iconify/vue";
import { ref } from 'vue';

const items = ref<Item[]>([])
const newItem = ref('')
const newItemHighPriority = ref(false)

const saveItem = () => {
  items.value.push({
    id: items.value.length + 1,
    label: newItem.value,
    highPriority: newItemHighPriority.value,
    purchased: false,
  })
  newItem.value = ''
  newItemHighPriority.value = false
}
</script>

<template>
  <form @submit.prevent="saveItem">
    <input v-model.trim="newItem" />
    <label>
      <input type="checkbox" v-model="newItemHighPriority" />
      <span>High Priority</span>
    </label>
    <button>Save Item</button>
  </form>
</template>

@submit.prevent 監聽表單送出事件並呼叫 saveItem,同時透過 preventDefault 避免頁面重新載入。v-model 雙向綁定 newItem 與輸入欄位、newItemHighPriority 與勾選框雙向綁定。

SvelteKit 範例

在 <script> 中定義 saveItem 方法,將新項目加入狀態陣列中。新項目包含 newItemnewItemHighPriority 的值。

<script lang="ts">
import Icon from '@iconify/svelte';

type Item = { id: number; label: string; purchased: boolean; higherPriority: boolean };

let newItem = $state('');
let newItemHigherPriority = $state(false);
let items = $state([] as Item[]);

function saveItem() {
  if (newItem) {
    items.push({
      id: items.length + 1,
      label: newItem,
      purchased: false,
      higherPriority: newItemHigherPriority
    });
    newItem = '';
    newItemHigherPriority = false;
  }
}

async function handleSubmit(event: SubmitEvent) {
  event.preventDefault();
  saveItem();
}
</script>
<form method="POST" onsubmit={handleSubmit}>
  <input id="newItem" name="newItem" type="text" bind:value={newItem} />
  <label>
    <input id="higherPriority" name="higherPriority" type="checkbox" bind:checked={newItemHigherPriority} />
    <span> Higher Priority</span>
  </label>
  <button class="btn btn-primary">Save Item</button>
</form>

Svelte 5 以 on 開頭的事件處理器名稱偵測並處理 DOM 事件。表單使用 onSubmit 執行 handleSubmit,該函式使用 preventDefault 避免頁面重新載入,並呼叫 saveItem 新增項目。

Angular 19 範例

Angular 信號 (signal) 的變異處理方式與 Vue 3、Svelte 不同。當新項目加入陣列時,items signal 不會自動更新,必須更新陣列的引用 (array reference)。Angular 提供 update 方法使用回呼函式 (callback function) 從前一狀態建立新的陣列。

import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { NgIcon, provideIcons } from '@ng-icons/core';
import { matRemove } from '@ng-icons/material-icons/baseline';

type Item = { id: number; label: string; purchased: boolean; highPriority: boolean };

@Component({
  selector: 'app-shopping-cart',
  imports: [FormsModule, NgIcon],
  viewProviders: [provideIcons({ matRemove })],
  template: `
    <form class="add-item-form" (ngSubmit)="saveItem()">
      <input type="text" name="newItem" [(ngModel)]="newItem" />
      <label>
        <input type="checkbox" [(ngModel)]="newItemHighPriority" name="newItemHighPriority" />
        <span> High Priority</span>
      </label>
      <button type="submit">Save Item</button>
    </form>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ShoppingCartComponent {
  items = signal<Item[]>([]);
  newItem = signal('');
  newItemHighPriority = signal(false);

  saveItem() {
    if (!this.newItem()) return;

    const id = this.items().length + 1;
    this.items.update(items => [
      ...items,
      {
        id,
        label: this.newItem(),
        purchased: false,
        highPriority: this.newItemHighPriority()
      }
    ]);
    this.newItem.set('');
    this.newItemHighPriority.set(false);
  }
}

Angular 的 ngSubmit 事件會處理表單送出,且會自動呼叫 preventDefault 避免頁面重載。表單使用 banana syntax (ngSubmit)="saveItem()" 綁定事件處理函式。

範例 2:點擊按鈕後刪除一筆項目

Vue 3 範例

deleteItem 函式利用 filter 過濾 items.value 陣列以去除指定 ID 的項目,並將結果重新指定給 items.value。

<script setup lang="ts">
import { Icon } from "@iconify/vue";
import { ref } from 'vue';

const items = ref<Item[]>([])
const newItem = ref('')
const newItemHighPriority = ref(false)

const deleteItem = (id: number) => {
  items.value = items.value.filter(item => item.id !== id)
}
</script>

<template>
  <!-- 先前的表單 -->
  <ul>
    <div v-for="item in items" :key="item.id">
      <li>{{ item.id }} - {{ item.label }}</li>
      <button aria-label="Delete" @click="deleteItem(item.id)">
        <Icon icon="ic:baseline-remove" />
      </button>
    </div>
  </ul>
</template>

按鈕的 @click 事件會觸發 deleteItem,刪除指定項目。

SvelteKit 範例

同樣用 filter 過濾 items 陣列以刪除指定 ID 項目。

<script lang="ts">
import Icon from '@iconify/svelte';

type Item = { id: number; label: string; purchased: boolean; higherPriority: boolean };

let newItem = $state('');
let newItemHigherPriority = $state(false);
let items = $state([] as Item[]);

function deleteItem(id: number) {
  items = items.filter(item => item.id !== id);
}
</script>

<ul>
  {#each items as item (item.id)}
    <li>{item.id} - {item.label}</li>
    <button onclick={() => deleteItem(item.id)} aria-label="delete an item">
      <Icon icon="ic:baseline-remove" />
    </button>
  {/each}
</ul>

Svelte 5 使用 onClick 綁定點擊事件並執行刪除。

Angular 19 範例

使用 update 方法與 Array.filter 創建新陣列,刪除指定 ID 項目。

type Item = { id: number; label: string; purchased: boolean; highPriority: boolean };

@Component({
  selector: 'app-shopping-cart',
  imports: [FormsModule, NgIcon],
  viewProviders: [provideIcons({ matRemove })],
  template: `
    <ul>
      @for (item of items(); track item.id) {
        <li [class]="itemClasses">
          {{ item.id }} - {{ item.label }}
        </li>
        <button aria-label="Delete" (click)="deleteItem(item.id)">
          <ng-icon name="matRemove"></ng-icon>
        </button>
      }
    </ul>
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ShoppingCartComponent {
  items = signal<Item[]>([]);
  newItem = signal('');
  newItemHighPriority = signal(false);

  deleteItem(id: number) {
    this.items.update(items => items.filter(item => item.id !== id));
  }
}

Angular 使用 (click)="deleteItem(item.id)" 綁定點擊事件。

這樣我們成功讓購物車元件在送出表單時新增項目,並在點擊按鈕時刪除項目。

參考資源

Github Repositories

Github Pages


上一篇
第 5 天:元件中的用戶輸入處理
下一篇
第七天 - 使用內建控管流程語法或指令進行條件渲染
系列文
作為 Angular 專家探索 Vue 3 和 Svelte 511
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言