iT邦幫忙

2025 iThome 鐵人賽

DAY 16
0
Vue.js

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

第 15 天- Add a Coffee Plan Form

  • 分享至 

  • xImage
  •  

第 15 天- Add a Coffee Plan Form

在第15天,我擴展了 PlanPicker 組件,加入了一個 AddCoffeePlan 組件,用來向咖啡計劃列表中 (plan list) 添加新的咖啡計劃 (coffee plan)。接著,PlanPicker 組件有兩個子組件,分別是 AddCoffeePlanCoffeePlan

建立 AddCoffeePlan

  • Vue 3 application

建立一個新的 components/AddCoffeePlan.vue 檔案。

<style scoped>
input {
  padding: 0.5rem 0.75rem;
}

.add-plan-form {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.add-plan-form input {
  width: 70%;
  border-radius: 3px;
  box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
  border: 1px solid #f1f5f8;
  color: #606f7b;
  padding: 0.5rem 0.75rem;
  box-sizing: border-box;
  font-size: 1rem;
  letter-spacing: 0.5px;
  margin: 0.5rem 0;
}
</style>

AddCoffeePlan 添加範圍限定的樣式。script 標籤具有 scoped 屬性。

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

const emit = defineEmits<{
  (e: 'newCoffeePlan', name: string): void
}>();

const newPlan = ref('');
function addPlan() {
    const trimmedPlan = newPlan.value.trim();
    if (!trimmedPlan) {
        return;
    }

    emit('newCoffeePlan', trimmedPlan);
    newPlan.value = '';
} 
</script>

newPlan 這個 ref 用來儲存將要新增到咖啡計劃列表 (plan list) 中的新咖啡計劃 (coffee plan)。

defineEmits 定義了一個自訂的 newCoffeePlan 事件,用來將 newPlan 的值傳遞給 PlanPicker 組件。

addPlan 用來處理表單提交事件,觸發 newCoffeePlan 事件,並清空 newPlan

<template>    
    <form class="add-plan-form" @submit.prevent="addPlan">
      <input v-model.trim="newPlan" type="text" placeholder="Add a new plan" />
      <button class="btn btn-primary" type="submit" :disabled="newPlan.length < 5">
        Add Plan
      </button>
    </form>    
</template>

模板使用 v-model.trim 將文本框綁定到 newPlan 這個 ref。

除非使用者在文本框輸入至少5個字元,否則 Add Plan 按鈕會被禁用。

當使用者提交表單時,addPlan 函數會驗證新的咖啡計劃 (coffee plan),並將該值發送給父組件。

  • SvelteKit application

建立一個新的 lib/add-coffee-plan.svelte 檔案。

AddCoffeePlan 添加範圍限定 (scoped) 的樣式 (CSS)。 樣式 (CSS) 預設即為範圍限定 (scoped)。

<style>
    input {
      padding: 0.5rem 0.75rem;
    }
    
    .add-plan-form {
      display: flex;
      align-items: center;
      justify-content: space-between;
    }
    
    .add-plan-form input {
      width: 70%;
      border-radius: 3px;
      box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
      border: 1px solid #f1f5f8;
      color: #606f7b;
      padding: 0.5rem 0.75rem;
      box-sizing: border-box;
      font-size: 1rem;
      letter-spacing: 0.5px;
      margin: 0.5rem 0;
    }
</style>
<script lang="ts">
interface Props {
    addCoffeePlan: (plan: string) => void;
}

const { addCoffeePlan }: Props = $props();

let newPlan = $state('');
const addPlan = (e: SubmitEvent) => {
    e.preventDefault();
    const trimmedPlan = newPlan.trim();
    if (!trimmedPlan) {
        return;
    }
    
    addCoffeePlan(trimmedPlan);
    newPlan = '';
}
</script>

newPlan 變數用來儲存將要新增到咖啡計劃列表 (plan list) 中的新咖啡計劃 (coffee plan)。

PlanPicker 組件將 addCoffeePlan 函數傳遞給 AddCoffeePlan 組件。 AddCoffeePlan 組件從 $props 解構出addCoffeePlan 函數,此函數用於向 PlanPicker 組件發出新的咖啡計劃 (coffee plan)。

addPlan 負責以下事項:

  • 監聽表單提交事件,
  • 防止頁面重新載入,
  • 透過呼叫 addCoffeePlan 函數通知父組件,
  • 清空該變數。
<form class="add-plan-form" onsubmit={addPlan}>
    <input type="text" placeholder="Add a new plan" bind:value={newPlan} />
    <button type="submit" class="btn btn-primary" disabled={newPlan.length < 5}>
        Add Plan
    </button>
</form>	

模板使用 bind:value 將文本框綁定到 newPlan 這個變數。

除非使用者在文本框輸入至少5個字元,否則 Add Plan 按鈕會被禁用。

表單註冊了 addPlan 用來監聽 onsubmit 事件。當使用者提交表單時,addPlan 會使用 addCoffeePlan 函數將新值傳遞給父組件。

  • Angular 19 application

Use the Angular CLI to create a new AddCoffeePlanComponent

ng g c AddCoffeePlan

該組件的 CSS 定義在獨立的 add-coffee-plan.component.css 檔案中。也支援內嵌的 CSS 樣式,具體使用哪種方式取決於程式設計的。

input {
    padding: 0.5rem 0.75rem;
}
  
.add-plan-form {
    display: flex;
    align-items: center;
    justify-content: space-between;
}
  
.add-plan-form input {
    width: 70%;
    border-radius: 3px;
    box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
    border: 1px solid #f1f5f8;
    color: #606f7b;
    padding: 0.5rem 0.75rem;
    box-sizing: border-box;
    font-size: 1rem;
    letter-spacing: 0.5px;
    margin: 0.5rem 0;
}
import { ChangeDetectionStrategy, Component, output, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';

@Component({
  selector: 'app-add-coffee-plan',
  imports: [FormsModule],
  template: `... inline template ...`,
  styleUrl: './add-coffee-plan.component.css',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class AddCoffeePlanComponent {
  newPlan = signal('');

  addCoffeePlan = output<string>();

  addPlan() {
    this.addCoffeePlan.emit(this.newPlan());
    this.newPlan.set('');
  }
}

該組件匯入了 FormsModule,以啟用 ngSubmit 並使信號 (signal) 可以雙向綁定 (two-way binding) 到 HTML 元素。

newPlan 信號用來儲存將要新增到咖啡計劃列表 (plan list) 中的新咖啡計劃 (coffee plan)。

addCoffeePlan 是一個輸出函數,用於將新值發送給 PlanPicker 組件。

addPlan 方法處理 ngSubmit 事件,隱式阻止頁面重新加載,觸發
addCoffeePlan 自訂事件,並清空 newPlan 信號。該組件匯入了 FormsModule,以啟用 ngSubmit 並使信號 (signal) 可以雙向綁定到 HTML 元素。

template: `
<form class="add-plan-form" (ngSubmit)="addPlan()">
    <input name="newPlan" type="text" placeholder="Add a new plan" [(ngModel)]="newPlan" />
    <button class="btn btn-primary" type="submit" [disabled]="newPlan().length < 5">
        Add Plan
    </button>
</form>   
`

[(ngModel)] 雙向綁定 newPlan 信號到輸入框。當使用者在文字框輸入新的咖啡計劃 (coffee plan) 時,newPlan 會接收新的值。

Add Plan 按鈕使用方括號語法 [expression] 將輸入綁定到屬性。當使用者輸入少於 5 個字元到文字框時,[disabled] 為 true。

ngSubmit 是一個自訂的 Angular 事件,當表單提交時觸發,事件處理函式 (event handler) 為 addPlanaddPlan 呼叫 addCoffeePlan 輸出事件 (event emitter),將新值傳遞給父組件。

將 AddCoffeePlan 匯入到 PlanPicker

  • Vue 3 application

AddCoffeePlan 匯入到 PlanPicker

<script setup lang="ts">
import AddCoffeePlan from './AddCoffeePlan.vue'
</script>

在模板中使用 AddCoffeePlan 組件

<template>
  <div class="plans">
    <AddCoffeePlan @newCoffeePlan="(plan) => plans.push(plan)" />
  </div>
</template>

@newCoffeePlan 自訂事件會呼叫一個內聯函式,以將新的計劃 (coffee plan) 加入咖啡計劃列表 (plan list) 中。

  • SvelteKit application

Import AddCoffeePlan to PlanPicker

<script lang="ts">
	import CoffeePlan from './coffee-plan.svelte';
</script>

Use the AddCoffeePlan in the template

<div class="plans">
	<AddCoffeePlan addCoffeePlan={(plan) => plans.push(plan)}  />
</div>

addCoffeePlan 自訂事件也會呼叫一個內聯函式,將新的計劃 (coffee plan) 加入到咖啡計劃列表 (plan list) 中。

  • Angular 19 application

請將 AddCoffeePlanComponent 匯入到 PlanPickerComponent

import { ChangeDetectionStrategy, Component, signal } from '@angular/core';
import { AddCoffeePlanComponent } from '../add-coffee-plan/add-coffee-plan.component';
import { CoffeePlanComponent } from '../coffee-plan/coffee-plan.component';

@Component({
  selector: 'app-plan-picker',
  imports: [CoffeePlanComponent, AddCoffeePlanComponent],
  template: `... inline template ...`,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class PlanPickerComponent {
  plans = signal(['The Single', 'The Curious', 'The Addict', 'The Hacker']);

  addPlan(name: string) {
    this.plans.update((plans) => [...plans, name]);
  }
}

AddCoffeePlanComponent 組件加入到 imports 陣列中。

addPlan 方法使用 Signal API 的 update 方法,將新的咖啡計劃加入到 plans 信號中。

<div class="plans">
   <app-add-coffee-plan (addCoffeePlan)="addPlan($event)" />
</div>

addCoffeePlan 自訂事件使用特殊的 ``$event來傳遞新的咖啡計劃給addPlan` 方法,以便將新的咖啡名稱加入。

結論

我們已成功建立了一個 AddCoffeePlan 組件,該組件可以將新的咖啡計劃新增到 PlanPicker 組件中的咖啡計劃列表。

資源

Github Repositories


上一篇
第14天 - 建立 PlanPicker 父元件
下一篇
第16天 - 使用元件事件選擇咖啡方案
系列文
作為 Angular 專家探索 Vue 3 和 Svelte 518
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言