iT邦幫忙

2025 iThome 鐵人賽

DAY 8
0
Vue.js

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

第七天 - 使用內建控管流程語法或指令進行條件渲染

  • 分享至 

  • xImage
  •  

本篇文章示範了 Vue 3、SvelteKit 與 Angular 如何使用內建的控管流程語法或指令來進行條件渲染。Vue 3 使用 v-ifv-else-ifv-else 指令來條件渲染,而 Svelte 與 Angular 則使用 if/else-if/else 來在 HTML 模板中條件渲染元素。

範例一:當清單有項目時顯示無序列表

Vue 3 範例

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

const items = ref<Item[]>([])
const deleteItem = (id: number) => { /* 刪除邏輯同前 */ }
</script>

<template>
  <template v-if="items.length > 0">
    <ul>
      <div class="list-item" 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>
  <p v-else>Nothing to see here.</p>
</template>

這裡將無序列表包裹在 <template> 元素內,並在該元素上加上 v-if="items.length > 0 指令。當 items 陣列長度大於 0 時,顯示此列表;反之則顯示一段文字 "Nothing to see here." 透過 v-else 指令達成。

SvelteKit 範例

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

  let items = $state([] as Item[]);

  function deleteItem(id: number) { /* 刪除邏輯同前 */ }
</script>

{#if items.length > 0}
<ul>
  {#each items as item (item.id)}
    <div class="list-item">
      <li>{item.id} - {item.label}</li>
      <button onclick={() => deleteItem(item.id)} aria-label="delete an item">
        <Icon icon="ic:baseline-remove" />
      </button>
    </div>
  {/each}
</ul>
{:else}
<p>Nothing to see here</p>
{/if}

使用 {#if} 包裹清單,當 items 不為空陣列時顯示清單,否則使用 {:else} 顯示文字。

Angular 19 範例

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

@Component({
  selector: 'app-shopping-cart',
  imports: [FormsModule, NgIcon],
  viewProviders: [provideIcons({ matRemove, matAdd, matSave })],
  template: `
    @if (items().length > 0) {
      <ul>
        @for (item of items(); track item.id) {
          <div class="list-item">
            <li>{{ item.id }} - {{ item.label }}</li>
            <button aria-label="Delete an item" (click)="deleteItem(item.id)">
              <ng-icon name="matRemove"></ng-icon>
            </button>
          </div>
        }
      </ul>
    } @else {
      <p>Nothing to see here.</p>
    }
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ShoppingCartComponent {
  items = signal<Item[]>([]);

  deleteItem(id: number) { /* 刪除邏輯同前 */ }
}

Angular 17 引入了新的控管流程語法,使用 @if@else 來條件渲染。當 items 信號陣列不空時展示列表,否則展示文字。

範例二:按下按鈕後條件顯示新增項目表單

Vue 3 範例

新增 isEditing 這個 ref 來保存新增項目表單的狀態。當 isEditingtrue 時,表單顯示且可編輯;為 false 時,表單隱藏不可編輯。

<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 = () => {
  /* 儲存邏輯同前 */
}

const isEditing = ref(false)

const toggleEditing = (value: boolean) => {
  isEditing.value = value
  newItem.value = ''
  newItemHighPriority.value = false
}
</script>

<div class="header">
  <button v-if="isEditing" @click="toggleEditing(false)" aria-label="Cancel">
    <Icon icon="ic:outline-close" />
  </button>
  <button v-else @click="toggleEditing(true)" aria-label="Add Item">
    <Icon icon="ic:outline-add" />
  </button>
</div>

<form v-if="isEditing" @submit.prevent="saveItem">
  <input v-model.trim="newItem" placeholder="Add new item" />
  <label>
    <input type="checkbox" v-model="newItemHighPriority" />
    <span> High Priority</span>
  </label>
  <button aria-label="Save Item">
    <Icon icon="ic:outline-save" />
  </button>
</form>

按下 "Add Item" 按鈕時,isEditing 會被設為 true,表單顯示且可編輯,並顯示 "Cancel" 按鈕。點擊 "Cancel" 時,toggleEditing 函式會將 isEditing 設為 false,表單隱藏,按鈕切換回 "Add Item"。

SvelteKit 範例

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

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

let isEditing = $state(false);

function saveItem() { /* 儲存邏輯同前 */ }

async function handleSubmit(event: SubmitEvent) {
   /* 儲存邏輯同前 */
}

function toggleEdit(value: boolean) {
  isEditing = value;
  newItem = '';
  newItemHigherPriority = false;
}
</script>

<div class="header">
  {#if isEditing}
    <button onclick={() => toggleEdit(false)} aria-label="Cancel">
      <Icon icon="ic:outline-close" />
    </button>
  {:else}
    <button onclick={() => toggleEdit(true)} aria-label="Add Item">
      <Icon icon="ic:outline-add" />
    </button>
  {/if}
</div>

{#if isEditing}
  <form method="POST" onsubmit={handleSubmit}>
    <input id="newItem" name="newItem" type="text" placeholder="Add item" bind:value={newItem} />
    <label>
      <input id="newItemHigherPriority" name="newItemHigherPriority" type="checkbox" bind:checked={newItemHigherPriority} />
      <span> Higher Priority</span>
    </label>
    <button aria-label="Save Item">
      <Icon icon="ic:outline-save" />
    </button>
  </form>
{/if}

isEditing 為 true,表單顯示可編輯,並顯示 "Cancel" 按鈕;按下 "Cancel" 會關閉表單。當 isEditing 為 false,表單隱藏,顯示 "Add Item" 按鈕。

Angular 19 範例

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

@Component({
  selector: 'app-shopping-cart',
  imports: [FormsModule, NgIcon],
  viewProviders: [provideIcons({ matRemove, matAdd, matSave })],
  template: `
    <div class="header">
      @if (isEditing()) {
        <button (click)="toggleEditing(false)" aria-label="Cancel">
          <ng-icon name="matRemove"></ng-icon>
        </button>
      } @else {
        <button (click)="toggleEditing(true)" aria-label="Add Item">
          <ng-icon name="matAdd"></ng-icon>
        </button>
      }
    </div>
    @if (isEditing()) {
      <form class="add-item-form" (ngSubmit)="saveItem()">
        <input type="text" placeholder="Add new item" name="newItem" [(ngModel)]="newItem" />
        <label>
          <input type="checkbox" [(ngModel)]="newItemHighPriority" name="newItemHighPriority" />
          <span> High Priority</span>
        </label>
        <button type="submit" aria-label="Save Item">
          <ng-icon name="matSave"></ng-icon>
        </button>
      </form>
    }
  `,
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ShoppingCartComponent {
  items = signal<Item[]>([]);
  newItem = signal('');
  newItemHighPriority = signal(false);
  isEditing = signal(false);

  toggleEditing(value: boolean) {
    this.isEditing.set(value);
    this.newItem.set('');
    this.newItemHighPriority.set(false);
  }

  saveItem() { /* 儲存邏輯同前 */ }
}

isEditing 信號用來控制新增項目表單的顯示與隱藏。當 isEditing 為 true,表單顯示且可編輯,取消按鈕出現;當 isEditing 為 false,顯示 "Add Item" 按鈕,表單隱藏。

Github Repositories

Github Pages


上一篇
第 6 天 - 元件中的用戶事件處理
下一篇
第 8 天 - 在 Vue 3、Svelte 5 和 Angular 中動態綁定 CSS 類別與樣式
系列文
作為 Angular 專家探索 Vue 3 和 Svelte 519
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言