bind(二)「那兒的資料都綁定完成了嗎?」他焦慮地問道。
「對的,都綁定好了。」我猶豫了一下:「你還是回家好好休息休息一下吧。」
他只是搖了搖頭。
「我想在這兒等到黛西睡了。晚安,夥伴。」~節錄自《The Great Svelte:第七章》
<input type='text'> 元素資料綁定<input type='checkbox'> 元素資料綁定bind:this HTML 元素綁定  昨天我們認識了 Svelte 的資料綁定 bind 語法,讓資料的流通,從原本的單向,也就是父元件 (parent component) 向子元件 (child component) 傳遞,變成雙向流通。藉由 bind,不僅父元件 (parent component) 當中的資料改動會影響子元件 (child component),任何子元件當中資料的改動,也會直接傳回父元件。
<input type='text'> 元素資料綁定  事實上,bind 能做到的資料綁定不僅存在於不同元件之間,就連元件的內部 HTML 元素,都可以藉由資料綁定的特性來簡化我們的程式碼。舉例來說,在專案當中實作一份供使用者填寫的表格時,往往會希望使用者填寫的內容可以直接連動到專案的狀態,所以就會變成:
<script>
  let sate = 'someValue';
  const handleInput = (e) => state = e.target.value;
</script>
<input type='text' value={state} on:input={handleInput}/>
第二行:let sate = 'someValue';
  宣告專案的狀態 state。這個狀態可以透過使用者從 <input> 輸入的內容而做修改。
第三行:const handleInput = (e) => state = e.target.value;
  為了要連動專案的狀態與使用者輸入的內容,我們實作一個函式 handleInput。
第六行:<input type='text' value={state} on:input={handleInput}/>
  藉由 value={state},將專案的狀態傳達給 <input>,同時利用 on:input={handleInput},讓使用者可以透過修改 <input> 的值來更新專案的狀態,藉此達到連動。
  這樣寫起來並沒有錯,邏輯縝密,思路清晰。唯一的問題是,有點太麻煩了。如果沒有要做特殊的運算或觸發不同的事件,單純只是想要藉由 <input> 值的改變來直接更新專案的狀態時,可以使用我們昨天介紹的 bind。這時候程式碼就能夠簡化變成:
<script>
  let state = 'someValue';
  // const handleInput = (e) => state = e.target.value;
</script>
<input type='text' bind:value={state} />
第三行:// const handleInput = (e) => state = e.target.value;
  有了 bind 做資料綁定,就不再需要用到事件處理器了。
第六行:<input type='text' bind:value={state} />
  直接利用 bind:value={state},將 <input> 當中的值與專案的狀態綁定在一起。不僅專案的狀態可以影響 <input> 的值,<input> 的值也可以直接影響專案的狀態。
  經過這樣子的說明,有沒有覺得 bind 真好用呢?從此寫程式碼不在漏東漏西。那就讓我們實際在專案當中練習看看囉。既然有了 bind,就讓我們給使用者更多互動的空間吧。今天練習的內容就是實作出讓使用者可以直接輸入色碼來更改色票顏色的功能。因為有了 bind,我們只需要專注在 Palettes.svelte 這個檔案當中就好:
/src/lib/Palettes.svelte
<div class="palettes">
  {#each palettes as { hex, id, locked } (id)}
    <div class="card">
      <div class="palette" style="background: #{hex}" />
      <div class="hex-code">
        <input type='text' size=10 bind:value={hex} >
        <p>
          {#await getHexName(hex)}
            wait...
          {:then name}
            {name}
          {/await}
        </p>
      </div>
      <!-- svelte-ignore a11y-no-static-element-interactions a11y-click-events-have-key-events -->
      <div class="lock-icon" on:click={() => (locked = !locked)}>
        {#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>
  有了 bind 的關係,我們不需要寫更多的 Javascript 去實作出 on:input 的事件處理器,所以 Javascript 的部分就忽略不贅述,只著重在 HTML 元素的部分。
<input type='text' size=10 bind:value={hex} ><input>,並且用 bind:value={hex} 來讓 <input> 當中的值能夠跟 palette 這個狀態的 hex 綁定在一起。
圖一、請直接輸入想要的 hex 色碼吧
<input type='checkbox'> 元素資料綁定  HTML 所提供的 <input> 類型百百種,所有的 <input> Svelte 都有方法可以 bind。
<input bind:value={state} />
<textarea bind:value={state} />
<input type="number" bind:value={state} />
<input type="range" bind:value={state} />
<input type="checkbox" bind:checked={booleanState} />
  比較特別的是,對於 <input type="checkbox"> 來說,要做資料綁定的是 checked 這個屬性。如果綁定的狀態 booleanState 結果為 true,<input type="checkbox"> 就會被勾選。勾選/未勾選,這兩種狀態的切換,其實就跟我們色票的上鎖/解鎖相同,不如就讓我們重新修改上鎖/解鎖的程式碼,改以 <input type="checkbox"> 的方式重新撰寫看看吧。同樣的,因為我們已經用 bind 做資料綁定了,只需要更改 Palettes 的內容即可:
/src/lib/Palettes.svelte
<div class="palettes">
  {#each palettes as { hex, id, locked } (id)}
    <div class="card">
      <div class="palette" style="background: #{hex}" />
      <div class="hex-code">
        <input type='text' size=10 bind:value={hex} >
        <p>
          {#await getHexName(hex)}
            wait...
          {:then name}
            {name}
          {/await}
        </p>
      </div>
      <!-- svelte-ignore a11y-no-static-element-interactions a11y-click-events-have-key-events -->
      <div class="lock-icon">
        <label>
          <input type='checkbox' bind:checked={locked}>
          {#if locked}
            <img src={lock} alt="color-locked" />
          {:else}
            <img src={unlock} alt="color-unlocked" />
          {/if}
        </label>
      </div>
    </div>
  {:else}
    <div class="card no-color">
      <div>
        <p>No color to show. Please add a color.</p>
      </div>
    </div>
  {/each}
</div>
完整的 HTML 段落如上面所示,不過我們真正修改的段落只有一小段,如果專注在這一小段程式碼的話:
<div class="lock-icon">
  <label>
    <input type='checkbox' bind:checked={locked}>
    {#if locked}
      <img src={lock} alt="color-locked" />
    {:else}
      <img src={unlock} alt="color-unlocked" />
    {/if}
  </label>
</div>
第一行:<div class="lock-icon">
  因為 <input type='checkbox'> 特性的關係,我們不再需要使用 on:click 事件處理器來更改 locked 的狀態,而是可以直接將 locked 的狀態跟 <input type='checkbox'> 的 checked 綁定在一起。
第三行:<input type='checkbox' bind:checked={locked}>
  用 bind:checked={locked} 將這兩個狀態綁定在一起。不僅 locked 的狀態可以影響 <input type='checkbox'> 的  checked 與否,另一方面,改變 checked 也能夠直接更新 locked 的值。
之後在 CSS 的段落中加上一些修飾的設定:
/src/lib/Palettes.svelte
<style>
.card .lock-icon input {
    display: none;
  }
  .card .lock-icon img {
    height: 100%; 
  }
</style>

圖二、用勾選/未勾選來表達上鎖/解鎖吧
bind:this HTML 元素綁定  最後介紹一下 bind 的有趣功能。bind 除了可以綁定資料狀態之外,還可以用 bind:this 來綁定 HTML 元素本身。
<script>
  let div;
</script>
<div bind:this={div} />
第二行:let div;
  宣告一個變數 div。
第四行:<div bind:this={div} />
  接著在 HTML 的 <div> 元素內,直接用 bind:this={div},將這個元素跟 Javascript 的變數 div 綁定在一起。之後就可以在 Javascript 當中用 div 來獲取 HTML 元素的狀態或是對其進行操作。其實就是精簡版的 let div = document.querySelector() 的概念。只是在 Svelte 當中,如同第 21 天:Svelte 的生命週期函式:onMount的討論,我們需要將 let div = document.querySelector() 這一類的程式碼放進 onMount 裡面,在 Svelte 元件加載進 DOM 之後才去執行。若是透過 bind:this ,則可以省去這些考量。
  沒錯,所以我們今天就要用 bind:this 再一次改寫 Modal.svelte:
/src/lib/Modal.svelte
<script>
  import { onMount } from "svelte";
  import { createEventDispatcher } from "svelte";
  export let showModal;
  let dialog;
  const dispatch = createEventDispatcher();
  const handleClose = () => dispatch("closeModal");
  // onMount(() => {
  //   dialog = document.querySelector("dialog");
  // });
  $: if (dialog && showModal) dialog.showModal();
  $: if (dialog && !showModal) dialog.close();
</script>
<!-- svelte-ignore a11y-click-events-have-key-events a11y-no-noninteractive-element-interactions -->
<dialog bind:this={dialog} on:close={handleClose} on:click={handleClose}>
  <!-- svelte-ignore a11y-no-static-element-interactions -->
  <div on:click|stopPropagation>
    <div class="title">
      <h2>Forbidden</h2>
      <small><em>adjective</em> for·bid·den \fər-ˈbi-dᵊn \</small>
    </div>
    <hr />
    <div class="content">
      <p>
        You have no privilidge to enter domain beyond 87. Please register as
        member so we can sent you some spam email.
      </p>
    </div>
    <hr />
    <!-- svelte-ignore a11y-autofocus -->
    <div class="footer">
      <button autofocus on:click={handleClose}>close modal</button>
    </div>
  </div>
</dialog>
第十行: // onMount(() => {
  因為我們打算改用 bind:this 來將 <dialog> 綁定到 Javascript 變數 dialog 上,所以這一段就不需要了。
第十九行:<dialog bind:this={dialog} on:close={handleClose} on:click={handleClose}>
  用 bind:this={dialog},將 <dialog> 綁定到 Javascript 變數 dialog 上。剩餘的內容不需要多做改變。

圖三,好久不見的 Forbidden
那麼今天關於 bind 的介紹就到這邊了,謝謝大家!