App.vue
<script>
export default {
data: function () {
return {
todoItems: []
}
},
methods: {
// 각각의 컴포넌트에서는 표현만 하고, 실질적인 데이터 처리는 App.vue 에서 하고 있다.
/..
clearAllItems: function () {
localStorage.clear()
// todoItems의 배열을 초기화해서 버튼을 누르는 순간, 화면에 리스트가 사라짐
this.todoItems = []
},
sortLatest () {
this.todoItems.sort(function (a, b) {
return b.time - a.time
})
},
sortOldest () {
this.todoItems.sort(function (a, b) {
return a.time - b.time
})
},
sortAllItem (selectedSort) {
console.log(selectedSort)
if (selectedSort.value === 'date-desc') {
this.sortLatest()
} else if (selectedSort.value === 'date-asc') {
this.sortOldest()
}
}
},
../
mounted () {
this.sortOldest()
},
components: {
'TodoHeader': TodoHeader,
'TodoInput': TodoInput,
'TodoList': TodoList,
'TodoFooter': TodoFooter
}
}
</script>
TodoFooter.vue
<script>
export default {
data () {
return {
selected: 'date-asc'
}
},
methods: {
sortTodo () {
this.$emit('sortItem', {value: this.selected})
},
clearTodo: function () {
this.$emit('clearAll')
}
}
}
</script>
인자를 같이 보내지는 않음 이벤트만 발생시키면 App.vue에서
<todo-footer v-on:sortItem="sortAllItem" v-on:clearAll="clearAllItems"></todo-footer>
v-on으로 이벤트를 받아서 현재 컴포넌트 메서드에서 실행시키면 됌.
여기서 문제는 화면을 업데이트 해주는 로직이 빠짐.
this.todoItems = []
를 추가해주면 todoItems 를 빈 배열로 만들어준다 (할 일 목록 모두 제거)
모달은 한군데서만 쓰는게 아니라 여러군데서 쓸 거임.
특정 컴포넌트를 모듈화하는 방법을 배워보자.
먼저 우리가 모달을 사용할 부분은 인풋창에 입력값이 없을 때 경고 모달창을 띄우는 거다!
vue 2 모달 코드 안내
https://v2.vuejs.org/v2/examples/modal.html
/src/components 안에 common 이라는 새 폴더를 생성해 MyModal.vue 생성한다
그 안에 template 부분에 위 링크의 html 코드 transition 태그를 복붙한다.
css도 style태그 안에 넣어준다.
MyModal.vue
<template>
<transition name="modal">
<div class="modal-mask">
<div class="modal-wrapper">
<div class="modal-container">
<!-- 모달 헤더-->
<div class="modal-header">
<slot name="header">
default header
</slot>
</div>
<!-- 모달 바디-->
<div class="modal-body">
<slot name="body">
default body
</slot>
</div>
<!-- 모달 푸터-->
<div class="modal-footer">
<slot name="footer">
default footer
<button class="modal-default-button" @click="$emit('close')">
OK
</button>
</slot>
</div>
</div>
</div>
</div>
</transition>
</template>
TodoInput.vue
<template>
<div class="inputBox shadow">
<input type="text" v-model="newTodoItem" v-on:keyup.enter="addTodo">
<!-- <button v-on:click="addTodo">add</button>-->
<span class="addContainer" v-on:click="addTodo">
<i class="fa-solid fa-plus addBtn"></i>
</span>
<MyModal v-if="showModal" @close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<h3 slot="header">custom header</h3>
</MyModal>
</div>
</template>
vue 에서 지양하는 template의 구조에는 템플릿 태그안에 무조건 한 개의 루트만 가질 수 있다.
<script>
import MyModal from './common/MyModal.vue'
export default {
data: function () {
return {
newTodoItem: '',
showModal: false
}
},
methods: {
addTodo: function () {
if (this.newTodoItem !== '') {
// this.$emit('이밴트이름', 인자1, 인자2...)
this.$emit('addTodoItem', this.newTodoItem)
this.clearInput()
} else {
this.showModal = !this.showModal
}
},
clearInput: function () {
this.newTodoItem = ''
}
},
components: {
MyModal: MyModal
}
}
</script>
: 특정컴포넌트의 일부 ui들을 재사용할 수 있는 기능
TodoInput.vue
<template>
<div class="inputBox shadow">
<input type="text" v-model="newTodoItem" v-on:keyup.enter="addTodo">
<!-- <button v-on:click="addTodo">add</button>-->
<span class="addContainer" v-on:click="addTodo">
<i class="fa-solid fa-plus addBtn"></i>
</span>
<!-- showModal 이 true여야 보임-->
<MyModal v-if="showModal" @close="showModal = false">
<!--
you can use custom content here to overwrite
default content
-->
<!-- = 너가 원하는 컨텐츠로 재정의해서 쓸 수 있다-->
<h3 slot="header">
경고!
</h3>
</MyModal>
</div>
</template>
<script>
import MyModal from './common/MyModal.vue'
export default {
data: function () {
return {
newTodoItem: '',
// 기본값을 false로 해줘야 처음에 화면에 떴을 때 모달이 보이지 않음
showModal: false
}
},
methods: {
addTodo: function () {
if (this.newTodoItem !== '') {
// this.$emit('이밴트이름', 인자1, 인자2...)
this.$emit('addTodoItem', this.newTodoItem)
this.clearInput()
} else {
this.showModal = !this.showModal
}
},
clearInput: function () {
this.newTodoItem = ''
}
},
components: {
MyModal: MyModal
}
}
</script>
우리가 여기서 눈여겨 봐야할 것은 modal 컴포넌트에서 정의한 값을 TodoInput 컴포넌트에서 Modal을 불러와서(컴포넌트 등록) 재정의 했다는 점이다.
Modal을 불러온 컴포넌트에서 태그 안에 slot을 쓰고 MyMoal에 정의해놓은 slot 이름을 써서 재정의한다.
<h3 slot="header">Error!</h3>
<div slot="body">아무 내용도 입력하지 않았습니다!</div>
<i class="closeModalBtn fas fa-times" @click="showModal = false"></i>
@click은 풀어쓰면 v-on:click="showModal = false" 인데 @는 v-on의 축약어이다.
showModal의 값을 다시 기본값인 false로 바꿔주면 부드럽게 사라진다 (트랜지션)
TodoList.vue
<template>
<div>
<transition-group name="list" tag="ul">
<li v-for="(todoItem, index) in propsdata" v-bind:key="todoItem.item" class="shadow">
<i class="checkBtn fa-solid fa-check" v-bind:class="{checkBtnCompleted: todoItem.completed}" v-on:click="toggleComplete(todoItem, index)"></i>
<span v-bind:class="{textCompleted: todoItem.completed}">{{ todoItem.item }}</span>
<span class="removeBtn" v-on:click="removeTodo(todoItem, index)">
<i class="fa-solid fa-trash-can"></i>
</span>
</li>
</transition-group>
</div>
</template>
트랜지션 그룹에 name이 CSS랑 연관된 속성이고 태그는 말 그대로 html태그를 말함
우리는 ul태그 안에 넣을 거기 때문에 ul로 쓰면 된다.
<style scoped>
/* 리스트 아이템 트랜지션 효과 */
.list-enter-active, .list-leave-active {
transition: all 1s;
}
.list-enter, .list-leave-to {
opacity: 0;
transform: translateY(30px);
}
</style>