each 邏輯區塊(一)「我的
each邏輯區塊看起來真棒,不是嗎?」他徵詢我的意見。「看看它是如何將繁瑣的工作處理得如此簡潔漂亮。」
我同意這個each邏輯區塊的確厲害。
「沒錯。」他掃視著那些each邏輯區塊,從每一個開頭到每一個結尾,「我花了整整三年時間掙得的錢才賣下了它。」~節錄自《The Great Svelte:第五章》
each 邏輯區塊  系列文進行到這邊已經過一半了,回頭想想我們似乎學會了不少東西,但看看這個專案的目標:隨機色票產生器,好像還有好大一段路要走!?其實並沒有。今天就讓我們把色票的部分實作出來,並且學習使用 each 邏輯區塊吧。
  為了要實作色票的部分,考量到分工的情形,我們也另外準備一個 Svelte 元件 Palettes.svelte 來專門處理色票吧。這個 Svelte 元件的位置一樣是在 /src/lib/ 資料夾裡頭,內容我們就先這樣寫寫看:
/src/lib/Palettes.svelte
<script>
  import unlock from "../assets/unlock.svg";
  let palettes = ['ff4000', '32e6e3', '009fe9'];
</script>
<div class="palettes">
  <!-- 第一張色票 -->
  <div class="card">
    <div class="palette" style='background: #{palettes[0]}' />
    <div class="hex-code">
      <p>
        {palettes[0]}
      </p>
    </div>
    <div class="lock-icon">
      <img src={unlock} alt="color-unlocked" />
    </div>
  </div>
  <!-- 第二張色票 -->
  <div class="card">
    <div class="palette" style='background: #{palettes[1]}' />
    <div class="hex-code">
      <p>
        {palettes[1]}
      </p>
    </div>
    <div class="lock-icon">
      <img src={unlock} alt="color-unlocked" />
    </div>
  </div>
  <!-- 第三張色票 -->
  <div class="card">
    <div class="palette" style='background: #{palettes[2]}' />
    <div class="hex-code">
      <p>
        {palettes[2]}
      </p>
    </div>
    <div class="lock-icon">
      <img src={unlock} alt="color-unlocked" />
    </div>
  </div>
</div>
  而關於 Palettes.svelte CSS 的設定部分,程式碼放在文末附錄,這邊就先專注 Javascript 跟 HTML 的元素。
第二行:import unlock from "../assets/unlock.svg";
  引入一個我們想要用的 svg 檔案。這是參考自 Font Awesome 當中代表解鎖這個意象的 SVG 圖檔。該檔案的出處可以參考這裡,而詳細內容請見文末附錄。
第三行:let palettes = ['ff4000', '32e6e3', '009fe9'];
  宣告變數 palettes,將我們需要的色票 hex 色碼集合成一個陣列。
第六行:<div class="palettes">
  將所有的色票放在這個 <div> 當中,作為色票容器 (container) 的一個 <div>。
第九行:<div class="card">
  每一張 <div class="card"> 代表一個顏色的色票。這是第一張色票,也就是 'ff4000'。
第十行:<div class="palette" style='background: #{palettes[0]}' />
  直接用 CSS 行內 (inline) 設定,將背景顏色藉由 style='background: #{palettes[0]}' 設定為第一張色票的顏色。
第十三行:{palettes[0]}
  同時將 hex 色碼以文字的方式呈現出來。
第二十二行:<div class="card">
  第二張色票,也就是 '32e6e3'。
第二十三行:<div class="palette" style='background: #{palettes[1]}' />
  直接用 CSS 行內 (inline) 設定,將背景顏色藉由 style='background: #{palettes[1]}' 設定為第二張色票的顏色。
第二十六行:{palettes[1]}
  同時將 hex 色碼以文字的方式呈現出來。
第三十五行:<div class="card">
  第三張色票,也就是 '009fe9'。
第三十六行:<div class="palette" style='background: #{palettes[2]}' />
  直接用 CSS 行內 (inline) 設定,將背景顏色藉由 style='background: #{palettes[2]}' 設定為第三張色票的顏色。
第三十九行:{palettes[2]}
  同時將 hex 色碼以文字的方式呈現出來。
  並且在 App.svelte 引入這個熱騰騰剛出爐的 Palettes.svelte:
/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 count = 87;
  let showModal = false;
  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 (!(tobeCount > 87)) count = tobeCount;
    else showModal = true;
  }
</script>
<main>
  <!-- 在 HTML 當中直接嵌入 Counter -->
  <Counter {count} on:changeCount={handleClick}/>
  <Palettes />
  <p class='comment'>Check out <a {href}>Svelte Tutorial</a>, the awesome article powered by {sparkle(someState)}!</p>
</main>
<Modal {showModal} on:closeModal={() => showModal = false}/>
第五行:import Palettes from './lib/Palettes.svelte';
  引入 Palettes.svelte。
第三十二行:<Palettes />
  使用 <Palettes />。

圖一、色票出現啦!
  色票出現是出現了,但是回頭看看我們的 Palettes.svelte 這個檔案。在 HTML 的段落,我們寫下一大堆程式碼做出三張色票,實在是挺沒效率的。想想看,用這個方法也許可以做出三張色票,但色票的數目如果繼續增加,來到四張、五張、六張,甚至如果色票的數量是由使用者端的操作而動態改變的話呢?
each 邏輯區塊  看看 Palettes.svelte 的第三行程式碼:let palettes = ['ff4000', '32e6e3', '009fe9'];。我們已經把色票最重要的資料用 Javascript 的陣列 (Array) 表示出來了,想要根據這個陣列輕鬆做出多個色票,其實需要的只是一個跟 Javascript 陣列方法 map 或 forEach 類似的操作罷了。
  既然 Svelte 的編譯器能認我們在 HTML 段落中開啟 Javascript 的領域,那我們有沒有辦法將類似 Javascript 的邏輯施加在 HTML 元素上呢?
  都敢這麼問了,那麼答案肯定是有的。Svelte 提供我們 each 這樣的邏輯運作,讓我們能從 Javascript 的陣列變出一連串相關的 HTML 元素。就來看看要怎麼做吧:
{#each array as item}
  <!-- 請放入想要重複的模板 -->
{/each}
第一行:{#each array as item}
  用 {#each} 開啟 each 運作邏輯,array 是代表 Javascript 陣列的變數,item 則是代表陣列當中各別項目的變數。
第二行:<!-- 請放入想要重複的模板 -->
  開始撰寫我們需要的 HTML 模板。還記的我們可以用大括號 {} 在 HTML 當中展開 Javascript 的領域吧。這邊我們就可以用大括號 {} 將先前宣告的變數 item 拿來使用。
第三行:{/each}
  用 {/each} 結束 each 的運作邏輯。
  沒錯就是這麼簡單。在 HTML 的段落中,用 {#each} 代表 each 運作邏輯的開頭,中間放入我們需要重複出現的 HTML 模板,最後用 {/each} 結束 each 運作邏輯。
  這樣講還是太抽象了嗎?那就讓我們實際在 Palettes.svelte 嘗試看看:
/src/lib/Palettes.svelte
<div class="palettes">
  <!-- 用 each 根據 palettes 這個陣列做出所有色票 -->
  {#each palettes as palette}
    <div class="card">
      <div class="palette" style='background: #{palette}' />
      <div class="hex-code">
        <p>
          {palette}
        </p>
      </div>
      <div class="lock-icon">
        <img src={unlock} alt="color-unlocked" />
      </div>
    </div>
  {/each}
</div>
因為 Javascript 跟 CSS 的部分都沒有做修改,這邊就先略過,著重在 HTML 的部分。
第一行:<div class="palettes">
  同樣放在這個 <div> 當中。
第四行:{#each palettes as palette}
  用 {#each} 開啟 each 邏輯段落。其中 palettes 是我們在 Javascript 已經宣告過,代表不同顏色的陣列變數。而 palette 則是新宣告的變數,用來表示陣列當中的各別項目。這個新宣告的變數名稱怎麼取就看我們決定,就像使用 Javascript 的陣列方法 forEach 的習慣,我們也可以簡簡單單的取名為 x。這邊我們取個有意義、增加程式碼可讀性的,既然是代表 palettes 陣列當中的各別項目,那就取為 palette 吧。
第六行:<div class="palette" style='background: #{palette}' />
  直接在 HTML 元素當中用大括號 {} 開啟 Javascript 的領域,並且放入新變數 palette。
第九行:{palette}
  同樣的,直接在 HTML 元素當中用大括號 {} 開啟 Javascript 的領域,並且放入新變數 palette。
第十六行:{/each}
  結束 each 的邏輯段落。
這麼一來就完成了。重新看看我們的專案,是不是跟圖一慢慢刻出來的頁面長得一模一樣呢?
  當然,就像在 Javascript 的陣列方法 map 或 forEach 一樣,除了有最基本代表陣列各別項目的變數可以使用之外,我們也能夠在 {#each} 這個運作邏輯當中使用代表各個項目索引 (index) 的變數:
{#each array as item, index}
  <!-- 請放入想要重複的模板 -->
{/each}
  這就是今天關於 each 運作邏輯的介紹,相關的程式碼可以在 Github 資源庫找到。明天就讓我們繼續圍繞著 Svelte 提供的 each 邏輯段落,將我們需要的色票產生器一步一步打造出來吧。
Palettes.svelte  這是今天 Palettes.svelte 最終的成果,包含 CSS 設定的部分:
/src/lib/Palettes.svelte
<script>
  import unlock from "../assets/unlock.svg";
  let palettes = ['ff4000', '32e6e3', '009fe9'];
</script>
<div class="palettes">
  <!-- 用 each 根據 palettes 這個陣列做出所有色票 -->
  {#each palettes as palette}
    <div class="card">
      <div class="palette" style='background: #{palette}' />
      <div class="hex-code">
        <p>
          {palette}
        </p>
      </div>
      <div class="lock-icon">
        <img src={unlock} alt="color-unlocked" />
      </div>
    </div>
  {/each}
</div>
<style>
  .palettes {
    margin: 1em;
    flex: 1;
    display: grid;
    gap: 0.5em;
  }
  .card {
    padding: 0.5em;
    background: var(--color-bg-3);
    border-radius: var(--border-radius);
    display: grid;
    grid-template-columns: 1fr 2fr 0.5fr;
    align-items: center;
  }
  .card .palette {
    height: 100%;
    border-radius: 0.2em;
  }
  .card .lock-icon {
    display: flex;
    width: 3em;
    height: 3em;
    padding: 0.7em;
    opacity: 0.3;
    cursor: pointer;
  }
  .card .lock-icon:hover {
    opacity: 0.5;
  }
</style>
unlock.svg/src/assets/unlock.svg
<svg xmlns="http://www.w3.org/2000/svg" height="1em" viewBox="0 0 448 512"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M144 144c0-44.2 35.8-80 80-80c31.9 0 59.4 18.6 72.3 45.7c7.6 16 26.7 22.8 42.6 15.2s22.8-26.7 15.2-42.6C331 33.7 281.5 0 224 0C144.5 0 80 64.5 80 144v48H64c-35.3 0-64 28.7-64 64V448c0 35.3 28.7 64 64 64H384c35.3 0 64-28.7 64-64V256c0-35.3-28.7-64-64-64H144V144z"/></svg>