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>