if 邏輯區塊if 邏輯區塊語法if 邏輯區塊  經過了三天的消化,我們知道了該如何運用 Svelte 提供的 each 邏輯區塊,讓我們可以運用類似 Javascript forEach 的邏輯運算,快速地根據 Javascript 陣列變數做出重複的 HTML 元素。是不是非常的方便又實用呢?今天就讓我們再接再厲,繼續學另一種 Svelte 所提供的邏輯區塊吧。
if 邏輯區塊語法一般的 HTML 並沒辦法提供條件式的運算來決定哪些 HTML 元素需要呈現給使用者,又是哪些 HTML 元素需要移除掉。但是為了能夠做出互動式 (reactive) 的使用者介面,這看起來是個不可或缺的功能。作為一個受到眾多工程師們喜愛的前端框架,Svelte 理所當然的能夠實現這個願望。程式碼的寫法如下:
{#if expression}
  <!-- 將需要的 HTML 元素放在這裡 -->
{/if}
第一行:{#if expression}...{/if}
  用 {#if} 開啟邏輯段落。expression 則是 Javascript 的表達式,能夠算出一個 true 或是 false 的結果。
第二行:<!-- 將需要的 HTML 元素放在這裡 -->
  如果前面 expression 的值,經過布林值的轉換,結果為 true,就會在呈現給使用者看的介面當中加入這邊的 HTML 元素。反之,如果為 false,就不會出現這些 HTML 元素。
第三行:{/if}
  記得要用 {/if} 結束這個邏輯段落。
  既然有 if 的邏輯段落,那想必會有跟 else 邏輯段落的組合囉:
{#if expression}
  <!-- 如果 expression 為 true,則顯示這邊的 HTML 元素 -->
{:else}
  <!-- 否則顯示這邊的 HTML 元素 -->
{/if}
第一行:{#if expression}
  用 {#if} 開啟邏輯段落。接著不贅述。
第二行:<!-- 如果 expression 為 true,則顯示這邊的 HTML 元素 -->
  如果前面 expression 的值,經過布林值的轉換,結果為 true,就會在呈現給使用者看的介面當中加入這邊的 HTML 元素。
第三行:{:else}
  用 {:else} 將 if 這個邏輯段落做展延。
第四行:<!-- 否則顯示這邊的 HTML 元素 -->
  如果前面 expression 的值,經過布林值的轉換,結果為 false,就會在呈現給使用者看的介面當中加入這邊的 HTML 元素。
第五行:{/if}
  最後,還是記得要用 {/if} 結束這個邏輯段落。
  既然有 if 跟 else 的邏輯段落,那想必又會再有 else if 邏輯段落的組合囉:
{#if expression1}
  <!-- 如果 expression1 為 true,則顯示這邊的 HTML 元素 -->
{:else if expression2}
  <!-- 如果 expression2 為 true,則顯示這邊的 HTML 元素 -->
{/if}
第一行:{#if expression}
  用 {#if} 開啟邏輯段落。接著不贅述。
第二行:<!-- 如果 expression1 為 true,則顯示這邊的 HTML 元素 -->
  如果 expression1 的值,經過布林值的轉換,結果為 true,就會在呈現給使用者看的介面當中加入這邊的 HTML 元素。
第三行:{:else if expression2}
  用 {:else} 將 if 這個邏輯段落做展延,並繼續做第二個條件 expression2 的計算。
第四行:<!-- 否則顯示這邊的 HTML 元素 -->
  如果 expression2 的值,經過布林值的轉換,結果為 true,就會在呈現給使用者看的介面當中加入這邊的 HTML 元素。
第五行:{/if}
  最後,還是記得要用 {/if} 結束這個邏輯段落。
  既然有 if 跟 else 跟 else if 的邏輯段落,那想必又會再有沒完沒了的各種組合囉?沒錯,Svelte 就是提供了一個這麼富有彈性又方便的 if 邏輯段落。
if 邏輯區塊  學到一個這麼好用的工具,不實際來運用看看豈不可惜。就讓我們試試看用 if 邏輯區塊去動態顯示色票的鎖定跟解除鎖定的狀態吧。直接來到 Palettes.svelte 的程式碼:
/src/lib/Palettes.svelte
<script>
  import unlock from "../assets/unlock.svg";
  import lock from "../assets/lock.svg";
  export let palettes;
</script>
<div class="palettes">
  {#each palettes as { hex, id, locked } (id)}
    <div class="card">
      <div class="palette" style="background: #{hex}" />
      <div class="hex-code">
        <p>
          {hex}
        </p>
      </div>
      <!-- svelte-ignore a11y-no-static-element-interactions a11y-click-events-have-key-events -->
      <div class="lock-icon">
        {#if locked}
          <img src={lock} alt="color-locked" />
        {:else}
          <img src={unlock} alt="color-unlocked" />
        {/if}
      </div>
    </div>
  {:else}
    <div class="card no-color">
      <div>
        <p>No color to show. Please add a color.</p>
      </div>
    </div>
  {/each}
</div>
第三行:import lock from "../assets/lock.svg";
  為了要表達鎖定色票的狀態,我們同樣也從 Font Awesome 參考了上鎖的圖示。詳細的程式碼可以參考 Font Awesome 的網站(這裡)。
第八行:{#each palettes as { hex, id, locked } (id)}
  因為我們需要從 palettes 每個物件當中的 locked 這個鍵值 (key) 來判斷該色票是否為鎖定的狀態,所以記得用解構賦值把 locked 也拿出來。
第十八行:{#if locked}
  用 {#if} 開啟 if 的邏輯段落。並且用 locked 這個變數的布林值來做為顯示 HTML 元素的依據。
第十九行:<img src={lock} alt="color-locked" />
  如果 locked 這個布林值為 true,則顯示上鎖這個圖示。
第二十行:{:else}
  用 {:else} 展延出 else 的邏輯段落。
第二十一行:<img src={unlock} alt="color-unlocked" />
  如果 locked 的布林值為 false,則顯示解鎖這個圖示。
第二十二行:{/if}
  記得用 {/if} 來結束整個 if 邏輯段落。
  雖然把 if 邏輯段落做好了,但是仔細檢視看看我們的專案,應該就會注意到現在所有色票的 locked 都為 false,這樣就看不出來 if 邏輯段落是否符合我們所設計的方式去運作。讓我們把新增的色票的 locked 都改成 true 試試看吧:
/src/App.svelte
<!-- 在 Javascript 當中 import Counter -->
<script>
  import Counter from "./lib/Counter.svelte";
  import Modal from "./lib/Modal.svelte";
  import Palettes from "./lib/Palettes.svelte";
  let showModal = false;
  let palettes = [
    { hex: "ff4000", locked: false, id: "init_01" },
    { hex: "32e6e3", locked: false, id: "init_02" },
    { hex: "009fe9", locked: false, id: "init_03" },
  ];
  $: count = palettes.length;
  const generateTone = () =>
    ("0" + Math.round(255 * Math.random()).toString(16)).slice(-2);
  const generateHex = () =>
    [generateTone(), generateTone(), generateTone()].reduce(
      (a, c) => a + c,
      ""
    );
  const generatePalette = (() => {
    let uuid = 0;
    return () => ({
      hex: generateHex(),
      locked: true,
      id: `${uuid++}`,
    });
  })();
  let someState = "TheGreatSvelte";
  const sparkle = (text) => {
    const sparkles = ["★", "☆", "✧", "✪"];
    const randomSparkles = () =>
      sparkles[Math.floor(Math.random() * sparkles.length)];
    const sparkledText = text
      .split("")
      .reduce((a, c) => a + randomSparkles() + c, "");
    return sparkledText;
  };
  const href = "https://ithelp.ithome.com.tw/users/20120178/ironman/7031";
  const handleClick = (e) => {
    console.log(e);
    const tobeCount = count + e.detail;
    if (0 <= tobeCount && tobeCount < 7) {
      switch (e.detail) {
        case 1:
          palettes = [...palettes, generatePalette()];
          break;
        case -1:
          palettes = palettes.slice(0, -1);
          break;
      }
    } else showModal = true;
  };
</script>
這麼一大段程式碼,有改變的只有這一行:
locked: true,locked 變成 true 吧。
圖一,新增的色票都給他鎖起來
  太棒了,看來我們做出來的 if 邏輯段落是真的有發揮作用的。那麼野心勃勃的人,是不是想要更進一步去實作互動的功能,讓使用者可以藉由點擊這個上鎖/解鎖圖示,來改變上鎖/解鎖的狀態呢?沒問題,讓我們藉由第十三天學過的客製化事件來完成這個任務吧:
/src/lib/Palettes
<script>
  import { createEventDispatcher } from "svelte";
  import unlock from "../assets/unlock.svg";
  import lock from "../assets/lock.svg";
  export let palettes;
  const dispatch = createEventDispatcher();
  const handleClick = (id) => dispatch("changeLock", id);
</script>
<div class="palettes">
  {#each palettes as { hex, id, locked } (id)}
    <div class="card">
      <div class="palette" style="background: #{hex}" />
      <div class="hex-code">
        <p>
          {hex}
        </p>
      </div>
      <!-- svelte-ignore a11y-no-static-element-interactions a11y-click-events-have-key-events -->
      <div class="lock-icon" on:click={() => handleClick(id)}>
        {#if locked}
          <img src={lock} alt="color-locked" />
        {:else}
          <img src={unlock} alt="color-unlocked" />
        {/if}
      </div>
    </div>
  {:else}
    <div class="card no-color">
      <div>
        <p>No color to show. Please add a color.</p>
      </div>
    </div>
  {/each}
</div>
第二行:import { createEventDispatcher } from "svelte";
  引入客製化事件需要的 createEventDispatcher
第七行:const dispatch = createEventDispatcher();
  初始化客製化事件產生器。
第八行:const handleClick = (id) => dispatch("changeLock", id);
  客製化一個 changeLock 事件,隨著該事件傳送出 id 的資訊,讓我們主要元件 (App.svelte) 能夠知道應該改變哪一個色票的 locked 狀態。
第二十一行:<div class="lock-icon" on:click={() => handleClick(id)}>
  在這個 <div> 上加上我們的事件處理器 handleClick,只要一點擊,就發送 changeLock 事件。
  接著來到我們的 App.svelte,需要做些修改,來接收子元件 Palettes.svelte 發送過來的 changeLock 事件:
/src/App.svelte
<!-- 在 Javascript 當中 import Counter、Modal、Palettes -->
<script>
  import Counter from "./lib/Counter.svelte";
  import Modal from "./lib/Modal.svelte";
  import Palettes from "./lib/Palettes.svelte";
  let showModal = false;
  let palettes = [
    { hex: "ff4000", locked: false, id: "init_01" },
    { hex: "32e6e3", locked: false, id: "init_02" },
    { hex: "009fe9", locked: false, id: "init_03" },
  ];
  $: count = palettes.length;
  const generateTone = () =>
    ("0" + Math.round(255 * Math.random()).toString(16)).slice(-2);
  const generateHex = () =>
    [generateTone(), generateTone(), generateTone()].reduce(
      (a, c) => a + c,
      ""
    );
  const generatePalette = (() => {
    let uuid = 0;
    return () => ({
      hex: generateHex(),
      locked: true,
      id: `${uuid++}`,
    });
  })();
  let someState = "TheGreatSvelte";
  const sparkle = (text) => {
    const sparkles = ["★", "☆", "✧", "✪"];
    const randomSparkles = () =>
      sparkles[Math.floor(Math.random() * sparkles.length)];
    const sparkledText = text
      .split("")
      .reduce((a, c) => a + randomSparkles() + c, "");
    return sparkledText;
  };
  const href = "https://ithelp.ithome.com.tw/users/20120178/ironman/7031";
  const handleClick = (e) => {
    console.log(e);
    const tobeCount = count + e.detail;
    if (0 <= tobeCount && tobeCount < 7) {
      switch (e.detail) {
        case 1:
          palettes = [...palettes, generatePalette()];
          break;
        case -1:
          palettes = palettes.slice(0, -1);
          break;
      }
    } else showModal = true;
  };
  const handleLock = (e) => {
    console.log(e);
    const targetId = e.detail;
    palettes = palettes.map((palette) => {
      if (palette.id === targetId) {
        return { ...palette, locked: !palette.locked };
      } else {
        return palette;
      }
    });
  };
</script>
第六十一行:const handleLock = (e) => {
  宣告一個函式 handleLock,用來處理 changeLock 事件。這個函式需要用我們的客製化事件當作參數,所以在參數的部分寫一個 e 代表。
第六十二行:console.log(e);
  將事件 e 記錄下來。單純方便偵錯使用,不一定需要這一行。
第六十三行:const targetId = e.detail;
  我們伴隨客製化事件 changeLock 一起送出來的資料會放在哪邊呢?就是在 e.detail 當中,所以用 targetId 將目標的 id 儲存起來。接下來的程式碼就不細講了。大意就是在 palettes 當中找出 id === targetId 的色票,並切換該色票的 locked 布林值。原本為 true 的就變成 false,原本為 false 的就變成 true。

圖二、三個可以自由解鎖/開鎖的色票
  好的,那麼今天關於 if 邏輯段落的介紹就到這邊了,謝謝大家。