์์ฒ๋ผ ๋จ์ํ๊ฒ to do ๋ฆฌ์คํธ๋ฅผ ์ถ๊ฐํ๊ณ , ํธ์งํ๊ณ , ์๋ฃํ๋ ๊ธฐ๋ฅ์์, ์๋์ ์นดํ ๊ณ ๋ฆฌ ํธ์ง ๊ธฐ๋ฅ์ ๋ฃ์๋ค.
์นดํ ๊ณ ๋ฆฌ ์ถ๊ฐ ๊ธฐ๋ฅ์ ์ํ UI
์ Nav Bar์ Daily Report์์๋ ๊ฐ๋จํ ๋ฐ์ดํฐ๋ฅผ ํ์ด ์ฐจํธ, ๋ผ์ธ ์ฐจํธ๋ก ์๊ฐํํด ๋ณด์ฌ์ฃผ๋๋ฐ, ๊ทธ๋๋ฅผ ์ํ ์ ์ง์ ์ด ํ์ํด <input type="color"> ํ๊ทธ์ ์ปฌ๋ฌ ํผ์ปค๋ฅผ ์ฒ์ ์จ๋ดค๋ค.
๊ธฐ์กด input ์ฐฝ์์ ๊ธ ์ฐ๊ณ ์ํฐ๋ฅผ hit ํ๋ฉด ์๋ก์ด ์นดํ ๊ณ ๋ฆฌ๊ฐ ๋ฑ๋ก๋๋ ๊ฒ์์ ์ปฌ๋ฌ๋ ํจ๊ป ์ ์ฅํ ์ ์๊ฒ ํ๋ค. @keyup.enter="addCate()" ๋ฉ์๋๋ฅผ ์ผ๋ค.
์ปจํธ๋กค ํจ๋ ๋ถ๋ถ์ table๋ก ์งฐ๋๋ฐ, ๋ ๋ฒ์งธ row์ธ <tr> ๋ด๋ถ์์ select์ ๋ํ v-for ๋๋ฌธ์ ์ด์ง ์ ๋ฅผ ๋จน์๋ค. ์ ์ํ์ ๊น๋ํ UI๋ฅผ ๊ทธ๋๋ก ์งํํ๋ ค๋๋ฐ ๊ตฌ์ฑ์ด ์๋์ ๊ฐ์๊ธฐ ๋๋ฌธ์ด๋ค.
<tr v-for="(item, index) in categories." :key="index">
<td><label /></td>
<td><input /><td>
<td><label /></td>
<td>
<select>
<option v-for="(cate, idx) in categories" :key="idx" :value="cate.title" :label="cate.title">
</select>
<input />
</td>
๊ฐ๋ตํ๊ฒ ์ด๋ฐ ๊ตฌ์กฐ์ธ๋ฐ, select ํ๊ทธ ๋ด option์ v-for๋ก ํ์ฌ๊น์ง ์ ์ฅ๋ categories ์ ๋ฐฐ์ด์ ๋ฐ์์ ๋ฟ๋ ค์ฃผ๋ ๊ฒ๊น์ง๋ ์ข์๋๋ฐ, v-for๊ฐ ์ ์ฉ๋์ง ์๋ ๊ณณ์์ cate๋ฅผ ๊ฐ์ง๊ณ ์ฌ ์๊ฐ ์์๋ค. ํ ์ ์์ด <td> ๋ด๋ถ์ <select>๋ ๊ทธ๋๋ก ๋๊ณ <tr>์์ ๊ฐ์ ๋ฐ์ดํฐ๋ฅผ ๊ฐ์ง๊ณ v-for๋ฅผ ํ ๋ฒ ๋ ์ผ๋ค.
์ด๋ฐ ๊ฒฝ์ฐ์๋ <tr>์ด ๋ฐ๋ณต๋๋ฏ๋ก, ์๋์ฒ๋ผ ๋ฐฐ์ด์ slice ํด์ค ํ์๊ฐ ์๊ธด๋ค.
<tr v-for="(item, index) in categories.slice(this.sliceNum, this.sliceNum + 1)" :key="index">
๊ทธ๋ฌ๋ฉด ํ๋ฉด์์๋ ์๊น์ฒ๋ผ ๋ฑ ํ์ํ ํ ์ค๋ง ์ป์ ์ ์๊ฒ ๋๋ค. 0, 1์ด ์๋ sliceNum ๋ณ์๋ฅผ ๋ฃ์ด์ค ๊ฒ์ ์นดํ ๊ณ ๋ฆฌ๋ฅผ ํด๋ฆญํ์ ๋ ์ด <tr>๋ฅผ ๋์ ์ผ๋ก ๋ฐ๊ฟ์ฃผ๊ธฐ ์ํด์์๋ค. <select>์ <option> ๋ด๋ถ์๋ :value๋ :label ๋ฑ์ ๋๋ ํฐ๋ธ๊ฐ ์ฌ๋ฌ ๊ฐ๊ฐ ์์ง๋ง, v-for๋ฅผ ์ค์ฒฉ์ผ๋ก ์ธ ์ ์์์ผ๋ฏ๋ก, <select> ํ๊ทธ์ @chage ์ด๋ฒคํธ๋ฅผ ๊ฑธ์ด ๋ณํ๊ฐ ์๊ฒผ์ ๋ changeCateInfo($event) ๋ฉ์๋๋ฅผ invoke ์์ผฐ๋ค.
changeCateInfo(event: any) {
const currentTitle = event.target.value;
for (let i = 0; i < this.categories.length; i++) {
if (this.categories[i].title === currentTitle) this.sliceNum = i;
}
},
this.categories๋ Vuex๋ฅผ ํตํ ์ํ ๊ด๋ฆฌ๋ฅผ ์ํ store.js์ ์ ์ฅํด๋ state์ ํ๋์ธ๋ฐ, ์ ์ฅํด๋ ๋ฐฐ์ด์ ๊ฐ์ง๊ณ ์ค๋ ๋ณ์์ด๋ค. this.categories ๊ธธ์ด๋งํผ ๋ฐ๋ณตํ๋ฉด์ event.target.value์ title์ด ์ผ์นํ ๋ ํด๋น ์ธ๋ฑ์ค๋ฅผ sliceNum์ ๋ฃ์ด์ฃผ๊ธฐ๋ก ํ๋ค.
๊ทธ๋ฌ๋ฉด <tr> ๋ฐ์ color picker๋ ํด๋น ์นดํ ๊ณ ๋ฆฌ ํ์ดํ์ ๋ง๋ ์์ ์ถ๋ ฅํด์ค ์ ์๊ฒ ๋๋ค.
๋ค๋ง ์ฌ๊ธฐ์ ๋ <select>์ <option>์ ๋ผ๋ฒจ์ด ์ ๋๋ก ํ๊ธฐ๊ฐ ์ ๋๋ ๋ฌธ์ ์ ์ด ์๊ธฐ๋๋ฐ ํ์ฐธ ํค๋งค๋ค๊ฐ :required ๋๋ ํฐ๋ธ์ :selected๋ก ํด๊ฒฐ์ ๋ดค๋ค.
<select
name="categories"
id="categories"
class="w-1/3 mr-3 pl-2 border rounded border-solid border-primary-lightgray"
@change="changeCateInfo($event)"
:required="true"
>
<option
v-for="(cate, idx) in categories"
:key="idx"
:value="cate.title"
:label="cate.title"
:selected="cate.title === item.title"
>{{ cate.title }}</option
>
</select>
๋ ๋๋ ํฐ๋ธ ๋ชจ๋ boolean ๊ฐ์ ๋ฐ๋๋ค. ๊ทธ๋ฌ๋ฉด ์ค์ฒฉ๋ ๋ v-for์ item๊ณผ cate ๊ฐ์ฒด๋ฅผ ๋ชจ๋ ๊ฐ์ง๊ณ ์ ์๋ก์ title์ ๋น๊ตํ๊ณ , ์ผ์นํ๋ค๋ฉด true๋ฅผ ๋ฐํํด ํด๋น ๋ผ๋ฒจ์ ๋ณด์ฌ์ค ์ ์๊ฒ ๋๋ค.
Store
์นดํ ๊ณ ๋ฆฌ ์ญ์ store์ ์ ์ฅํด state๋ก ๊ด๋ฆฌํ๊ณ , localStorage์ ์ ์ฅํด์ ์ฌ์ฉํ ์ ์๊ฒ ํ๋ค. ๋ผ๋ฒจ์ ์ง์ ํ๊ณ ์ปฌ๋ฌ๋ฅผ pickํ๊ฑฐ๋ ์ปฌ๋ฌ๋ฅผ ๋ณ๊ฒฝํ์ง ์๊ณ ํ์ดํ๋ง ์์ฑํ๊ณ ์ํฐ๋ฅผ hitํ๋ฉด this.$store.commit("<mutation ์ด๋ฆ >", payload) store๋ก ์ปค๋ฐ์ ํ๋๋ก ํ๋ค.
changeExistingCate(item: Category, event: any, param: string) {
const cateIndex = this.categories.indexOf(item);
if (param === "color") {
this.$store.commit("modifyCate", {
backgroundColor: event.target.value,
index: cateIndex
});
} else {
this.$store.commit("modifyCate", {
title: event.target.value,
index: cateIndex
});
}
},
chageExistingCate๋ ๋ ๋ฒ ์ฐ์ด๋๋ฐ, ํ๋๋ param์ด 'color'์ผ ๋ ํ๋๋ 'title'๋ก ๋ค์ด์ฌ ๋์ด๋ค. commit ํด์ค ๋์ paylaod๊ฐ ๊ฐ๊ฐ ๋ค๋ฅด๋ฏ๋ก, store.js๋ ์๋์ฒ๋ผ ์์ฑํด์ฃผ์๋ค.
// store/store.js
modifyCate(state, payload) {
const categories = this.state.categories;
const index = payload.index;
const bgColor = payload.backgroundColor;
const title = payload.title;
if (bgColor && index) {
categories[index].backgroundColor = bgColor;
}
if (title && index) {
categories[index].title = title;
}
localStorage.setItem("categories", JSON.stringify(state.categories));
}
๐ todo.vue ์ปดํฌ๋ํธ์ ๋ํ ์ ์ฒด ์์ค๋ ์ด๊ณณ์์