iT邦幫忙

2025 iThome 鐵人賽

DAY 10
0
Vue.js

新手學習Vue.js與實作之旅系列 第 10

Day10 實作Colorful Notes

  • 分享至 

  • xImage
  •  

在昨天比較條件渲染的內容中,透過實作Notes的部分程式碼來更加釐清v-if和v-show寫法的差異,今天內容將探討實作Notes的程式碼撰寫,複習先前學習過的插值表達式 {{ }} 以及v-bind、v-for、v-on、v-model等指令。

以下實作是使用Composition API撰寫,將著重講解<script setup>標籤的程式碼,以及<template>標籤內有使用到Vue語法的地方,HTML和CSS的基礎語法就不再此贅述:

1. 建立響應式資料

首先要使用 ref 創建響應式資料、定義資料變數

  • showModal: 控制新增筆記的彈窗是否顯示
  • newNote: 存放使用者輸入的筆記內容
  • errorMessage: 存放錯誤訊息
  • notes: 存放所有筆記的陣列
import {ref} from "vue";
const showModal = ref(false)
const newNote = ref("")
const errorMessage = ref("")
const notes = ref([])

2. 產生隨機顏色的函數

透過使用JavaScript的產生隨機顏色語法,可以讓筆記的背景顏色看起來更豐富些,並且設定顏色皆為淺色系,產生 0-360 度的色相值、飽和度為 100%、亮度為 75%。

function getRandomColor() {
  return "hsl(" + Math.random() * 360 + ", 100%, 75%)";
}

3. 新增筆記功能

首先要限制使用者至少輸入10個字元才能順利新增筆記,否則會顯示出錯誤訊息,接著創建 id、文字內容、日期、背景色的筆記物件,最後還要設定新增筆記後會重置狀態 (關閉新增筆記的彈窗並清空輸入欄位和錯誤訊息)。

const addNote = () => {
    // 限制輸入長度至少為10個字元
    if(newNote.value.length < 10){
      return errorMessage.value = "Note needs to be 10 characters or more"
    }
    // 新增筆記到陣列
    notes.value.push({
      id: Math.floor(Math.random() * 1000000),
      text: newNote.value,
      date: new Date(),
      backgroundColor: getRandomColor()
    });
    // 重置狀態
    showModal.value = false;
    newNote.value = ""
    errorMessage.value=""
}

4. 新增筆記的彈窗介面

  • 條件渲染 v-if
    • 控制輸入文字的彈窗是否顯示,當 showModal 為 true 時才會渲染標籤內的元素。
    • 控制錯誤訊息是否顯示,當 errorMessage 為 true 時才會渲染錯誤訊息。
  • 雙向綁定 v-model
    將文字輸入區域和顯示筆記內容區域綁定,並且透過 .trim 修飾符會去除輸入內容前後的空白,可以防止使用者輸入無效的空格。
  • 事件處理 v-on (簡寫為 @ )
    • 將 Add Note 按鈕加上事件監聽,當按鈕被點擊時,會執行 addNote 的方法。
    • 將 Close 按鈕加上事件監聽,當按鈕被點擊時,會將 showModal 設為 false,就會關閉新增筆記的彈窗。
<div v-if="showModal" class="overlay">
      <div class="modal">
        <textarea v-model.trim="newNote" name="note" id="note" cols="30" rows="10">
        </textarea>
        <p v-if="errorMessage">{{ errorMessage }}</p>
        <button @click="addNote">Add Note</button>
        <button class="close" @click="showModal = false">Close</button>
      </div>
</div>

5. 顯示筆記的介面

  • 事件處理v-on (簡寫為 @ )
    將 + 號按鈕加上事件監聽,當按鈕被點擊時,會將 showModal 設為 true,就會顯示新增筆記的彈窗。
  • 列表渲染 v-for
    透過遍歷來顯示所有筆記內容,並且搭配 key 屬性當作唯一識別碼,當刪除指定的 note 時, Vue 能夠識別並移除對應的 DOM 元素,而其他 note 的 DOM 元素會保持不變,可以避免不必要的重新渲染。
  • 屬性綁定 v-bind (簡寫為 :)
    • 透過 key 屬性綁定 note.id 作為 note 的唯一識別碼,讓 Vue 能夠高效追蹤每個 note 元素
    • 透過 style 屬性來動態設定 note 的背景顏色,進行動態樣式綁定,將每個note的隨機背景顏色套用到對應的 DOM 元素上。
<div class="container">
      <header>
        <h1>Colorful Notes</h1>
        <button @click="showModal = true">+</button>
      </header>
      <div class="cards-container">
        <div v-for="note in notes" :key="note.id" class="card" 
             :style="{backgroundColor: note.backgroundColor}">
          <p class="main-text">{{ note.text }}</p>
          <p class="date">{{ note.date.toLocaleDateString("en-US") }}</p>
        </div>
      </div>
</div>

畫面呈現結果

https://ithelp.ithome.com.tw/upload/images/20250913/20169120nYmTlVnwDa.png
https://ithelp.ithome.com.tw/upload/images/20250914/20169120XbzGjcpY8U.png

最後附上完整程式碼(包含CSS美化的部分)

<script setup>
  import {ref} from "vue";
  const showModal = ref(false)
  const newNote = ref("")
  const errorMessage = ref("")
  const notes = ref([])

  //產生隨機顏色的函數
  function getRandomColor() {
  return "hsl(" + Math.random() * 360 + ", 100%, 75%)";
  }

  const addNote = () => {
    // 限制輸入長度至少為10個字元
    if(newNote.value.length < 10){
      return errorMessage.value = "Note needs to be 10 characters or more"
    }
    // 新增筆記到陣列
    notes.value.push({
      id: Math.floor(Math.random() * 1000000),
      text: newNote.value,
      date: new Date(),
      backgroundColor: getRandomColor()
    });
    // 重置狀態
    showModal.value = false;
    newNote.value = ""
    errorMessage.value=""
  }
</script>

<template>
  <main>
    <!-- 新增筆記的彈窗介面 -->
    <div v-if="showModal" class="overlay">
      <div class="modal">
        <textarea v-model.trim="newNote" name="note" id="note" cols="30" rows="10"></textarea>
        <p v-if="errorMessage">{{ errorMessage }}</p>
        <button @click="addNote">Add Note</button>
        <button class="close" @click="showModal = false">Close</button>
      </div>
    </div>
    <!-- 顯示已新增筆記的介面 -->
    <div class="container">
      <header>
        <h1>Colorful Notes</h1>
        <button @click="showModal = true">+</button>
      </header>
      <div class="cards-container">
        <div v-for="note in notes" :key="note.id" class="card" :style="{backgroundColor: note.backgroundColor}">
          <p class="main-text">{{ note.text }}</p>
          <p class="date">{{ note.date.toLocaleDateString("en-US") }}</p>
        </div>
      </div>
    </div>
  </main>
</template>

<style>
  main{
    height: 100vh;
    width: 100vw;
  }
  .container{
    max-width: 1000px;
    padding: 10px;
    margin: 0 auto;
  }
  header{
    display: flex;
    justify-content: space-between;
    align-items: center;
  }
  h1{
    font-weight: bold;
    margin-bottom: 25px;
    font-size: 75px;
  }
  header button{
    border: none;
    padding: 10px;
    width: 50px;
    height: 50px;
    cursor: pointer;
    background-color: rgb(51, 53, 53);
    border-radius: 100%;
    color: white;
    font-size: 20px;
  }
  .main-text {
    word-wrap: break-word;      
    word-break: break-word;     
    overflow-wrap: break-word;  
    hyphens: auto; 
    max-height: 220px; 
    overflow-y: auto;             
  }
  .card{
    width: 225px;
    height: 224px;
    background-color: rgb(150, 168, 217);
    padding: 10px;
    border-radius: 15px;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    margin-right: 20px;
    margin-bottom: 20px;
  }
  .date{
    font-weight: bold;
    font-size: 12.5px;
  }
  .cards-container{
    display: flex;
    flex-wrap: wrap;
  }
  .overlay{
    position: absolute;
    width: 100%;
    height: 100%;
    background-color: rgba(0,0,0,0.77);
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: center;
  }
  .modal{
    width: 750px;
    background-color: white;
    border-radius: 10px;
    padding: 30px;
    position: relative;
    display: flex;
    flex-direction: column;
  }
  .modal button{
    padding: 10px 20px;
    font-size: 20px;
    width: 100%;
    background-color: rgb(110, 179, 156);
    border: none;
    color: white;
    cursor: pointer;
    margin-top: 15px;
  }
  .modal .close{
    background-color: rgb(205, 98, 96);
    margin-top: 7px;
  }
  .modal p{
    color: red;
  }
</style>

參考資源

https://stackoverflow.com/questions/23601792/get-only-light-colors-randomly-using-javascript
https://www.youtube.com/watch?v=I_xLMmNeLDY


上一篇
Day9 Vue指令- 條件渲染
下一篇
Day11 Vue 計算屬性
系列文
新手學習Vue.js與實作之旅11
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言