這是〈Svelte 從小白到入門〉系列的第四集,各集連結如下:


在前面的集數裡,我們遇到的 JS 與 HTMl 連動的關係都是單向的,也就是某個事件引發 JS 函式,而函式改變了某個變數的值,變數的值又反映在 HTML 中,但在那些 HTML 表單應用中,往往是要雙向連動的,而 Svelte 也提供了這樣的機制。

數據綁定

在下面的示例中,文字框和文字是要連動的,套用過去的經驗,會把 <input> 綁上 on:input 的事件,再製做相關的函式更新文字部份,但 Svelte 提供了另外的 bind: 語法讓這件事變得更簡單而直覺:

我們利用 bind:value={name} 這句讓 <input>value 屬性和 JS 的 name 的值連動,並且是雙向的連動,不僅用戶在文字框輸入會改變同時改變兩者的值,並且如果是 JS 的 name 的值有被改動到,也會連動到 <input>value

群組表單元素

HTML 的表單元素除了輸入框外,還有單選的 radio input 和多選的 checkbox input,Svelte 提供了 bind:group 語法來對付他們,以 radio input 為例:

<input type=radio bind:group={scoops} name="scoops" value={1}>One scoop
<input type=radio bind:group={scoops} name="scoops" value={2}>Two scoops

他們都和 JS 的 scoops 連動,而 scoops 的值則是來自 value,選 One scoop,則 JS 的 scoops 將會是 1,以此類推。

Checkbox input 也是同樣的作法,差別在多選的值在 JS 內會是以陣列的結構儲存。

完整的例子如下:

Textarea

正規的 HTML <textarea> 是把內容放在開關標籤中間:

<textarea>Some words are *italic*, some are **bold**</textarea>

但 Svelte 要做數據綁定的話,則是和 <input> 一樣用 bind:value 當作屬性放在 <textarea> 內:

下拉選單

在 HTML 中,一個典型的下拉選單是由一個 <select> 與多個 <option> 構成的:

<select>
    <option>Where did you go to school?</option>
    <option>What is your mother's name?</option>
</select>

在 Svelte,則是把 bind:value 加到 <select> 內,至於 <option>,可以用原本的 HTML 方式,但往往我們也會把它和 JS 的變數綁定在一起,像這樣:

上面的範例中,<option> 是和 JS 的 questions 綁定,比較奇怪的點是 questions 陣列內都是 object,而 <option>value 竟可照吃不誤,因為「Svelte doesn’t mind」。

而真正用到雙向綁定的是 <select>,透過 bind:value={selected} 與 JS 的 selected 綁定,依照瀏覽器的邏輯,預設會幫我們帶入 <option> 的第一個項目,也就是「Where did you go to school?」,而在 JS 這邊,因為雙向綁定,selected 會是<option>value,也就是 { id: 1, text: 'Where did you go to school?' } 這個物件。

其他錦上添花的部份:

  • <select> 內的小函式,令選項改變時,清空答案框
  • <form> 裡面加了 preventDefault 的修飾器,防止頁面重載

多選選單

在 HTML 把 <select> 加上 multiple 屬性即為多選,拿前面的 checkbox 範例改造一下:

和單選的差別只在於多選的項目反應到 JS 上會是陣列的形式。

Contenteditable

賦予 contenteditable 屬性的元素,可以用 bind:innerHTMLbind:textContent 的方式綁定:

陣列與迴圈

在下面的例子裡,JS 的 todos 被放進 HTML 內做迴圈,迴圈內也可以針與 todos 的成員(todo)做綁定:

至於那句陌生的樣式語法 class:done={todo.done} 在此先略過,如果本系列沒有斷尾的話會再提到。

這個範例也是前端框架最常見到的 to-do list 應用,就這樣不知不覺的做完了。:)

多媒體元素

<video><audio> 這類多媒體,Svetle 提供更多綁定的對象:

  • duration (readonly) — the total duration of the video, in seconds
  • buffered (readonly) — an array of {start, end} objects
  • seekable (readonly) — ditto
  • played (readonly) — ditto
  • seeking (readonly) — boolean
  • ended (readonly) — boolean
  • currentTime — the current point in the video, in seconds
  • playbackRate — how fast to play the video, where 1 is ‘normal’
  • paused — this one should be self-explanatory
  • volume — a value between 0 and 1
  • muted — a boolean value where true is muted

Videos additionally have readonly videoWidth and videoHeight bindings.

以上偷懶不解釋。:p

尺度

對於 HTMl 區塊類元素可以透過綁定機制取得 clientWidthclientWidthoffsetWidthoffsetHeight,不過就如同原始的 web api 那樣,這些值也是唯讀的,只能單向讀取。

關於 clientWidthoffsetWidth 的差異,可以看〈一次搞懂 clientHeight/clientWidth/offSetHeight/offsetWidth/scrollHeight/scrollWidth/scrollTop/scrollLeft 的區別〉。

參見下面的例子:

裡面利用 range input 數值綁定的方式,動態變更字大小,以及利用前述的方式取得文字容器的寬高。

This

bind:this 令綁定的對象參照到元素(或元件)自身,取得元素自身的參照後,就能在 JS 區域去操作該元素。

看文字可能不好理解,實例如下:

我們用 <canvas bind:this={canvas}> 將 HTML 元素 <canvas> 參照到 JS 變數 canvas 上,或者我們可以把這件事理解為:

canvas = <canvas>

當然上面這行不是正確的語法,但就是這個意思。

canvas<canvas> 元素後,就可以在 JS 內操控它,範例內剩下九成的程式碼是一系列 Canvas 花式操作,本人也是不懂裝懂的程度,請看〈Canvas 教學文件

範例中的 onMount() 的部份,容後敘明,只要本系列沒有停更…。

元件綁定

綁定機制不僅可用在原生的 HTML 元素上,也可用在 Svelte 元件上。

下面的例子有 App 父元件及子元件 Keypad,就是那令人又愛又恨的數字小鍵盤。

App 負責顯示遮蔽後的密碼、Keypad 負責顯示數字鍵盤,用戶按下的數字會以字串的形式存在 Keypad 的 value,而 App 的 pin 又綁定到 Keypad 的 value props,使 pin 可以得到 value 的值,最後再加上一個簡單的遮蔽函式,因此得以即時反應用戶按下的數字在密碼遮蔽區。

另一方面,用戶在 Keypad 按下 submit 後分派給父元件 App,而 App 接收到 submit 事件後觸發 handleSubmit() 彈出提示框。

綁定機制還可以直接用在元件上。

在下面的例子裡,用 <InputField bind:this={field}> 這句把調用的這份 InputField 元件(instance)與 field 變數綁定,並且 InputField 又暴露了 focus(),因而我們可以用 field.focus() 的形式執行 InputField instance 的 focus()

小結

看了各種花式 bind: 是不是越來越混亂了呢?別擔心,我也是。:p